diff --git a/AUTHORS b/AUTHORS
index 6ec00b58..0a70fcb 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -38,6 +38,7 @@
 Alex Henrie <alexhenrie24@gmail.com>
 Alex Scheele <alexscheele@gmail.com>
 Alexander Douglas <agdoug@amazon.com>
+Alexander Guettler <alexander@guettler.io>
 Alexander Shalamov <alexander.shalamov@intel.com>
 Alexander Sulfrian <alexander@sulfrian.net>
 Alexandre Abreu <wiss1976@gmail.com>
@@ -1011,6 +1012,7 @@
 Igalia S.L. <*@igalia.com>
 Imagination Technologies Limited <*@imagination.corp-partner.google.com>
 Impossible Dreams Network <*@impossibledreams.net>
+Intel Corporation <*@intel.com>
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
 Macadamian <*@macadamian.com>
diff --git a/DEPS b/DEPS
index 3c48a93..67321e5 100644
--- a/DEPS
+++ b/DEPS
@@ -48,11 +48,6 @@
   # purposes.
   'checkout_configuration': 'default',
 
-  # By default, don't check out android. Will be overridden by gclient
-  # variables.
-  # TODO(ehmaldonado): Remove this once the bug in gclient is fixed.
-  'checkout_android': False,
-
   # Pull in Android native toolchain dependencies for Chrome OS too, so we can
   # build ARC++ support libraries.
   'checkout_android_native_support': 'checkout_android or checkout_chromeos',
@@ -121,11 +116,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '83de8a6f8c0eff9b0fa41a20928aed9b8fcd9026',
+  'skia_revision': '33b4b4908b7acb9913707d1a4a9d8dfe83fb26fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '344d13bb4df5b7ba4c0fcbd6848d339f5d85b1b1',
+  'v8_revision': '626b53addaae93b2d93ce5905d8a754f87cefb0f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -133,7 +128,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': 'd3e0e84ce7e1fcde7bbe71ebfba009ddd5942de3',
+  'angle_revision': '94d8a9a445bef9b7978f513f7f8d449696716296',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -141,11 +136,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '24e71928441e008fc49485cc795f578552df5bcc',
+  'swiftshader_revision': 'fed67899a7b44c9409e94442eaca0deab2f809e3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '92cc7930050ecc97c4d7ca1391a12f9a979b8ee5',
+  'pdfium_revision': '8da0e1b3c27a0bcaac3b3fe08f752506d8042288',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -169,7 +164,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '1ea07c56ac9b57c30eb784ab2af582af0cdd4b08',
+  'nacl_revision': 'ff15c51d20785fa51c0ef03ccb480b87299acbeb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -181,7 +176,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': '78448d90081e62d442d5dbb4b6ac41bd471cb91a',
+  'catapult_revision': 'a9729f33472aa371f0e16e70dbada1d96ee20427',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -229,7 +224,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.
-  'spv_tools_revision': 'cb27ffdcd8a128530371aca8a9743fe146c0a26f',
+  'spv_tools_revision': '8d2d66f30c5c25029ac029af2bc9c4aa6979e5bc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -245,7 +240,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': 'c7f416c0c994886c9cdacd8c983764f6515a473c',
+  'dawn_revision': '799cddcb83fda2911491fd653023c38a28bba1bd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -368,7 +363,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '556fe19d5ac744bf6cddce01b96779b495a9b1fb',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'd1654fd43dbe718ba59310a7aa3f80147f7cf8fd',
       'condition': 'checkout_ios',
   },
 
@@ -679,7 +674,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd8ee2713b0aa932dabda8bae77dd26e6dc6e0906',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7f4fca60eabb6aba7d67ebdb81df63190e36831d',
       'condition': 'checkout_linux',
   },
 
@@ -694,7 +689,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'ea44d5db9e74027d28ef383156c754627a68dfe1',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '88d06ccdd29bf388a6c9906f60060421d008cf82',
       'condition': 'checkout_linux',
   },
 
@@ -704,7 +699,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e229a0e3c5f8646cd6c486ea12c9ef28113e91b6',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'deab113bfb35941f9a173e3a424bc7a67a55affa',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -826,7 +821,7 @@
   },
 
   'src/third_party/arcore-android-sdk/src': {
-      'url': Var('chromium_git') + '/external/github.com/google-ar/arcore-android-sdk.git' + '@' + '25b4589b55c02344cee5fd5722b06202c8b8776d',
+      'url': Var('chromium_git') + '/external/github.com/google-ar/arcore-android-sdk.git' + '@' + '6fcebcbf021b53ceb254e58cce6d5d4a4214819d',
       'condition': 'checkout_android',
   },
 
@@ -883,7 +878,7 @@
   },
 
   'src/third_party/leveldatabase/src':
-    Var('chromium_git') + '/external/leveldb.git' + '@' + 'b70493ca8586285b49e9888e2b528f71806bdc6e',
+    Var('chromium_git') + '/external/leveldb.git' + '@' + 'fe4494804f5e3a2e25485d32aeb0eb7d2f25732e',
 
   'src/third_party/libFuzzer/src':
     Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' +  Var('libfuzzer_revision'),
@@ -930,7 +925,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '759d1de9d0d22bab78bc4946998af79ff8b2044d',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '858fe955ae5a42a0006c974b0837df18b246986f',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4931ebc0a816458c18a6734e91a4d1b5acd5c56',
@@ -1036,7 +1031,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '4a0ee4e0ef368c6dd85d71525f6476417f2b0ae3',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '46f05ad5a1ff5f16a42b9cacf689c23019ff1502',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1199,7 +1194,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f22b9ad8d75bf99652dfc3b1db40e73d9fdd3840',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ccc1b57e32bd99e4f220a3db0e540713f4349ad9',
+    Var('webrtc_git') + '/src.git' + '@' + '309aafe3513af2c53439cb0ea105a39156547acb',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1240,7 +1235,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@439ba1a6f0d5efbdf79cfea87df68acafee729a4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4a6135a21fa1c4c02083b6b7c6fc28712a7b910f',
     'condition': 'checkout_src_internal',
   },
 
@@ -1767,7 +1762,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/libs/com_google_ar_core',
-              'version': 'version:1.5.0-cr0',
+              'version': 'version:1.6.0-cr0',
           },
       ],
       'condition': 'checkout_android',
@@ -2525,19 +2520,6 @@
     'condition': 'checkout_android or checkout_linux',
     'action': ['vpython', 'src/chrome/android/profiles/update_afdo_profile.py'],
   },
-  {
-    # This downloads SDK extras and puts them in the
-    # third_party/android_tools/sdk/extras directory.
-    'name': 'sdkextras',
-    'pattern': '.',
-    'condition': 'checkout_android',
-    # When adding a new sdk extras package to download, add the package
-    # directory and zip file to .gitignore in third_party/android_tools.
-    'action': [ 'python',
-                'src/build/android/play_services/update.py',
-                'download'
-    ],
-  },
   # Download checkstyle for use in PRESUBMIT for Java changes.
   {
     'name': 'checkstyle',
diff --git a/WATCHLISTS b/WATCHLISTS
index 0dbc54f..d892030 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1835,8 +1835,7 @@
     'app_list': ['tfarina@chromium.org'],
     'app_shortcuts': ['dominickn+watch-app_shortcuts@chromium.org',
                       'loyso+watch@chromium.org',
-                      'mgiuca+watch@chromium.org',
-                      'tapted+watch@chromium.org'],
+                      'mgiuca+watch@chromium.org'],
     'apps': ['chromium-apps-reviews@chromium.org',
              'dominickn+watch-apps@chromium.org',
              'tfarina@chromium.org'],
@@ -1922,7 +1921,7 @@
                         'odejesush+watch@chromium.org'],
     'blink_canvas': [ 'dongseong.hwang@intel.com',
                       'fserb+watch@chromium.org'],
-    'blink_client_hints': ['yoav@yoav.ws'],
+    'blink_client_hints': ['yoavweiss@chromium.org'],
     'blink_clipboard': ['dcheng@chromium.org'],
     'blink_common': ['jbroman+watch@chromium.org',
                      'kinuko+watch@chromium.org'],
@@ -1991,7 +1990,7 @@
                     'mlamouri+watch-blink@chromium.org',
                     'srirama.m@samsung.com'],
     'blink_media_queries': ['kenneth.christiansen@gmail.com',
-                            'yoav@yoav.ws'],
+                            'yoavweiss@chromium.org'],
     'blink_mediastream': ['tommyw+watchlist@chromium.org'],
     'blink_modules': ['haraken@chromium.org'],
     'blink_navigator_content_utils': ['gyuyoung.kim@chromium.org'],
@@ -2011,9 +2010,9 @@
                                 'fserb+watch@chromium.org',
                                 'pdr+graphicswatchlist@chromium.org',
                                 'schenney@chromium.org'],
-    'blink_preloadScanner': ['yoav@yoav.ws'],
+    'blink_preloadScanner': ['yoavweiss@chromium.org'],
     'blink_prerender': ['gavinp+prerender@chromium.org',
-                        'yoav@yoav.ws'],
+                        'yoavweiss@chromium.org'],
     'blink_public_api': ['blink-reviews-api@chromium.org'],
     'blink_quota': ['kinuko+fileapi@chromium.org',
                     'nhiroki@chromium.org',
@@ -2175,9 +2174,9 @@
     'crostini': ['crostini-ui@chromium.org'],
     'custom_tabs': ['amalova+watch@chromium.org',
                     'lizeb+watch-custom-tabs@chromium.org',
-                    'peconn@chromium.org',
+                    'peconn+watch@chromium.org',
                     'peter@chromium.org',
-                    'pshmakov@chromium.org'],
+                    'pshmakov+watch@chromium.org'],
     'dbus': ['hashimoto+watch@chromium.org'],
     'deep_memory_profiler': ['dmikurube@chromium.org'],
     'device_bluetooth': ['mattreynolds+watch@chromium.org',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 32b5cb4b..8bbf041 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -561,6 +561,8 @@
     "browser/aw_ssl_host_state_delegate.h",
     "browser/aw_url_checker_delegate_impl.cc",
     "browser/aw_url_checker_delegate_impl.h",
+    "browser/aw_url_loader_throttle.cc",
+    "browser/aw_url_loader_throttle.h",
     "browser/aw_variations_seed_bridge.cc",
     "browser/aw_variations_seed_bridge.h",
     "browser/aw_variations_service_client.cc",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index d3ae34f..aa532fc 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -23,6 +23,7 @@
 #include "android_webview/browser/aw_settings.h"
 #include "android_webview/browser/aw_speech_recognition_manager_delegate.h"
 #include "android_webview/browser/aw_url_checker_delegate_impl.h"
+#include "android_webview/browser/aw_url_loader_throttle.h"
 #include "android_webview/browser/aw_web_contents_view_delegate.h"
 #include "android_webview/browser/net/aw_url_request_context_getter.h"
 #include "android_webview/browser/net_helpers.h"
@@ -759,6 +760,18 @@
     }
   }
 
+  if (request.resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
+    const bool is_load_url =
+        request.transition_type & ui::PAGE_TRANSITION_FROM_API;
+    const bool is_go_back_forward =
+        request.transition_type & ui::PAGE_TRANSITION_FORWARD_BACK;
+    const bool is_reload = ui::PageTransitionCoreTypeIs(
+        static_cast<ui::PageTransition>(request.transition_type),
+        ui::PAGE_TRANSITION_RELOAD);
+    if (is_load_url || is_go_back_forward || is_reload)
+      result.push_back(std::make_unique<AwURLLoaderThrottle>(resource_context));
+  }
+
   return result;
 }
 
diff --git a/android_webview/browser/aw_draw_fn_impl.cc b/android_webview/browser/aw_draw_fn_impl.cc
index 73f30a0..97683f4 100644
--- a/android_webview/browser/aw_draw_fn_impl.cc
+++ b/android_webview/browser/aw_draw_fn_impl.cc
@@ -29,9 +29,6 @@
 using base::android::JavaParamRef;
 using content::BrowserThread;
 
-// TODO(ericrk): Audit CHECKs in this file and replace most with graceful error
-// handling.
-
 namespace android_webview {
 
 namespace {
@@ -87,6 +84,137 @@
 };
 
 namespace {
+VulkanState* g_vulkan_state = nullptr;
+}
+
+class VulkanState : public base::RefCounted<VulkanState> {
+ public:
+  static scoped_refptr<VulkanState> GetOrCreateInstance(
+      AwDrawFn_InitVkParams* params) {
+    if (g_vulkan_state) {
+      DCHECK_EQ(params->device, g_vulkan_state->device());
+      DCHECK_EQ(params->queue, g_vulkan_state->queue());
+      return base::WrapRefCounted(g_vulkan_state);
+    }
+
+    auto new_state = base::WrapRefCounted(new VulkanState);
+    if (!new_state->Initialize(params))
+      return nullptr;
+
+    return new_state;
+  }
+
+  VkPhysicalDevice physical_device() { return physical_device_; }
+  VkDevice device() { return device_; }
+  VkQueue queue() { return queue_; }
+  gpu::VulkanImplementation* implementation() { return implementation_.get(); }
+  GrContext* gr_context() { return gr_context_.get(); }
+
+ private:
+  friend class base::RefCounted<VulkanState>;
+
+  VulkanState() {
+    DCHECK_EQ(nullptr, g_vulkan_state);
+    g_vulkan_state = this;
+  }
+
+  ~VulkanState() {
+    DCHECK_EQ(g_vulkan_state, this);
+    g_vulkan_state = nullptr;
+  }
+
+  bool Initialize(AwDrawFn_InitVkParams* params) {
+    physical_device_ = params->physical_device;
+    device_ = params->device;
+    queue_ = params->queue;
+
+    // Don't call init on implementation. Instead call InitVulkanForWebView,
+    // which avoids creating a new instance.
+    implementation_ = gpu::CreateVulkanImplementation();
+    if (!InitVulkanForWebView(params->instance, params->device)) {
+      LOG(ERROR) << "Unable to initialize Vulkan pointers.";
+      return false;
+    }
+
+    // Create our Skia GrContext.
+    GrVkGetProc get_proc =
+        MakeUnifiedGetter(vkGetInstanceProcAddr, vkGetDeviceProcAddr);
+    GrVkExtensions extensions;
+    extensions.init(get_proc, params->instance, params->physical_device,
+                    params->enabled_instance_extension_names_length,
+                    params->enabled_instance_extension_names,
+                    params->enabled_device_extension_names_length,
+                    params->enabled_device_extension_names);
+    GrVkBackendContext backend_context{
+        .fInstance = params->instance,
+        .fPhysicalDevice = params->physical_device,
+        .fDevice = params->device,
+        .fQueue = params->queue,
+        .fGraphicsQueueIndex = params->graphics_queue_index,
+        .fInstanceVersion = params->instance_version,
+        .fVkExtensions = &extensions,
+        .fDeviceFeatures = params->device_features,
+        .fDeviceFeatures2 = params->device_features_2,
+        .fMemoryAllocator = nullptr,
+        .fGetProc = get_proc,
+        .fOwnsInstanceAndDevice = false,
+    };
+    gr_context_ = GrContext::MakeVulkan(backend_context);
+    if (!gr_context_) {
+      LOG(ERROR) << "Unable to initialize GrContext.";
+      return false;
+    }
+    return true;
+  }
+
+  static bool InitVulkanForWebView(VkInstance instance, VkDevice device) {
+    gpu::VulkanFunctionPointers* vulkan_function_pointers =
+        gpu::GetVulkanFunctionPointers();
+
+    // If we are re-initing, we don't need to re-load the shared library or
+    // re-bind unassociated pointers. These shouldn't change.
+    if (!vulkan_function_pointers->vulkan_loader_library_) {
+      base::NativeLibraryLoadError native_library_load_error;
+      vulkan_function_pointers->vulkan_loader_library_ =
+          base::LoadNativeLibrary(base::FilePath("libvulkan.so"),
+                                  &native_library_load_error);
+      if (!vulkan_function_pointers->vulkan_loader_library_)
+        return false;
+      if (!vulkan_function_pointers->BindUnassociatedFunctionPointers())
+        return false;
+    }
+
+    // These vars depend on |instance| and |device| and should be
+    // re-initialized.
+    if (!vulkan_function_pointers->BindInstanceFunctionPointers(instance))
+      return false;
+    if (!vulkan_function_pointers->BindPhysicalDeviceFunctionPointers(instance))
+      return false;
+    if (!vulkan_function_pointers->BindDeviceFunctionPointers(device))
+      return false;
+
+    return true;
+  }
+
+  static GrVkGetProc MakeUnifiedGetter(const PFN_vkGetInstanceProcAddr& iproc,
+                                       const PFN_vkGetDeviceProcAddr& dproc) {
+    return [&iproc, &dproc](const char* proc_name, VkInstance instance,
+                            VkDevice device) {
+      if (device != VK_NULL_HANDLE) {
+        return dproc(device, proc_name);
+      }
+      return iproc(instance, proc_name);
+    };
+  }
+
+  VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
+  VkDevice device_ = VK_NULL_HANDLE;
+  VkQueue queue_ = VK_NULL_HANDLE;
+  std::unique_ptr<gpu::VulkanImplementation> implementation_;
+  sk_sp<GrContext> gr_context_;
+};
+
+namespace {
 
 AwDrawFnFunctionTable* g_draw_fn_function_table = nullptr;
 
@@ -137,36 +265,6 @@
   static_cast<AwDrawFnImpl*>(data)->PostDrawVk(params);
 }
 
-GrVkGetProc MakeUnifiedGetter(const PFN_vkGetInstanceProcAddr& iproc,
-                              const PFN_vkGetDeviceProcAddr& dproc) {
-  return [&iproc, &dproc](const char* proc_name, VkInstance instance,
-                          VkDevice device) {
-    if (device != VK_NULL_HANDLE) {
-      return dproc(device, proc_name);
-    }
-    return iproc(instance, proc_name);
-  };
-}
-
-bool InitVulkanForWebView(VkInstance instance, VkDevice device) {
-  gpu::VulkanFunctionPointers* vulkan_function_pointers =
-      gpu::GetVulkanFunctionPointers();
-  base::NativeLibraryLoadError native_library_load_error;
-  vulkan_function_pointers->vulkan_loader_library_ = base::LoadNativeLibrary(
-      base::FilePath("libvulkan.so"), &native_library_load_error);
-  if (!vulkan_function_pointers->vulkan_loader_library_)
-    return false;
-  if (!vulkan_function_pointers->BindUnassociatedFunctionPointers())
-    return false;
-  if (!vulkan_function_pointers->BindInstanceFunctionPointers(instance))
-    return false;
-  if (!vulkan_function_pointers->BindPhysicalDeviceFunctionPointers(instance))
-    return false;
-  if (!vulkan_function_pointers->BindDeviceFunctionPointers(device))
-    return false;
-  return true;
-}
-
 }  // namespace
 
 static void JNI_AwDrawFnImpl_SetDrawFnFunctionTable(JNIEnv* env,
@@ -241,14 +339,7 @@
     TakeInFlightDrawForReUse();
   }
 
-  // Delete |gr_context_| first, as it may use Vk.
-  gr_context_.reset();
-
-  queue_ = VK_NULL_HANDLE;
-  device_ = VK_NULL_HANDLE;
-  physical_device_ = VK_NULL_HANDLE;
-  vk_implementation_.reset();
-
+  vk_state_.reset();
   gl_context_.reset();
 }
 
@@ -272,56 +363,20 @@
 }
 
 void AwDrawFnImpl::InitVk(AwDrawFn_InitVkParams* params) {
-  // Cache Vulkan objects which we'll need to re-use, as Skia does not give a
-  // way to later retrieve them.
-  DCHECK(device_ == VK_NULL_HANDLE);
-  DCHECK(queue_ == VK_NULL_HANDLE);
-  DCHECK(physical_device_ == VK_NULL_HANDLE);
-  device_ = params->device;
-  queue_ = params->queue;
-  physical_device_ = params->physical_device;
-
-  // We should never have a |vk_implementation_| if we are calling VkInit. This
+  // We should never have a |vk_state_| if we are calling VkInit. This
   // means context destroyed was not correctly called.
-  DCHECK(!vk_implementation_);
-  vk_implementation_ = gpu::CreateVulkanImplementation();
-  // Don't call init on implementation. Instead call InitVulkanForWebView,
-  // which avoids creating a new instance.
-  // TODO(ericrk): Call this only once per process, unless we lose our context
-  // and get a new VkDevice or VkInstance.
-  CHECK(InitVulkanForWebView(params->instance, params->device));
-
-  // Create our Skia GrContext.
-  GrVkGetProc get_proc =
-      MakeUnifiedGetter(vkGetInstanceProcAddr, vkGetDeviceProcAddr);
-  GrVkExtensions extensions;
-  extensions.init(get_proc, params->instance, params->physical_device,
-                  params->enabled_instance_extension_names_length,
-                  params->enabled_instance_extension_names,
-                  params->enabled_device_extension_names_length,
-                  params->enabled_device_extension_names);
-  GrVkBackendContext backend_context{
-      .fInstance = params->instance,
-      .fPhysicalDevice = params->physical_device,
-      .fDevice = params->device,
-      .fQueue = params->queue,
-      .fGraphicsQueueIndex = params->graphics_queue_index,
-      .fInstanceVersion = params->instance_version,
-      .fVkExtensions = &extensions,
-      .fDeviceFeatures = params->device_features,
-      .fDeviceFeatures2 = params->device_features_2,
-      .fMemoryAllocator = nullptr,
-      .fGetProc = get_proc,
-      .fOwnsInstanceAndDevice = false,
-  };
-  gr_context_ = GrContext::MakeVulkan(backend_context);
-  CHECK(gr_context_);
+  DCHECK(!vk_state_);
+  vk_state_ = VulkanState::GetOrCreateInstance(params);
 
   // Make sure we have a GL context.
+  DCHECK(!gl_context_);
   gl_context_ = GLNonOwnedCompatibilityContext::GetOrCreateInstance();
 }
 
 void AwDrawFnImpl::DrawVk(AwDrawFn_DrawVkParams* params) {
+  if (!vk_state_ || !gl_context_)
+    return;
+
   if (!gl_context_->MakeCurrent()) {
     LOG(ERROR) << "Failed to make GL context current for drawing.";
     return;
@@ -344,7 +399,7 @@
 
   // If we weren't able to re-use a previous draw, create one.
   if (!pending_draw_) {
-    pending_draw_ = std::make_unique<InFlightDraw>();
+    pending_draw_ = std::make_unique<InFlightDraw>(vk_state_.get());
 
     AHardwareBuffer_Desc desc = {};
     desc.width = params->width;
@@ -358,24 +413,38 @@
                  AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
     AHardwareBuffer* buffer = nullptr;
     base::AndroidHardwareBufferCompat::GetInstance().Allocate(&desc, &buffer);
-    CHECK(buffer);
-    auto cleanup = base::android::ScopedHardwareBufferHandle::Adopt(buffer);
+    if (!buffer) {
+      LOG(ERROR) << "Failed to allocate AHardwareBuffer for WebView rendering.";
+      return;
+    }
+    auto scoped_buffer =
+        base::android::ScopedHardwareBufferHandle::Adopt(buffer);
 
     pending_draw_->ahb_image = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(
         gfx::Size(params->width, params->height));
-    CHECK(pending_draw_->ahb_image->Initialize(buffer, false /* preserved */));
+    if (!pending_draw_->ahb_image->Initialize(scoped_buffer.get(),
+                                              false /* preserved */)) {
+      LOG(ERROR) << "Failed to initialize GLImage for AHardwareBuffer.";
+      return;
+    }
 
     glGenTextures(1, static_cast<GLuint*>(&pending_draw_->texture_id));
     GLenum target = GL_TEXTURE_2D;
     glBindTexture(target, pending_draw_->texture_id);
-    CHECK(pending_draw_->ahb_image->BindTexImage(target));
+    if (!pending_draw_->ahb_image->BindTexImage(target)) {
+      LOG(ERROR) << "Failed to bind GLImage for AHardwareBuffer.";
+      return;
+    }
     glBindTexture(target, 0);
     glGenFramebuffersEXT(1, &pending_draw_->framebuffer_id);
     glBindFramebufferEXT(GL_FRAMEBUFFER, pending_draw_->framebuffer_id);
     glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                               GL_TEXTURE_2D, pending_draw_->texture_id, 0);
-    CHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) ==
-          GL_FRAMEBUFFER_COMPLETE);
+    if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) !=
+        GL_FRAMEBUFFER_COMPLETE) {
+      LOG(ERROR) << "Failed to set up framebuffer for WebView GL drawing.";
+      return;
+    }
   }
 
   // Ask GL to wait on any Vk sync_fd before writing.
@@ -385,8 +454,6 @@
   base::ScopedFD gl_done_fd;
   {
     glBindFramebufferEXT(GL_FRAMEBUFFER, pending_draw_->framebuffer_id);
-    CHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) ==
-          GL_FRAMEBUFFER_COMPLETE);
     glViewport(0, 0, params->width, params->height);
     glDisable(GL_STENCIL_TEST);
     glDisable(GL_SCISSOR_TEST);
@@ -436,20 +503,24 @@
   };
   SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
   pending_draw_->draw_context = GrVkSecondaryCBDrawContext::Make(
-      gr_context_.get(), info, drawable_info, &props);
+      vk_state_->gr_context(), info, drawable_info, &props);
 
   // If we have a |gl_done_fd|, create a Skia GrBackendSemaphore from
   // |gl_done_fd| and wait.
   if (gl_done_fd.is_valid()) {
     VkSemaphore gl_done_semaphore;
-    if (!vk_implementation_->ImportSemaphoreFdKHR(
-            device_, std::move(gl_done_fd), &gl_done_semaphore)) {
-      CHECK(false);
+    if (!vk_state_->implementation()->ImportSemaphoreFdKHR(
+            vk_state_->device(), std::move(gl_done_fd), &gl_done_semaphore)) {
+      LOG(ERROR) << "Could not create Vulkan semaphore for GL completion.";
+      return;
     }
     GrBackendSemaphore gr_semaphore;
     gr_semaphore.initVulkan(gl_done_semaphore);
     if (!pending_draw_->draw_context->wait(1, &gr_semaphore)) {
-      CHECK(false);
+      // If wait returns false, we must clean up the |gl_done_semaphore|.
+      vkDestroySemaphore(vk_state_->device(), gl_done_semaphore, nullptr);
+      LOG(ERROR) << "Could not wait on GL completion semaphore.";
+      return;
     }
   }
 
@@ -459,13 +530,15 @@
     VkImageCreateInfo vk_image_info;
     VkDeviceMemory vk_device_memory;
     VkDeviceSize mem_allocation_size;
-    if (!vk_implementation_->CreateVkImageAndImportAHB(
-            device_, physical_device_, gfx::Size(params->width, params->height),
+    if (!vk_state_->implementation()->CreateVkImageAndImportAHB(
+            vk_state_->device(), vk_state_->physical_device(),
+            gfx::Size(params->width, params->height),
             base::android::ScopedHardwareBufferHandle::Create(
                 pending_draw_->ahb_image->GetAHardwareBuffer()->buffer()),
             &vk_image, &vk_image_info, &vk_device_memory,
             &mem_allocation_size)) {
-      CHECK(false);
+      LOG(ERROR) << "Could not create VkImage from AHB.";
+      return;
     }
 
     // Create backend texture from the VkImage.
@@ -476,16 +549,18 @@
                                  vk_image_info.initialLayout,
                                  vk_image_info.format,
                                  vk_image_info.mipLevels};
-    pending_draw_->device = device_;
   }
 
   // Create an SkImage from AHB.
   GrBackendTexture backend_texture(params->width, params->height,
                                    pending_draw_->image_info);
   pending_draw_->ahb_skimage = SkImage::MakeFromTexture(
-      gr_context_.get(), backend_texture, kBottomLeft_GrSurfaceOrigin,
+      vk_state_->gr_context(), backend_texture, kBottomLeft_GrSurfaceOrigin,
       kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
-  CHECK(pending_draw_->ahb_skimage);
+  if (!pending_draw_->ahb_skimage) {
+    LOG(ERROR) << "Could not create SkImage from VkImage.";
+    return;
+  }
 
   // Draw the SkImage.
   SkPaint paint;
@@ -496,6 +571,9 @@
 }
 
 void AwDrawFnImpl::PostDrawVk(AwDrawFn_PostDrawVkParams* params) {
+  if (!vk_state_ || !gl_context_)
+    return;
+
   // Release the SkImage so that Skia transitions it back to EXTERNAL.
   pending_draw_->ahb_skimage.reset();
 
@@ -508,19 +586,30 @@
   sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
   sem_info.pNext = &export_info;
   sem_info.flags = 0;
-  bool result = vkCreateSemaphore(device_, &sem_info, nullptr,
-                                  &pending_draw_->post_draw_semaphore);
-  CHECK(result == VK_SUCCESS);
+  VkResult result = vkCreateSemaphore(vk_state_->device(), &sem_info, nullptr,
+                                      &pending_draw_->post_draw_semaphore);
+  if (result != VK_SUCCESS) {
+    LOG(ERROR) << "Could not create VkSemaphore.";
+    return;
+  }
   GrBackendSemaphore gr_post_draw_semaphore;
   gr_post_draw_semaphore.initVulkan(pending_draw_->post_draw_semaphore);
 
   // Flush so that we know the image's transition has been submitted and that
   // the |post_draw_semaphore| is pending.
   GrSemaphoresSubmitted submitted =
-      gr_context_->flushAndSignalSemaphores(1, &gr_post_draw_semaphore);
-  CHECK(submitted == GrSemaphoresSubmitted::kYes);
-  vk_implementation_->GetSemaphoreFdKHR(
-      device_, pending_draw_->post_draw_semaphore, &pending_draw_->sync_fd);
+      vk_state_->gr_context()->flushAndSignalSemaphores(
+          1, &gr_post_draw_semaphore);
+  if (submitted != GrSemaphoresSubmitted::kYes) {
+    LOG(ERROR) << "Skia could not submit GrSemaphore.";
+    return;
+  }
+  if (!vk_state_->implementation()->GetSemaphoreFdKHR(
+          vk_state_->device(), pending_draw_->post_draw_semaphore,
+          &pending_draw_->sync_fd)) {
+    LOG(ERROR) << "Could not retrieve SyncFD from |post_draw_semaphore|.";
+    return;
+  }
 
   // Get a fence to wait on for CPU-side cleanup.
   VkFenceCreateInfo create_info{
@@ -529,12 +618,18 @@
       .flags = 0,
   };
   DCHECK(VK_NULL_HANDLE == pending_draw_->post_draw_fence);
-  VkResult vk_result = vkCreateFence(device_, &create_info, nullptr,
-                                     &pending_draw_->post_draw_fence);
-  DCHECK_EQ(vk_result, VK_SUCCESS);
-  CHECK(pending_draw_->post_draw_fence != VK_NULL_HANDLE);
-  result = vkQueueSubmit(queue_, 0, nullptr, pending_draw_->post_draw_fence);
-  DCHECK_EQ(result, VK_SUCCESS);
+  result = vkCreateFence(vk_state_->device(), &create_info, nullptr,
+                         &pending_draw_->post_draw_fence);
+  if (result != VK_SUCCESS) {
+    LOG(ERROR) << "Could not create VkFence.";
+    return;
+  }
+  result = vkQueueSubmit(vk_state_->queue(), 0, nullptr,
+                         pending_draw_->post_draw_fence);
+  if (result != VK_SUCCESS) {
+    LOG(ERROR) << "Could not submit fence to queue.";
+    return;
+  }
 
   // Add the |pending_draw_| to |in_flight_draws_|.
   in_flight_draws_.push_back(std::move(pending_draw_));
@@ -542,27 +637,25 @@
 
 std::unique_ptr<AwDrawFnImpl::InFlightDraw>
 AwDrawFnImpl::TakeInFlightDrawForReUse() {
+  DCHECK(vk_state_);
   DCHECK(!in_flight_draws_.empty());
   std::unique_ptr<InFlightDraw>& draw = in_flight_draws_.front();
 
   // Wait for our draw's |post_draw_fence| to pass.
-  CHECK(draw->post_draw_fence != VK_NULL_HANDLE);
+  DCHECK(draw->post_draw_fence != VK_NULL_HANDLE);
   VkResult wait_result =
-      vkWaitForFences(device_, 1, &draw->post_draw_fence, VK_TRUE,
+      vkWaitForFences(vk_state_->device(), 1, &draw->post_draw_fence, VK_TRUE,
                       base::TimeDelta::FromSeconds(60).InNanoseconds());
   if (wait_result != VK_SUCCESS) {
     LOG(ERROR) << "Fence did not pass in the expected timeframe.";
     return nullptr;
   }
 
-  // Unclear if we can re-use the GrVkSecondaryCBDrawContext.
-  // TODO(ericrk): Follow-up.
   draw->draw_context->releaseResources();
   draw->draw_context.reset();
-  // Clean up the |post_draw_fence|, which we'll re-create.
-  vkDestroyFence(device_, draw->post_draw_fence, nullptr);
+  vkDestroyFence(vk_state_->device(), draw->post_draw_fence, nullptr);
   draw->post_draw_fence = VK_NULL_HANDLE;
-  vkDestroySemaphore(device_, draw->post_draw_semaphore, nullptr);
+  vkDestroySemaphore(vk_state_->device(), draw->post_draw_semaphore, nullptr);
   draw->post_draw_semaphore = VK_NULL_HANDLE;
 
   std::unique_ptr<InFlightDraw> draw_to_return = std::move(draw);
@@ -570,21 +663,51 @@
   return draw_to_return;
 }
 
-AwDrawFnImpl::InFlightDraw::InFlightDraw() = default;
+AwDrawFnImpl::InFlightDraw::InFlightDraw(VulkanState* vk_state)
+    : vk_state(vk_state) {}
 
 AwDrawFnImpl::InFlightDraw::~InFlightDraw() {
-  // These are always destroyed in TakeInFlightDrawForReUse.
-  CHECK(post_draw_fence == VK_NULL_HANDLE);
-  CHECK(post_draw_semaphore == VK_NULL_HANDLE);
+  // If |draw_context| is valid, we encountered an error during Vk drawing and
+  // should call vkQueueWaitIdle to ensure safe shutdown.
+  bool encountered_error = !!draw_context;
+  if (encountered_error) {
+    // Clean up one-off objects which may have been left alive due to an error.
 
-  // Clean up re-usable components.
+    // Clean up |ahb_skimage| first, as doing so generates Vk commands we need
+    // to flush before the vkQueueWaitIdle below.
+    if (ahb_skimage) {
+      ahb_skimage.reset();
+      vk_state->gr_context()->flush();
+    }
+    // We encountered an error and are not sure when our Vk objects are safe to
+    // delete. VkQueueWaitIdle to ensure safety.
+    vkQueueWaitIdle(vk_state->queue());
+    if (draw_context) {
+      draw_context->releaseResources();
+      draw_context.reset();
+    }
+    if (post_draw_fence != VK_NULL_HANDLE) {
+      vkDestroyFence(vk_state->device(), post_draw_fence, nullptr);
+      post_draw_fence = VK_NULL_HANDLE;
+    }
+    if (post_draw_semaphore != VK_NULL_HANDLE) {
+      vkDestroySemaphore(vk_state->device(), post_draw_semaphore, nullptr);
+      post_draw_semaphore = VK_NULL_HANDLE;
+    }
+  }
+  DCHECK(!draw_context);
+  DCHECK(!ahb_skimage);
+  DCHECK(post_draw_fence == VK_NULL_HANDLE);
+  DCHECK(post_draw_semaphore == VK_NULL_HANDLE);
+
+  // Clean up re-usable components that are expected to still be alive.
   if (texture_id)
     glDeleteTextures(1, &texture_id);
   if (framebuffer_id)
     glDeleteFramebuffersEXT(1, &framebuffer_id);
   if (image_info.fImage != VK_NULL_HANDLE) {
-    vkDestroyImage(device, image_info.fImage, nullptr);
-    vkFreeMemory(device, image_info.fAlloc.fMemory, nullptr);
+    vkDestroyImage(vk_state->device(), image_info.fImage, nullptr);
+    vkFreeMemory(vk_state->device(), image_info.fAlloc.fMemory, nullptr);
   }
 }
 
diff --git a/android_webview/browser/aw_draw_fn_impl.h b/android_webview/browser/aw_draw_fn_impl.h
index a3993a1..924d6af 100644
--- a/android_webview/browser/aw_draw_fn_impl.h
+++ b/android_webview/browser/aw_draw_fn_impl.h
@@ -16,7 +16,6 @@
 #include "third_party/skia/include/gpu/vk/GrVkTypes.h"
 #include "third_party/vulkan/include/vulkan/vulkan.h"
 
-class GrContext;
 class GrVkSecondaryCBDrawContext;
 
 namespace gl {
@@ -25,6 +24,7 @@
 
 namespace android_webview {
 class GLNonOwnedCompatibilityContext;
+class VulkanState;
 
 class AwDrawFnImpl {
  public:
@@ -50,7 +50,7 @@
  private:
   // Struct which represents one in-flight draw for the Vk interop path.
   struct InFlightDraw {
-    InFlightDraw();
+    explicit InFlightDraw(VulkanState* vk_state);
     ~InFlightDraw();
     sk_sp<GrVkSecondaryCBDrawContext> draw_context;
     VkFence post_draw_fence = VK_NULL_HANDLE;
@@ -61,7 +61,9 @@
     uint32_t texture_id = 0;
     uint32_t framebuffer_id = 0;
     GrVkImageInfo image_info;
-    VkDevice device;  // Used to clean up |image_info|
+
+    // Used to clean up Vulkan objects.
+    VulkanState* vk_state;
   };
 
   CompositorFrameConsumer* GetCompositorFrameConsumer() {
@@ -73,14 +75,10 @@
   int functor_handle_;
   RenderThreadManager render_thread_manager_;
 
-  // Members for the GL-interop path.
-  VkQueue queue_ = VK_NULL_HANDLE;
-  VkDevice device_ = VK_NULL_HANDLE;
-  VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
-  std::unique_ptr<gpu::VulkanImplementation> vk_implementation_;
-  sk_sp<GrContext> gr_context_;
+  // State used for Vk rendering.
+  scoped_refptr<VulkanState> vk_state_;
 
-  // GL context used to draw via GL in interop path.
+  // GL context used to draw via GL in Vk interop path.
   scoped_refptr<GLNonOwnedCompatibilityContext> gl_context_;
 
   // Queue of draw contexts pending cleanup.
diff --git a/android_webview/browser/aw_proxying_url_loader_factory.cc b/android_webview/browser/aw_proxying_url_loader_factory.cc
index 4639d4a..10476b7 100644
--- a/android_webview/browser/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/aw_proxying_url_loader_factory.cc
@@ -8,9 +8,12 @@
 
 #include "android_webview/browser/aw_contents_client_bridge.h"
 #include "android_webview/browser/aw_contents_io_thread_client.h"
+#include "android_webview/browser/input_stream.h"
 #include "android_webview/browser/net/aw_web_resource_response.h"
 #include "android_webview/browser/net_helpers.h"
+#include "android_webview/browser/net_network_service/android_stream_reader_url_loader.h"
 #include "android_webview/browser/renderer_host/auto_login_parser.h"
+#include "base/android/build_info.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -28,6 +31,21 @@
 
 const char kAutoLoginHeaderName[] = "X-Auto-Login";
 
+class AndroidResponseDelegate
+    : public AndroidStreamReaderURLLoader::ResponseDelegate {
+ public:
+  AndroidResponseDelegate(std::unique_ptr<AwWebResourceResponse> response)
+      : response_(std::move(response)) {}
+
+  std::unique_ptr<android_webview::InputStream> OpenInputStream(
+      JNIEnv* env) override {
+    return response_->GetInputStream(env);
+  }
+
+ private:
+  std::unique_ptr<AwWebResourceResponse> response_;
+};
+
 // Handles intercepted, in-progress requests/responses, so that they can be
 // controlled and modified accordingly.
 class InterceptedRequest : public network::mojom::URLLoader,
@@ -71,6 +89,9 @@
   void ResumeReadingBodyFromNet() override;
 
   void ContinueAfterIntercept();
+  void ContinueAfterInterceptWithOverride(
+      std::unique_ptr<AwWebResourceResponse> response);
+
   void InterceptResponseReceived(
       std::unique_ptr<AwWebResourceResponse> response);
 
@@ -146,13 +167,21 @@
       AwWebResourceRequest(request_),
       base::BindOnce(&InterceptedRequest::InterceptResponseReceived,
                      weak_factory_.GetWeakPtr()));
+
+  // We send the application's package name in the X-Requested-With header for
+  // compatibility with previous WebView versions. This should not be visible to
+  // shouldInterceptRequest.
+  request_.headers.SetHeaderIfMissing(
+      "X-Requested-With",
+      base::android::BuildInfo::GetInstance()->host_package_name());
 }
 
 void InterceptedRequest::InterceptResponseReceived(
     std::unique_ptr<AwWebResourceResponse> response) {
   if (response) {
-    // TODO(timvolodine): handle the case where response contains data,
-    // i.e. is actually overridden, crbug.com/893566.
+    // non-null response: make sure to use it as an override for the
+    // normal network data.
+    ContinueAfterInterceptWithOverride(std::move(response));
   } else {
     ContinueAfterIntercept();
   }
@@ -168,6 +197,16 @@
   }
 }
 
+void InterceptedRequest::ContinueAfterInterceptWithOverride(
+    std::unique_ptr<AwWebResourceResponse> response) {
+  network::mojom::URLLoaderClientPtr proxied_client;
+  proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
+  AndroidStreamReaderURLLoader* loader = new AndroidStreamReaderURLLoader(
+      request_, std::move(proxied_client), traffic_annotation_,
+      std::make_unique<AndroidResponseDelegate>(std::move(response)));
+  loader->Start();
+}
+
 namespace {
 // TODO(timvolodine): consider factoring this out of this file.
 
@@ -230,7 +269,7 @@
   // intercept response headers here
   // pause/resume proxied_client_binding_ if necessary
 
-  if (head.headers->response_code() >= 400) {
+  if (head.headers && head.headers->response_code() >= 400) {
     // In Android WebView the WebViewClient.onReceivedHttpError callback
     // is invoked for any resource (main page, iframe, image, etc.) with
     // status code >= 400.
@@ -248,8 +287,8 @@
     // Check for x-auto-login-header
     HeaderData header_data;
     std::string header_string;
-    if (head.headers->GetNormalizedHeader(kAutoLoginHeaderName,
-                                          &header_string)) {
+    if (head.headers && head.headers->GetNormalizedHeader(kAutoLoginHeaderName,
+                                                          &header_string)) {
       if (ParseHeader(header_string, ALLOW_ANY_REALM, &header_data)) {
         // TODO(timvolodine): consider simplifying this and above callback
         // code, crbug.com/897149.
diff --git a/android_webview/browser/aw_url_loader_throttle.cc b/android_webview/browser/aw_url_loader_throttle.cc
new file mode 100644
index 0000000..96d619ed
--- /dev/null
+++ b/android_webview/browser/aw_url_loader_throttle.cc
@@ -0,0 +1,50 @@
+// 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 "android_webview/browser/aw_url_loader_throttle.h"
+
+#include "android_webview/browser/aw_resource_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/http/http_response_headers.h"
+
+namespace android_webview {
+
+AwURLLoaderThrottle::AwURLLoaderThrottle(
+    content::ResourceContext* resource_context)
+    : aw_resource_context_(static_cast<AwResourceContext*>(resource_context)) {}
+
+AwURLLoaderThrottle::~AwURLLoaderThrottle() = default;
+
+void AwURLLoaderThrottle::WillStartRequest(network::ResourceRequest* request,
+                                           bool* defer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  AddExtraHeadersIfNeeded(request->url, &request->headers);
+}
+
+void AwURLLoaderThrottle::WillRedirectRequest(
+    net::RedirectInfo* redirect_info,
+    const network::ResourceResponseHead& response_head,
+    bool* defer,
+    std::vector<std::string>* to_be_removed_request_headers,
+    net::HttpRequestHeaders* modified_request_headers) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  AddExtraHeadersIfNeeded(redirect_info->new_url, modified_request_headers);
+}
+
+void AwURLLoaderThrottle::AddExtraHeadersIfNeeded(
+    const GURL& url,
+    net::HttpRequestHeaders* headers) {
+  std::string extra_headers = aw_resource_context_->GetExtraHeaders(url);
+  if (extra_headers.empty())
+    return;
+
+  net::HttpRequestHeaders temp_headers;
+  temp_headers.AddHeadersFromString(extra_headers);
+  for (net::HttpRequestHeaders::Iterator it(temp_headers); it.GetNext();)
+    headers->SetHeaderIfMissing(it.name(), it.value());
+}
+
+}  // namespace android_webview
diff --git a/android_webview/browser/aw_url_loader_throttle.h b/android_webview/browser/aw_url_loader_throttle.h
new file mode 100644
index 0000000..a003b85
--- /dev/null
+++ b/android_webview/browser/aw_url_loader_throttle.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_AW_URL_LOADER_THROTTLE_H_
+#define ANDROID_WEBVIEW_BROWSER_AW_URL_LOADER_THROTTLE_H_
+
+#include "base/macros.h"
+#include "content/public/common/url_loader_throttle.h"
+
+class GURL;
+
+namespace content {
+class ResourceContext;
+}
+
+namespace net {
+class HttpRequestHeaders;
+}
+
+namespace android_webview {
+class AwResourceContext;
+
+class AwURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+  explicit AwURLLoaderThrottle(content::ResourceContext* resource_context);
+  ~AwURLLoaderThrottle() override;
+
+  // content::URLLoaderThrottle implementation:
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override;
+  void WillRedirectRequest(
+      net::RedirectInfo* redirect_info,
+      const network::ResourceResponseHead& response_head,
+      bool* defer,
+      std::vector<std::string>* to_be_removed_request_headers,
+      net::HttpRequestHeaders* modified_request_headers) override;
+
+ private:
+  void AddExtraHeadersIfNeeded(const GURL& url,
+                               net::HttpRequestHeaders* headers);
+
+  AwResourceContext* aw_resource_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(AwURLLoaderThrottle);
+};
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_BROWSER_AW_URL_LOADER_THROTTLE_H_
diff --git a/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc b/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
index 5e36ed0ce..856203c5 100644
--- a/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
+++ b/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
@@ -63,20 +63,6 @@
   DISALLOW_COPY_AND_ASSIGN(InputStreamReaderWrapper);
 };
 
-class AndroidResponseDelegate
-    : public AndroidStreamReaderURLLoader::ResponseDelegate {
- public:
-  AndroidResponseDelegate(std::unique_ptr<AwWebResourceResponse> response)
-      : response_(std::move(response)) {}
-  std::unique_ptr<android_webview::InputStream> OpenInputStream(
-      JNIEnv* env) override {
-    return response_->GetInputStream(env);
-  }
-
- private:
-  std::unique_ptr<AwWebResourceResponse> response_;
-};
-
 AndroidStreamReaderURLLoader::AndroidStreamReaderURLLoader(
     const network::ResourceRequest& resource_request,
     network::mojom::URLLoaderClientPtr client,
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index 6ad95c7..ddc67c3 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -276,8 +276,6 @@
     content::AppCacheService* appcache_service,
     ResourceType resource_type,
     std::vector<std::unique_ptr<content::ResourceThrottle>>* throttles) {
-  AddExtraHeadersIfNeeded(request, resource_context);
-
   const content::ResourceRequestInfo* request_info =
       content::ResourceRequestInfo::ForRequest(request);
 
@@ -320,14 +318,6 @@
           request->url(), is_main_frame));
 }
 
-void AwResourceDispatcherHostDelegate::OnRequestRedirected(
-    const GURL& redirect_url,
-    net::URLRequest* request,
-    content::ResourceContext* resource_context,
-    network::ResourceResponse* response) {
-  AddExtraHeadersIfNeeded(request, resource_context);
-}
-
 void AwResourceDispatcherHostDelegate::RequestComplete(
     net::URLRequest* request) {
   if (request && !request->status().is_success()) {
@@ -478,34 +468,4 @@
   }
 }
 
-void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
-    net::URLRequest* request,
-    content::ResourceContext* resource_context) {
-  const content::ResourceRequestInfo* request_info =
-      content::ResourceRequestInfo::ForRequest(request);
-  if (!request_info)
-    return;
-  if (request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME)
-    return;
-
-  const ui::PageTransition transition = request_info->GetPageTransition();
-  const bool is_load_url =
-      transition & ui::PAGE_TRANSITION_FROM_API;
-  const bool is_go_back_forward =
-      transition & ui::PAGE_TRANSITION_FORWARD_BACK;
-  const bool is_reload = ui::PageTransitionCoreTypeIs(
-      transition, ui::PAGE_TRANSITION_RELOAD);
-  if (is_load_url || is_go_back_forward || is_reload) {
-    AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
-    std::string extra_headers = awrc->GetExtraHeaders(request->url());
-    if (!extra_headers.empty()) {
-      net::HttpRequestHeaders headers;
-      headers.AddHeadersFromString(extra_headers);
-      for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
-        request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
-      }
-    }
-  }
-}
-
 }  // namespace android_webview
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
index 5ab886e..1ea2fca 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h
@@ -46,12 +46,6 @@
   void OnResponseStarted(net::URLRequest* request,
                          content::ResourceContext* resource_context,
                          network::ResourceResponse* response) override;
-
-  void OnRequestRedirected(const GURL& redirect_url,
-                           net::URLRequest* request,
-                           content::ResourceContext* resource_context,
-                           network::ResourceResponse* response) override;
-
   void RequestComplete(net::URLRequest* request) override;
 
   void RemovePendingThrottleOnIoThread(IoThreadClientThrottle* throttle);
@@ -73,8 +67,6 @@
   void AddPendingThrottleOnIoThread(int render_process_id,
                                     int render_frame_id,
                                     IoThreadClientThrottle* pending_throttle);
-  void AddExtraHeadersIfNeeded(net::URLRequest* request,
-                               content::ResourceContext* resource_context);
 
   // Pair of render_process_id and render_frame_id.
   typedef std::map<content::GlobalFrameRoutingId, IoThreadClientThrottle*>
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 8143935..e9bd805 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -33,7 +33,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -44,62 +43,6 @@
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
 
-    private static class ShouldInterceptRequestClient extends TestAwContentsClient {
-
-        public static class ShouldInterceptRequestHelper extends CallbackHelper {
-            private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
-            private ConcurrentHashMap<String, AwWebResourceResponse> mReturnValuesByUrls =
-                    new ConcurrentHashMap<String, AwWebResourceResponse>();
-            private ConcurrentHashMap<String, AwWebResourceRequest> mRequestsByUrls =
-                    new ConcurrentHashMap<String, AwWebResourceRequest>();
-            // This is read on another thread, so needs to be marked volatile.
-            private volatile AwWebResourceResponse mShouldInterceptRequestReturnValue;
-            void setReturnValue(AwWebResourceResponse value) {
-                mShouldInterceptRequestReturnValue = value;
-            }
-            void setReturnValueForUrl(String url, AwWebResourceResponse value) {
-                mReturnValuesByUrls.put(url, value);
-            }
-            public List<String> getUrls() {
-                assert getCallCount() > 0;
-                return mShouldInterceptRequestUrls;
-            }
-            public AwWebResourceResponse getReturnValue(String url) {
-                AwWebResourceResponse value = mReturnValuesByUrls.get(url);
-                if (value != null) return value;
-                return mShouldInterceptRequestReturnValue;
-            }
-            public AwWebResourceRequest getRequestsForUrl(String url) {
-                assert getCallCount() > 0;
-                assert mRequestsByUrls.containsKey(url);
-                return mRequestsByUrls.get(url);
-            }
-            public void notifyCalled(AwWebResourceRequest request) {
-                mShouldInterceptRequestUrls.add(request.url);
-                mRequestsByUrls.put(request.url, request);
-                notifyCalled();
-            }
-        }
-
-        @Override
-        public AwWebResourceResponse shouldInterceptRequest(AwWebResourceRequest request) {
-            AwWebResourceResponse returnValue =
-                    mShouldInterceptRequestHelper.getReturnValue(request.url);
-            mShouldInterceptRequestHelper.notifyCalled(request);
-            return returnValue;
-        }
-
-        private ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
-
-        public ShouldInterceptRequestClient() {
-            mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
-        }
-
-        public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
-            return mShouldInterceptRequestHelper;
-        }
-    }
-
     private static final int TEAPOT_STATUS_CODE = 418;
     private static final String TEAPOT_RESPONSE_PHRASE = "I'm a teapot";
 
@@ -124,14 +67,14 @@
     }
 
     private TestWebServer mWebServer;
-    private ShouldInterceptRequestClient mContentsClient;
+    private TestAwContentsClient mContentsClient;
     private AwTestContainerView mTestContainerView;
     private AwContents mAwContents;
-    private ShouldInterceptRequestClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
+    private TestAwContentsClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
 
     @Before
     public void setUp() throws Exception {
-        mContentsClient = new ShouldInterceptRequestClient();
+        mContentsClient = new TestAwContentsClient();
         mTestContainerView = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         mAwContents = mTestContainerView.getAwContents();
         mShouldInterceptRequestHelper = mContentsClient.getShouldInterceptRequestHelper();
@@ -289,7 +232,7 @@
     @Feature({"AndroidWebView"})
     public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable {
         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
-        final ShouldInterceptRequestClient.OnLoadResourceHelper onLoadResourceHelper =
+        final TestAwContentsClient.OnLoadResourceHelper onLoadResourceHelper =
                 mContentsClient.getOnLoadResourceHelper();
 
         int callCount = onLoadResourceHelper.getCallCount();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
index 163209fd..4baba6b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
@@ -14,11 +14,16 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsClient.AwWebResourceRequest;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.ServerCertificate;
 
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * A test suite for WebView's network-related configuration. This tests WebView's default settings,
  * which are configured by either AwURLRequestContextGetter or NetworkContext.
@@ -64,4 +69,141 @@
             mTestServer.stopAndDestroyServer();
         }
     }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testRequestedWithHeaderMainFrame() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            final String echoHeaderUrl = mTestServer.getURL("/echoheader?X-Requested-With");
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), echoHeaderUrl);
+            AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+            final String xRequestedWith = mActivityTestRule.getJavaScriptResultBodyTextContent(
+                    mAwContents, mContentsClient);
+            final String packageName = InstrumentationRegistry.getInstrumentation()
+                                               .getTargetContext()
+                                               .getPackageName();
+            Assert.assertEquals("X-Requested-With header should be the app package name",
+                    packageName, xRequestedWith);
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testRequestedWithHeaderSubResource() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            // Use the test server's root path as the baseUrl to satisfy same-origin restrictions.
+            final String baseUrl = mTestServer.getURL("/");
+            final String echoHeaderUrl = mTestServer.getURL("/echoheader?X-Requested-With");
+            final String pageWithIframeHtml = "<html><body><p>Main frame</p><iframe src='"
+                    + echoHeaderUrl + "'/></body></html>";
+            // We use loadDataWithBaseUrlSync because we need to dynamically control the HTML
+            // content, which EmbeddedTestServer doesn't support. We don't need to encode content
+            // because loadDataWithBaseUrl() encodes content itself.
+            mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
+                    mContentsClient.getOnPageFinishedHelper(), pageWithIframeHtml,
+                    /* mimeType */ null, /* isBase64Encoded */ false, baseUrl, /* historyUrl */
+                    null);
+            AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+            final String xRequestedWith =
+                    getJavaScriptResultIframeTextContent(mAwContents, mContentsClient);
+            final String packageName = InstrumentationRegistry.getInstrumentation()
+                                               .getTargetContext()
+                                               .getPackageName();
+            Assert.assertEquals("X-Requested-With header should be the app package name",
+                    packageName, xRequestedWith);
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testRequestedWithHeaderHttpRedirect() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            final String echoHeaderUrl = mTestServer.getURL("/echoheader?X-Requested-With");
+            final String encodedEchoHeaderUrl = URLEncoder.encode(echoHeaderUrl, "UTF-8");
+            // Returns a server-redirect (301) to echoHeaderUrl.
+            final String redirectToEchoHeaderUrl =
+                    mTestServer.getURL("/server-redirect?" + encodedEchoHeaderUrl);
+            mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
+                    redirectToEchoHeaderUrl);
+            AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+            final String xRequestedWith = mActivityTestRule.getJavaScriptResultBodyTextContent(
+                    mAwContents, mContentsClient);
+            final String packageName = InstrumentationRegistry.getInstrumentation()
+                                               .getTargetContext()
+                                               .getPackageName();
+            Assert.assertEquals("X-Requested-With header should be the app package name",
+                    packageName, xRequestedWith);
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testRequestedWithApplicationValuePreferred() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            final String echoHeaderUrl = mTestServer.getURL("/echoheader?X-Requested-With");
+            final String applicationRequestedWithValue = "foo";
+            final Map<String, String> extraHeaders = new HashMap<>();
+            extraHeaders.put("X-Requested-With", applicationRequestedWithValue);
+            mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
+                    echoHeaderUrl, extraHeaders);
+            AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents);
+            final String xRequestedWith = mActivityTestRule.getJavaScriptResultBodyTextContent(
+                    mAwContents, mContentsClient);
+            Assert.assertEquals("Should prefer app's provided X-Requested-With header",
+                    applicationRequestedWithValue, xRequestedWith);
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Network"})
+    public void testRequestedWithHeaderShouldInterceptRequest() throws Throwable {
+        mTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            final String url = mTestServer.getURL("/any-http-url-will-suffice.html");
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+            AwWebResourceRequest request =
+                    mContentsClient.getShouldInterceptRequestHelper().getRequestsForUrl(url);
+            Assert.assertFalse("X-Requested-With should be invisible to shouldInterceptRequest",
+                    request.requestHeaders.containsKey("X-Requested-With"));
+        } finally {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    /**
+     * Like {@link AwActivityTestRule#getJavaScriptResultBodyTextContent}, but it gets the text
+     * content of the iframe instead. This assumes the main frame has only a single iframe.
+     */
+    private String getJavaScriptResultIframeTextContent(
+            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
+        final String script =
+                "document.getElementsByTagName('iframe')[0].contentDocument.body.textContent";
+        String raw =
+                mActivityTestRule.executeJavaScriptAndWaitForResult(awContents, viewClient, script);
+        return mActivityTestRule.maybeStripDoubleQuotes(raw);
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwProxyControllerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwProxyControllerTest.java
new file mode 100644
index 0000000..ece3870
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwProxyControllerTest.java
@@ -0,0 +1,252 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.test;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwProxyController;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.Feature;
+import org.chromium.net.test.util.TestWebServer;
+
+import java.util.concurrent.Executor;
+
+/**
+ * AwProxyController tests.
+ */
+@RunWith(AwJUnit4ClassRunner.class)
+public class AwProxyControllerTest {
+    @Rule
+    public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
+
+    private static final String MATCH_ALL_SCHEMES = "*";
+    private static final String DIRECT = "direct://";
+    private static final String LOOPBACK = "<-loopback>";
+    private static final String CONTENT = "CONTENT";
+    private static final String PROXY = "PROXY";
+
+    private AwProxyController mAwProxyController;
+    private TestWebServer mContentServer;
+    private TestWebServer mProxyServer;
+    private String mContentUrl;
+    private String mProxyUrl;
+
+    @Before
+    public void setup() throws Exception {
+        mAwProxyController = new AwProxyController();
+        mContentServer = TestWebServer.start();
+        mProxyServer = TestWebServer.startAdditional();
+        mContentUrl = mContentServer.setResponse(
+                "/", "<html><head><title>" + CONTENT + "</title></head>Page 1</html>", null);
+        mProxyUrl = mProxyServer
+                            .setResponse("/",
+                                    "<html><head><title>" + PROXY + "</title></head>Page 1</html>",
+                                    null)
+                            .replace("http://", "")
+                            .replace("/", "");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        clearProxyOverrideSync();
+        mContentServer.shutdown();
+        mProxyServer.shutdown();
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView"})
+    public void testProxyOverride() throws Throwable {
+        final TestAwContentsClient contentsClient = new TestAwContentsClient();
+        final AwTestContainerView testContainerView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testContainerView.getAwContents();
+
+        int proxyServerRequestCount = mProxyServer.getRequestCount("/");
+
+        // Set proxy override and load content url
+        // Localhost should use proxy with loopback rule
+        setProxyOverrideSync(
+                new String[][] {{MATCH_ALL_SCHEMES, mProxyUrl}}, new String[] {LOOPBACK});
+        TestAwContentsClient.OnReceivedTitleHelper onReceivedTitleHelper =
+                contentsClient.getOnReceivedTitleHelper();
+        int onReceivedTitleCallCount = onReceivedTitleHelper.getCallCount();
+        mActivityTestRule.loadUrlSync(
+                awContents, contentsClient.getOnPageFinishedHelper(), mContentUrl);
+        onReceivedTitleHelper.waitForCallback(onReceivedTitleCallCount);
+
+        proxyServerRequestCount++;
+        Assert.assertEquals(proxyServerRequestCount, mProxyServer.getRequestCount("/"));
+        Assert.assertEquals(PROXY, onReceivedTitleHelper.getTitle());
+
+        // Clear proxy override and load content url
+        clearProxyOverrideSync();
+        onReceivedTitleCallCount = onReceivedTitleHelper.getCallCount();
+        mActivityTestRule.loadUrlSync(
+                awContents, contentsClient.getOnPageFinishedHelper(), mContentUrl);
+        onReceivedTitleHelper.waitForCallback(onReceivedTitleCallCount);
+
+        Assert.assertEquals(proxyServerRequestCount, mProxyServer.getRequestCount("/"));
+        Assert.assertEquals(CONTENT, onReceivedTitleHelper.getTitle());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView"})
+    public void testProxyOverrideLocalhost() throws Throwable {
+        final TestAwContentsClient contentsClient = new TestAwContentsClient();
+        final AwTestContainerView testContainerView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
+        final AwContents awContents = testContainerView.getAwContents();
+
+        int proxyServerRequestCount = mProxyServer.getRequestCount("/");
+
+        // Set proxy override and load a local url
+        // Localhost should not use proxy settings
+        setProxyOverrideSync(new String[][] {{MATCH_ALL_SCHEMES, mProxyUrl}}, new String[] {});
+        TestAwContentsClient.OnReceivedTitleHelper onReceivedTitleHelper =
+                contentsClient.getOnReceivedTitleHelper();
+        int onReceivedTitleCallCount = onReceivedTitleHelper.getCallCount();
+        mActivityTestRule.loadUrlSync(
+                awContents, contentsClient.getOnPageFinishedHelper(), mContentUrl);
+        onReceivedTitleHelper.waitForCallback(onReceivedTitleCallCount);
+
+        Assert.assertEquals(proxyServerRequestCount, mProxyServer.getRequestCount("/"));
+        Assert.assertEquals(CONTENT, onReceivedTitleHelper.getTitle());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testCallbacks() throws Throwable {
+        // Test setProxyOverride's callback
+        setProxyOverrideSync(null, null);
+        // Test clearProxyOverride's callback with a proxy override setting
+        clearProxyOverrideSync();
+        // Test clearProxyOverride's callback without a proxy override setting
+        clearProxyOverrideSync();
+        // If we got to this point it means all callbacks were called as expected
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testValidInput() throws Throwable {
+        String[][] proxyRules = {{MATCH_ALL_SCHEMES, DIRECT},
+                {MATCH_ALL_SCHEMES, "www.example.com"},
+                {MATCH_ALL_SCHEMES, "http://www.example.com"},
+                {MATCH_ALL_SCHEMES, "https://www.example.com"},
+                {MATCH_ALL_SCHEMES, "www.example.com:123"},
+                {MATCH_ALL_SCHEMES, "http://www.example.com:123"}, {MATCH_ALL_SCHEMES, "10.0.0.1"},
+                {MATCH_ALL_SCHEMES, "10.0.0.1:123"}, {MATCH_ALL_SCHEMES, "http://10.0.0.1"},
+                {MATCH_ALL_SCHEMES, "https://10.0.0.1"}, {MATCH_ALL_SCHEMES, "http://10.0.0.1:123"},
+                {MATCH_ALL_SCHEMES, "[FE80:CD00:0000:0CDE:1257:0000:211E:729C]"},
+                {MATCH_ALL_SCHEMES, "[FE80:CD00:0:CDE:1257:0:211E:729C]"}};
+        String[] bypassRules = {
+                "www.rule.com", "*.rule.com", "*rule.com", "www.*.com", "www.rule*"};
+        setProxyOverrideSync(proxyRules, bypassRules);
+        // If we got to this point it means our input was accepted as expected
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testInvalidProxyUrls() throws Throwable {
+        String[] invalidProxyUrls = {
+                null,
+                "", // empty
+                "   ", // spaces only
+                "dddf:", // bad port
+                "dddd:d", // bad port
+                "http://", // no valid host/port
+                "http:/", // ambiguous, will fail due to bad port
+                "http:", // ambiguous, will fail due to bad port
+                "direct://xyz", // direct shouldn't have host/port
+        };
+
+        for (String proxyUrl : invalidProxyUrls) {
+            try {
+                setProxyOverrideSync(new String[][] {{MATCH_ALL_SCHEMES, proxyUrl}}, null);
+                Assert.fail("No exception for invalid proxy url: " + proxyUrl);
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testInvalidBypassRules() throws Throwable {
+        String[] invalidBypassRules = {
+                null,
+                "", // empty
+                "http://", // no valid host/port
+                "20:example.com", // bad port
+                "example.com:-20" // bad port
+        };
+
+        for (String bypassRule : invalidBypassRules) {
+            try {
+                setProxyOverrideSync(null, new String[] {bypassRule});
+                Assert.fail("No exception for invalid bypass rule: " + bypassRule);
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
+        }
+    }
+
+    private void setProxyOverrideSync(String[][] proxyRules, String[] bypassRules)
+            throws Exception {
+        CallbackHelper ch = new CallbackHelper();
+        int callCount = ch.getCallCount();
+        String result = ThreadUtils.runOnUiThreadBlocking(() -> {
+            return mAwProxyController.setProxyOverride(proxyRules, bypassRules, new Runnable() {
+                @Override
+                public void run() {
+                    ch.notifyCalled();
+                }
+            }, new SynchronousExecutor());
+        });
+        if (!result.isEmpty()) {
+            throw new IllegalArgumentException(result);
+        }
+        ch.waitForCallback(callCount);
+    }
+
+    private void clearProxyOverrideSync() throws Exception {
+        CallbackHelper ch = new CallbackHelper();
+        int callCount = ch.getCallCount();
+        String result = ThreadUtils.runOnUiThreadBlocking(() -> {
+            return mAwProxyController.clearProxyOverride(new Runnable() {
+                @Override
+                public void run() {
+                    ch.notifyCalled();
+                }
+            }, new SynchronousExecutor());
+        });
+        if (!result.isEmpty()) {
+            throw new IllegalArgumentException(result);
+        }
+        ch.waitForCallback(callCount);
+    }
+
+    static class SynchronousExecutor implements Executor {
+        @Override
+        public void execute(Runnable r) {
+            r.run();
+        }
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
index 62ce10a..7a2bc1cc 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java
@@ -24,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * AwContentsClient subclass used for testing.
@@ -48,6 +49,7 @@
     private final OnReceivedTitleHelper mOnReceivedTitleHelper;
     private final PictureListenerHelper mPictureListenerHelper;
     private final ShouldOverrideUrlLoadingHelper mShouldOverrideUrlLoadingHelper;
+    private final ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
     private final OnLoadResourceHelper mOnLoadResourceHelper;
     private final DoUpdateVisitedHistoryHelper mDoUpdateVisitedHistoryHelper;
     private final OnCreateWindowHelper mOnCreateWindowHelper;
@@ -71,6 +73,7 @@
         mOnReceivedTitleHelper = new OnReceivedTitleHelper();
         mPictureListenerHelper = new PictureListenerHelper();
         mShouldOverrideUrlLoadingHelper = new ShouldOverrideUrlLoadingHelper();
+        mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
         mOnLoadResourceHelper = new OnLoadResourceHelper();
         mDoUpdateVisitedHistoryHelper = new DoUpdateVisitedHistoryHelper();
         mOnCreateWindowHelper = new OnCreateWindowHelper();
@@ -123,6 +126,10 @@
         return mShouldOverrideUrlLoadingHelper;
     }
 
+    public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
+        return mShouldInterceptRequestHelper;
+    }
+
     public OnLoadResourceHelper getOnLoadResourceHelper() {
         return mOnLoadResourceHelper;
     }
@@ -516,6 +523,54 @@
     }
 
     /**
+     * Callback helper for shouldInterceptRequest.
+     */
+    public static class ShouldInterceptRequestHelper extends CallbackHelper {
+        private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
+        private ConcurrentHashMap<String, AwWebResourceResponse> mReturnValuesByUrls =
+                new ConcurrentHashMap<String, AwWebResourceResponse>();
+        private ConcurrentHashMap<String, AwWebResourceRequest> mRequestsByUrls =
+                new ConcurrentHashMap<String, AwWebResourceRequest>();
+        // This is read on another thread, so needs to be marked volatile.
+        private volatile AwWebResourceResponse mShouldInterceptRequestReturnValue;
+        void setReturnValue(AwWebResourceResponse value) {
+            mShouldInterceptRequestReturnValue = value;
+        }
+        void setReturnValueForUrl(String url, AwWebResourceResponse value) {
+            mReturnValuesByUrls.put(url, value);
+        }
+        public List<String> getUrls() {
+            assert getCallCount() > 0;
+            return mShouldInterceptRequestUrls;
+        }
+        public AwWebResourceResponse getReturnValue(String url) {
+            AwWebResourceResponse value = mReturnValuesByUrls.get(url);
+            if (value != null) return value;
+            return mShouldInterceptRequestReturnValue;
+        }
+        public AwWebResourceRequest getRequestsForUrl(String url) {
+            assert getCallCount() > 0;
+            assert mRequestsByUrls.containsKey(url);
+            return mRequestsByUrls.get(url);
+        }
+        public void notifyCalled(AwWebResourceRequest request) {
+            mShouldInterceptRequestUrls.add(request.url);
+            mRequestsByUrls.put(request.url, request);
+            notifyCalled();
+        }
+    }
+
+    @Override
+    public AwWebResourceResponse shouldInterceptRequest(AwWebResourceRequest request) {
+        super.shouldInterceptRequest(request);
+        if (TRACE) Log.i(TAG, "shouldInterceptRequest " + request.url);
+        AwWebResourceResponse returnValue =
+                mShouldInterceptRequestHelper.getReturnValue(request.url);
+        mShouldInterceptRequestHelper.notifyCalled(request);
+        return returnValue;
+    }
+
+    /**
      * Callback helper for OnLoadedResource.
      */
     public static class OnLoadResourceHelper extends CallbackHelper {
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 0addc54..41458e42 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -282,7 +282,7 @@
     const std::string& process_type,
     const content::MainFunctionParams& main_function_params) {
   if (process_type.empty()) {
-    browser_runner_.reset(content::BrowserMainRunner::Create());
+    browser_runner_ = content::BrowserMainRunner::Create();
     int exit_code = browser_runner_->Initialize(main_function_params);
     DCHECK_LT(exit_code, 0);
 
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index ff2678b..f94feb00 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -212,6 +212,7 @@
     "../javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwPermissionManagerTest.java",
+    "../javatests/src/org/chromium/android_webview/test/AwProxyControllerTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwScrollOffsetManagerTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java",
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn
index 658f42e..65f836b 100644
--- a/android_webview/tools/system_webview_shell/BUILD.gn
+++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -24,8 +24,9 @@
     "apk/src/org/chromium/webview_shell/StartupTimeActivity.java",
     "apk/src/org/chromium/webview_shell/TelemetryActivity.java",
     "apk/src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java",
-    "apk/src/org/chromium/webview_shell/WebViewCreateDestroyActivity.java",
+    "apk/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java",
     "apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java",
+    "apk/src/org/chromium/webview_shell/WebViewCreateDestroyActivity.java",
     "apk/src/org/chromium/webview_shell/WebViewLayoutTestActivity.java",
     "apk/src/org/chromium/webview_shell/WebViewThreadTestActivity.java",
     "apk/src/org/chromium/webview_shell/WebViewTracingActivity.java",
diff --git a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
index 3d7157b..4376adc 100644
--- a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
+++ b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
@@ -123,6 +123,12 @@
             android:exported="true">
         </activity>
 
+        <activity
+            android:name="org.chromium.webview_shell.WebViewAnimationTestActivity"
+            android:noHistory="true"
+            android:exported="true">
+        </activity>
+
         <uses-library android:name="android.test.runner" />
     </application>
 </manifest>
diff --git a/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_animation_test.xml b/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_animation_test.xml
new file mode 100644
index 0000000..44cf7c0
--- /dev/null
+++ b/android_webview/tools/system_webview_shell/apk/res/layout/activity_webview_animation_test.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <Button
+            android:id="@+id/translate"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/translate_button" />
+        <Button
+            android:id="@+id/scale"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/scale_button" />
+        <Button
+            android:id="@+id/rotate"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/rotate_button" />
+        <CheckBox
+            android:id="@+id/use_layer"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:checked="false"
+            android:text="@string/layer_button" />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/alpha_button"
+            android:layout_gravity="center_vertical" />
+        <SeekBar
+            android:id="@+id/view_alpha"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:max="100"
+            android:progress="100"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="8dp" />
+    </LinearLayout>
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1">
+        <WebView
+            android:id="@+id/webview"
+            android:layout_width="300dp"
+            android:layout_height="300dp"
+            android:layout_gravity="center" />
+    </FrameLayout>
+</LinearLayout>
diff --git a/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml b/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml
index cc94cf2..14c90c2d 100644
--- a/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml
+++ b/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml
@@ -13,6 +13,8 @@
           android:title="@string/menu_enable_tracing"/>
     <item android:id="@+id/menu_print"
           android:title="@string/menu_print"/>
+    <item android:id="@+id/start_animation_activity"
+          android:title="@string/menu_start_animation_activity"/>
     <item android:id="@+id/menu_about"
           android:title="@string/menu_about"/>
 </menu>
diff --git a/android_webview/tools/system_webview_shell/apk/res/values/strings.xml b/android_webview/tools/system_webview_shell/apk/res/values/strings.xml
index 7d74335..c39328a 100644
--- a/android_webview/tools/system_webview_shell/apk/res/values/strings.xml
+++ b/android_webview/tools/system_webview_shell/apk/res/values/strings.xml
@@ -17,7 +17,15 @@
     <string name="menu_reset_webview">Destroy and create new WebView</string>
     <string name="menu_clear_cache">Clear cache</string>
     <string name="menu_enable_tracing">Enable tracing</string>
+    <string name="menu_start_animation_activity">Animation test</string>
     <string name="menu_print">Print</string>
     <string name="menu_about">About WebView</string>
     <string name="load_url">Load URL</string>
+
+    <!-- activity_webview_animation_test strings -->
+    <string name="alpha_button">View Alpha</string>
+    <string name="layer_button">Layer</string>
+    <string name="rotate_button">Rotate</string>
+    <string name="scale_button">Scale</string>
+    <string name="translate_button">Translate</string>
 </resources>
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java
new file mode 100644
index 0000000..e67b6e5
--- /dev/null
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java
@@ -0,0 +1,157 @@
+// 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.
+
+package org.chromium.webview_shell;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.webkit.WebView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+/**
+ * Activity to exercise transform animations on WebView.
+ */
+public class WebViewAnimationTestActivity extends Activity {
+    private static final String HTML = "<html>"
+            + "  <head>"
+            + "    <style type =\"text/css\">"
+            + "      .container {"
+            + "            display: grid;"
+            + "            grid-template-columns: 100px 100px 100px 100px 100px;"
+            + "            grid-template-rows: 100px 100px 100px 100px 100px;"
+            + "      }"
+            + "     .alt1 {"
+            + "       background-color: #aaffaa;"
+            + "     }"
+            + "     .alt2 {"
+            + "       background-color: #ff4545;"
+            + "     }"
+            + "    </style>"
+            + "  </head>"
+            + "  <body>"
+            + "   <div class=\"container\">"
+            + "     <div class=\"alt1\">00</div>"
+            + "     <div class=\"alt2\">01</div>"
+            + "     <div class=\"alt1\">02</div>"
+            + "     <div class=\"alt2\">03</div>"
+            + "     <div class=\"alt1\">04</div>"
+            + "     <div class=\"alt2\">05</div>"
+            + "     <div class=\"alt1\">06</div>"
+            + "     <div class=\"alt2\">07</div>"
+            + "     <div class=\"alt1\">08</div>"
+            + "     <div class=\"alt2\">09</div>"
+            + "     <div class=\"alt1\">10</div>"
+            + "     <div class=\"alt2\">11</div>"
+            + "     <div class=\"alt1\">12</div>"
+            + "     <div class=\"alt2\">13</div>"
+            + "     <div class=\"alt1\">14</div>"
+            + "     <div class=\"alt2\">15</div>"
+            + "     <div class=\"alt1\">16</div>"
+            + "     <div class=\"alt2\">17</div>"
+            + "     <div class=\"alt1\">18</div>"
+            + "     <div class=\"alt2\">19</div>"
+            + "     <div class=\"alt1\">20</div>"
+            + "     <div class=\"alt2\">21</div>"
+            + "     <div class=\"alt1\">22</div>"
+            + "     <div class=\"alt2\">23</div>"
+            + "     <div class=\"alt1\">24</div>"
+            + "   </div>"
+            + "  </body>"
+            + "</html>";
+
+    private WebView mWebView;
+    private boolean mIsWindowHardwareAccelerated;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_webview_animation_test);
+        mWebView = (WebView) findViewById(R.id.webview);
+
+        mIsWindowHardwareAccelerated =
+                (getWindow().getAttributes().flags
+                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+                != 0;
+        mWebView.setBackgroundColor(0);
+        mWebView.loadDataWithBaseURL("http://foo.bar", HTML, "text/html", null, "http://foo.bar");
+        OnClickListener onClickListner = (View v) -> {
+            switch (v.getId()) {
+                case R.id.translate:
+                    runTranslate();
+                    break;
+                case R.id.scale:
+                    runScale();
+                    break;
+                case R.id.rotate:
+                    runRotate();
+                    break;
+            }
+        };
+        findViewById(R.id.scale).setOnClickListener(onClickListner);
+        findViewById(R.id.translate).setOnClickListener(onClickListner);
+        findViewById(R.id.rotate).setOnClickListener(onClickListner);
+        ((SeekBar) findViewById(R.id.view_alpha))
+                .setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+                    @Override
+                    public void onProgressChanged(SeekBar view, int progress, boolean fromUser) {
+                        switch (view.getId()) {
+                            case R.id.view_alpha:
+                                mWebView.setAlpha(progress / 100f);
+                                break;
+                        }
+                    }
+
+                    @Override
+                    public void onStartTrackingTouch(SeekBar seekBar) {}
+
+                    @Override
+                    public void onStopTrackingTouch(SeekBar seekBar) {}
+                });
+        CheckBox checkBox = ((CheckBox) findViewById(R.id.use_layer));
+        checkBox.setOnCheckedChangeListener(
+                (CompoundButton arg0, boolean checked) -> { setWebViewLayer(checked); });
+        setWebViewLayer(checkBox.isChecked());
+    }
+
+    private void runTranslate() {
+        if (mWebView.getTranslationX() == 0f) {
+            mWebView.animate().translationX(100f).translationY(100f);
+        } else {
+            mWebView.animate().translationX(0f).translationY(0f);
+        }
+    }
+
+    private void runScale() {
+        if (mWebView.getScaleX() == 1f) {
+            mWebView.animate().scaleX(.5f).scaleY(.5f);
+        } else {
+            mWebView.animate().scaleX(1f).scaleY(1f);
+        }
+    }
+
+    private void runRotate() {
+        if (mWebView.getRotationX() == 0f) {
+            mWebView.animate().rotationX(45f).rotationY(45f).rotation(90f);
+        } else {
+            mWebView.animate().rotationX(0f).rotationY(0f).rotation(0f);
+        }
+    }
+
+    private void setWebViewLayer(boolean isOnLayer) {
+        if (isOnLayer) {
+            mWebView.setLayerType(mIsWindowHardwareAccelerated ? View.LAYER_TYPE_HARDWARE
+                                                               : View.LAYER_TYPE_SOFTWARE,
+                    null);
+        } else {
+            mWebView.setLayerType(View.LAYER_TYPE_NONE, null);
+        }
+    }
+}
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index 9bcf425..7c275f5 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -550,6 +550,9 @@
                     }
                 }
                 return true;
+            case R.id.start_animation_activity:
+                startActivity(new Intent(this, WebViewAnimationTestActivity.class));
+                return true;
             case R.id.menu_print:
                 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
                 String jobName = "WebViewShell document";
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 2d6ff4b..3d6d3d9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -180,32 +180,6 @@
     "assistant/assistant_ui_controller.h",
     "assistant/assistant_view_delegate_impl.cc",
     "assistant/assistant_view_delegate_impl.h",
-    "assistant/model/assistant_alarm_timer_model.cc",
-    "assistant/model/assistant_alarm_timer_model.h",
-    "assistant/model/assistant_alarm_timer_model_observer.h",
-    "assistant/model/assistant_cache_model.cc",
-    "assistant/model/assistant_cache_model.h",
-    "assistant/model/assistant_cache_model_observer.h",
-    "assistant/model/assistant_interaction_model.cc",
-    "assistant/model/assistant_interaction_model.h",
-    "assistant/model/assistant_interaction_model_observer.h",
-    "assistant/model/assistant_notification_model.cc",
-    "assistant/model/assistant_notification_model.h",
-    "assistant/model/assistant_notification_model_observer.h",
-    "assistant/model/assistant_query.cc",
-    "assistant/model/assistant_query.h",
-    "assistant/model/assistant_query_history.cc",
-    "assistant/model/assistant_query_history.h",
-    "assistant/model/assistant_response.cc",
-    "assistant/model/assistant_response.h",
-    "assistant/model/assistant_screen_context_model.cc",
-    "assistant/model/assistant_screen_context_model.h",
-    "assistant/model/assistant_screen_context_model_observer.h",
-    "assistant/model/assistant_ui_element.cc",
-    "assistant/model/assistant_ui_element.h",
-    "assistant/model/assistant_ui_model.cc",
-    "assistant/model/assistant_ui_model.h",
-    "assistant/model/assistant_ui_model_observer.h",
     "assistant/ui/assistant_background_layer.cc",
     "assistant/ui/assistant_background_layer.h",
     "assistant/ui/assistant_container_view.cc",
@@ -958,6 +932,7 @@
     "system/tracing_notification_controller.h",
     "system/tray/actionable_view.cc",
     "system/tray/actionable_view.h",
+    "system/tray/detailed_view_delegate.cc",
     "system/tray/detailed_view_delegate.h",
     "system/tray/hover_highlight_view.cc",
     "system/tray/hover_highlight_view.h",
@@ -1029,8 +1004,6 @@
     "system/unified/top_shortcut_button.h",
     "system/unified/top_shortcuts_view.cc",
     "system/unified/top_shortcuts_view.h",
-    "system/unified/unified_detailed_view_delegate.cc",
-    "system/unified/unified_detailed_view_delegate.h",
     "system/unified/unified_notifier_settings_controller.cc",
     "system/unified/unified_notifier_settings_controller.h",
     "system/unified/unified_slider_bubble_controller.cc",
@@ -1355,6 +1328,7 @@
   deps = [
     "//ash/app_list/presenter",
     "//ash/app_menu",
+    "//ash/assistant/model",
     "//ash/assistant/ui:constants",
     "//ash/components/cursor",
     "//ash/components/fast_ink",
@@ -1878,6 +1852,7 @@
     "wm/screen_pinning_controller_unittest.cc",
     "wm/session_state_animator_impl_unittest.cc",
     "wm/splitview/split_view_controller_unittest.cc",
+    "wm/splitview/split_view_drag_indicators_unittest.cc",
     "wm/splitview/split_view_highlight_view_unittest.cc",
     "wm/stacking_controller_unittest.cc",
     "wm/system_gesture_event_filter_unittest.cc",
@@ -1922,6 +1897,7 @@
     "//ash/app_list:test_support",
     "//ash/app_list/presenter",
     "//ash/app_menu",
+    "//ash/assistant/model",
     "//ash/components/fast_ink",
     "//ash/components/fast_ink:unit_tests",
     "//ash/components/quick_launch/public/mojom:constants",
diff --git a/ash/accelerators/exit_warning_handler.cc b/ash/accelerators/exit_warning_handler.cc
index a96f141..a2773a0 100644
--- a/ash/accelerators/exit_warning_handler.cc
+++ b/ash/accelerators/exit_warning_handler.cc
@@ -148,7 +148,6 @@
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.accept_events = false;
   params.keep_on_top = true;
-  params.remove_standard_frame = true;
   params.delegate = delegate;
   params.bounds = bounds;
   params.name = "ExitWarningWindow";
diff --git a/ash/app_list/views/apps_container_view.cc b/ash/app_list/views/apps_container_view.cc
index 7cd7d4095..e677193 100644
--- a/ash/app_list/views/apps_container_view.cc
+++ b/ash/app_list/views/apps_container_view.cc
@@ -26,6 +26,7 @@
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/strings/grit/ui_strings.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/textfield/textfield.h"
 
 namespace app_list {
@@ -522,6 +523,12 @@
 void AppsContainerView::DisableFocusForShowingActiveFolder(bool disabled) {
   suggestion_chip_container_view_->DisableFocusForShowingActiveFolder(disabled);
   apps_grid_view_->DisableFocusForShowingActiveFolder(disabled);
+
+  // Ignore the page switcher in accessibility tree so that buttons inside it
+  // will not be accessed by ChromeVox.
+  page_switcher_->GetViewAccessibility().OverrideIsIgnored(disabled);
+  page_switcher_->GetViewAccessibility().NotifyAccessibilityEvent(
+      ax::mojom::Event::kTreeChanged);
 }
 
 int AppsContainerView::GetExpectedSuggestionChipY(float progress) {
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 3f8f3c26c..d8d8d1b2 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -689,11 +689,6 @@
         No recent items
       </message>
 
-      <!-- Window Selector -->
-      <message name="IDS_ASH_WINDOW_SELECTOR_INPUT_FILTER_ACCESSIBLE_NAME" desc="The accessible name for the window selector filter search input box.">
-        Type the name of an app or document
-      </message>
-
       <!-- Status tray charging strings. -->
       <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected.">
         Low-power charger connected
diff --git a/ash/assistant/model/BUILD.gn b/ash/assistant/model/BUILD.gn
new file mode 100644
index 0000000..64ae790
--- /dev/null
+++ b/ash/assistant/model/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2018 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.
+
+assert(is_chromeos)
+
+component("model") {
+  output_name = "assistant_model"
+
+  defines = [ "IS_ASSISTANT_MODEL_IMPL" ]
+
+  sources = [
+    "assistant_alarm_timer_model.cc",
+    "assistant_alarm_timer_model.h",
+    "assistant_alarm_timer_model_observer.h",
+    "assistant_cache_model.cc",
+    "assistant_cache_model.h",
+    "assistant_cache_model_observer.h",
+    "assistant_interaction_model.cc",
+    "assistant_interaction_model.h",
+    "assistant_interaction_model_observer.h",
+    "assistant_notification_model.cc",
+    "assistant_notification_model.h",
+    "assistant_notification_model_observer.h",
+    "assistant_query.cc",
+    "assistant_query.h",
+    "assistant_query_history.cc",
+    "assistant_query_history.h",
+    "assistant_response.cc",
+    "assistant_response.h",
+    "assistant_screen_context_model.cc",
+    "assistant_screen_context_model.h",
+    "assistant_screen_context_model_observer.h",
+    "assistant_ui_element.cc",
+    "assistant_ui_element.h",
+    "assistant_ui_model.cc",
+    "assistant_ui_model.h",
+    "assistant_ui_model_observer.h",
+  ]
+
+  deps = [
+    "//ash/assistant/ui:constants",
+    "//chromeos/services/assistant/public/mojom",
+    "//services/content/public/cpp",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/ash/assistant/model/DEPS b/ash/assistant/model/DEPS
new file mode 100644
index 0000000..606c206
--- /dev/null
+++ b/ash/assistant/model/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+  "-ash",
+  "+ash/assistant/ui/assistant_ui_constants.h",
+  "+ash/assistant/model",
+  "+chromeos/services/assistant/public" ,
+  "+services/content/public",
+  "+ui/gfx/geometry",
+]
diff --git a/ash/assistant/model/assistant_alarm_timer_model.h b/ash/assistant/model/assistant_alarm_timer_model.h
index 1ee7cb8..9d166bb4 100644
--- a/ash/assistant/model/assistant_alarm_timer_model.h
+++ b/ash/assistant/model/assistant_alarm_timer_model.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 
@@ -20,7 +21,7 @@
   kTimer,
 };
 
-struct AlarmTimer {
+struct COMPONENT_EXPORT(ASSISTANT_MODEL) AlarmTimer {
   std::string id;
   AlarmTimerType type;
   base::TimeTicks end_time;
@@ -31,7 +32,7 @@
 
 // The model belonging to AssistantAlarmTimerController which tracks alarm/timer
 // state and notifies a pool of observers.
-class AssistantAlarmTimerModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantAlarmTimerModel {
  public:
   AssistantAlarmTimerModel();
   ~AssistantAlarmTimerModel();
diff --git a/ash/assistant/model/assistant_alarm_timer_model_observer.h b/ash/assistant/model/assistant_alarm_timer_model_observer.h
index d586304..4431361 100644
--- a/ash/assistant/model/assistant_alarm_timer_model_observer.h
+++ b/ash/assistant/model/assistant_alarm_timer_model_observer.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/component_export.h"
 #include "base/observer_list_types.h"
 
 namespace base {
@@ -20,7 +21,8 @@
 
 // A checked observer which receives notification of changes to the Assistant
 // alarm/timer model.
-class AssistantAlarmTimerModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantAlarmTimerModelObserver
+    : public base::CheckedObserver {
  public:
   // Invoked when the specified alarm/timer has been added.
   virtual void OnAlarmTimerAdded(const AlarmTimer& alarm_timer,
diff --git a/ash/assistant/model/assistant_cache_model.h b/ash/assistant/model/assistant_cache_model.h
index eabe1f20..814b133 100644
--- a/ash/assistant/model/assistant_cache_model.h
+++ b/ash/assistant/model/assistant_cache_model.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
@@ -16,7 +17,7 @@
 
 class AssistantCacheModelObserver;
 
-class AssistantCacheModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantCacheModel {
  public:
   using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
   using AssistantSuggestionPtr =
diff --git a/ash/assistant/model/assistant_cache_model_observer.h b/ash/assistant/model/assistant_cache_model_observer.h
index 6344c39..571ee95a 100644
--- a/ash/assistant/model/assistant_cache_model_observer.h
+++ b/ash/assistant/model/assistant_cache_model_observer.h
@@ -7,6 +7,7 @@
 
 #include <map>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list_types.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
@@ -15,7 +16,8 @@
 
 // A checked observer which receives notification of changes to the Assistant
 // cache.
-class AssistantCacheModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantCacheModelObserver
+    : public base::CheckedObserver {
  public:
   using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
 
diff --git a/ash/assistant/model/assistant_interaction_model.h b/ash/assistant/model/assistant_interaction_model.h
index 5132f1a..cbb583a 100644
--- a/ash/assistant/model/assistant_interaction_model.h
+++ b/ash/assistant/model/assistant_interaction_model.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/assistant/model/assistant_query_history.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 
@@ -43,7 +44,7 @@
 
 // Models the Assistant interaction. This includes query state, state of speech
 // recognition, as well as a renderable AssistantResponse.
-class AssistantInteractionModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantInteractionModel {
  public:
   AssistantInteractionModel();
   ~AssistantInteractionModel();
diff --git a/ash/assistant/model/assistant_interaction_model_observer.h b/ash/assistant/model/assistant_interaction_model_observer.h
index 1f02676..f48cd1c 100644
--- a/ash/assistant/model/assistant_interaction_model_observer.h
+++ b/ash/assistant/model/assistant_interaction_model_observer.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list_types.h"
 
@@ -22,7 +23,8 @@
 
 // A checked observer which receives notification of changes to an Assistant
 // interaction.
-class AssistantInteractionModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantInteractionModelObserver
+    : public base::CheckedObserver {
  public:
   // Invoked when the interaction state is changed.
   virtual void OnInteractionStateChanged(InteractionState interaction_state) {}
diff --git a/ash/assistant/model/assistant_notification_model.h b/ash/assistant/model/assistant_notification_model.h
index 566f104f..cac6c3c 100644
--- a/ash/assistant/model/assistant_notification_model.h
+++ b/ash/assistant/model/assistant_notification_model.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
@@ -18,7 +19,7 @@
 
 // The model belonging to AssistantNotificationController which tracks
 // notification state and notifies a pool of observers.
-class AssistantNotificationModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantNotificationModel {
  public:
   using AssistantNotification =
       chromeos::assistant::mojom::AssistantNotification;
diff --git a/ash/assistant/model/assistant_notification_model_observer.h b/ash/assistant/model/assistant_notification_model_observer.h
index e98a11c..16cbb51ce 100644
--- a/ash/assistant/model/assistant_notification_model_observer.h
+++ b/ash/assistant/model/assistant_notification_model_observer.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_NOTIFICATION_MODEL_OBSERVER_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_NOTIFICATION_MODEL_OBSERVER_H_
 
+#include "base/component_export.h"
 #include "base/observer_list_types.h"
 
 namespace chromeos {
@@ -19,7 +20,8 @@
 
 // A checked observer which receives notification of changes to the Assistant
 // notification model.
-class AssistantNotificationModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantNotificationModelObserver
+    : public base::CheckedObserver {
  public:
   using AssistantNotification =
       chromeos::assistant::mojom::AssistantNotification;
diff --git a/ash/assistant/model/assistant_query.h b/ash/assistant/model/assistant_query.h
index 7aa1285..785929f 100644
--- a/ash/assistant/model/assistant_query.h
+++ b/ash/assistant/model/assistant_query.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 
 namespace ash {
@@ -36,7 +37,7 @@
 // AssistantQuery --------------------------------------------------------------
 
 // Base class for an Assistant query.
-class AssistantQuery {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantQuery {
  public:
   virtual ~AssistantQuery() = default;
 
@@ -64,7 +65,8 @@
 // AssistantNullQuery ----------------------------------------------------------
 
 // An null Assistant query used to signify the absence of an Assistant query.
-class AssistantNullQuery : public AssistantQuery {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantNullQuery
+    : public AssistantQuery {
  public:
   AssistantNullQuery()
       : AssistantQuery(AssistantQueryType::kNull,
@@ -82,7 +84,8 @@
 // AssistantTextQuery ----------------------------------------------------------
 
 // An Assistant text query.
-class AssistantTextQuery : public AssistantQuery {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantTextQuery
+    : public AssistantQuery {
  public:
   AssistantTextQuery(
       const std::string& text = std::string(),
@@ -111,7 +114,8 @@
 // recognition improves, both the high and low confidence portions of the query
 // will be non-empty. When speech is fully recognized, only the high confidence
 // portion will be populated.
-class AssistantVoiceQuery : public AssistantQuery {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantVoiceQuery
+    : public AssistantQuery {
  public:
   AssistantVoiceQuery() : AssistantVoiceQuery(std::string(), std::string()) {}
 
diff --git a/ash/assistant/model/assistant_query_history.h b/ash/assistant/model/assistant_query_history.h
index 71bedbb..7aac7db 100644
--- a/ash/assistant/model/assistant_query_history.h
+++ b/ash/assistant/model/assistant_query_history.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "ash/ash_export.h"
+#include "base/component_export.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/optional.h"
@@ -16,7 +16,7 @@
 namespace ash {
 
 // Caches user query history.
-class ASH_EXPORT AssistantQueryHistory {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantQueryHistory {
  public:
   class Iterator {
    public:
diff --git a/ash/assistant/model/assistant_response.h b/ash/assistant/model/assistant_response.h
index f257a864..8c3ee8f0 100644
--- a/ash/assistant/model/assistant_response.h
+++ b/ash/assistant/model/assistant_response.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "services/content/public/cpp/navigable_contents.h"
@@ -18,7 +19,7 @@
 class AssistantUiElement;
 
 // Models a renderable Assistant response.
-class AssistantResponse {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantResponse {
  public:
   using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
   using AssistantSuggestionPtr =
diff --git a/ash/assistant/model/assistant_screen_context_model.h b/ash/assistant/model/assistant_screen_context_model.h
index 93e27af..799d832 100644
--- a/ash/assistant/model/assistant_screen_context_model.h
+++ b/ash/assistant/model/assistant_screen_context_model.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 
@@ -18,7 +19,7 @@
   kInProgress,
 };
 
-class AssistantScreenContextModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantScreenContextModel {
  public:
   AssistantScreenContextModel();
   ~AssistantScreenContextModel();
diff --git a/ash/assistant/model/assistant_screen_context_model_observer.h b/ash/assistant/model/assistant_screen_context_model_observer.h
index a480798..de8dfc5 100644
--- a/ash/assistant/model/assistant_screen_context_model_observer.h
+++ b/ash/assistant/model/assistant_screen_context_model_observer.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list_types.h"
 
@@ -14,7 +15,8 @@
 
 // A checked observer which receives notification of changes to the Assistant
 // screen context model state.
-class AssistantScreenContextModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantScreenContextModelObserver
+    : public base::CheckedObserver {
  public:
   // Invoked when the screen context request state is changed.
   virtual void OnScreenContextRequestStateChanged(
diff --git a/ash/assistant/model/assistant_ui_element.h b/ash/assistant/model/assistant_ui_element.h
index 4849163..041f6d1a 100644
--- a/ash/assistant/model/assistant_ui_element.h
+++ b/ash/assistant/model/assistant_ui_element.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "services/content/public/cpp/navigable_contents.h"
 
@@ -24,7 +25,7 @@
 // AssistantUiElement ----------------------------------------------------------
 
 // Base class for a UI element that will be rendered inside of Assistant UI.
-class AssistantUiElement {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantUiElement {
  public:
   virtual ~AssistantUiElement() = default;
 
@@ -42,7 +43,8 @@
 // AssistantCardElement --------------------------------------------------------
 
 // An Assistant UI element that will be rendered as an HTML card.
-class AssistantCardElement : public AssistantUiElement {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantCardElement
+    : public AssistantUiElement {
  public:
   using ProcessingCallback = base::OnceCallback<void(bool)>;
 
@@ -103,7 +105,8 @@
 // AssistantTextElement --------------------------------------------------------
 
 // An Assistant UI element that will be rendered as text.
-class AssistantTextElement : public AssistantUiElement {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantTextElement
+    : public AssistantUiElement {
  public:
   explicit AssistantTextElement(const std::string& text)
       : AssistantUiElement(AssistantUiElementType::kText), text_(text) {}
diff --git a/ash/assistant/model/assistant_ui_model.cc b/ash/assistant/model/assistant_ui_model.cc
index 76e55820..b4c7de2 100644
--- a/ash/assistant/model/assistant_ui_model.cc
+++ b/ash/assistant/model/assistant_ui_model.cc
@@ -5,7 +5,6 @@
 #include "ash/assistant/model/assistant_ui_model.h"
 
 #include "ash/assistant/model/assistant_ui_model_observer.h"
-#include "ash/assistant/util/histogram_util.h"
 
 namespace ash {
 
diff --git a/ash/assistant/model/assistant_ui_model.h b/ash/assistant/model/assistant_ui_model.h
index dfc2edb..419fca2 100644
--- a/ash/assistant/model/assistant_ui_model.h
+++ b/ash/assistant/model/assistant_ui_model.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_UI_MODEL_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_UI_MODEL_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "ui/gfx/geometry/rect.h"
@@ -62,7 +63,7 @@
 };
 
 // Models the Assistant UI.
-class AssistantUiModel {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantUiModel {
  public:
   AssistantUiModel();
   ~AssistantUiModel();
diff --git a/ash/assistant/model/assistant_ui_model_observer.h b/ash/assistant/model/assistant_ui_model_observer.h
index 380202ca..66487df 100644
--- a/ash/assistant/model/assistant_ui_model_observer.h
+++ b/ash/assistant/model/assistant_ui_model_observer.h
@@ -5,6 +5,7 @@
 #ifndef ASH_ASSISTANT_MODEL_ASSISTANT_UI_MODEL_OBSERVER_H_
 #define ASH_ASSISTANT_MODEL_ASSISTANT_UI_MODEL_OBSERVER_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/observer_list_types.h"
 #include "base/optional.h"
@@ -19,7 +20,8 @@
 
 // A checked observer which receives notification of changes to the Assistant UI
 // model.
-class AssistantUiModelObserver : public base::CheckedObserver {
+class COMPONENT_EXPORT(ASSISTANT_MODEL) AssistantUiModelObserver
+    : public base::CheckedObserver {
  public:
   // Invoked when the UI mode is changed.
   virtual void OnUiModeChanged(AssistantUiMode ui_mode) {}
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 5134194b..2a6c99f 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -1892,6 +1892,41 @@
   }
 }
 
+TEST_F(DisplayManagerTest, DisplayRemovedOnlyOnceWhenEnteringDockedMode) {
+  // Create two displays, one internal, and one external, such that the full ID
+  // of the internal display is *greater* than the full ID of the external
+  // display, but the port-index part (least significant 8-bit) of the ID of the
+  // internal display is *less* than the port-index part of the external
+  // display.
+  constexpr int64_t kInternalDisplayId = 0x4D10DBEBF24802LL;
+  constexpr int64_t kExternalDisplayId = 0x4CABEF61B95735LL;
+  const auto internal_info = display::ManagedDisplayInfo::CreateFromSpecWithID(
+      "0+0-400x400", kInternalDisplayId);
+  const auto external_info = display::ManagedDisplayInfo::CreateFromSpecWithID(
+      "401+0-600x600", kExternalDisplayId);
+  vector<display::ManagedDisplayInfo> display_info_list{internal_info,
+                                                        external_info};
+  display_manager()->OnNativeDisplaysChanged(display_info_list);
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+
+  // Switching to docked mode in this configuration should result in only a
+  // single display removal, and no new display additions.
+  // https://crbug.com/921275.
+  reset();
+  display_info_list.clear();
+  display_info_list.emplace_back(external_info);
+  display_manager()->OnNativeDisplaysChanged(display_info_list);
+
+  // There should only be 1 display change, 0 adds, and 1 removal.
+  EXPECT_EQ("1 0 1 1 1", GetCountSummary());
+  const unsigned int expected_changed_metrics =
+      display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
+      display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
+      display::DisplayObserver::DISPLAY_METRIC_PRIMARY;
+  EXPECT_EQ(expected_changed_metrics, changed_metrics());
+}
+
 TEST_F(DisplayManagerTest, Rotate) {
   UpdateDisplay("100x200/r,300x400/l");
   EXPECT_EQ("1,1 100x200", GetDisplayInfoAt(0).bounds_in_native().ToString());
diff --git a/ash/highlighter/highlighter_controller_unittest.cc b/ash/highlighter/highlighter_controller_unittest.cc
index 8aad7da73..ccecc4f 100644
--- a/ash/highlighter/highlighter_controller_unittest.cc
+++ b/ash/highlighter/highlighter_controller_unittest.cc
@@ -294,7 +294,8 @@
   controller_->RemoveObserver(&observer);
 }
 
-TEST_F(HighlighterControllerTest, HighlighterGesturesScaled) {
+// Disabled due to https://crbug.com/917113.
+TEST_F(HighlighterControllerTest, DISABLED_HighlighterGesturesScaled) {
   controller_test_api_->SetEnabled(true);
   ui::test::EventGenerator* event_generator = GetEventGenerator();
   event_generator->EnterPenPointerMode();
@@ -413,7 +414,9 @@
 }
 
 // Test that the selection is never crossing the screen bounds.
-TEST_F(HighlighterControllerTest, SelectionInsideScreen) {
+//
+// Disabled due to https://crbug.com/917113.
+TEST_F(HighlighterControllerTest, DISABLED_SelectionInsideScreen) {
   controller_test_api_->SetEnabled(true);
   ui::test::EventGenerator* event_generator = GetEventGenerator();
   event_generator->EnterPenPointerMode();
diff --git a/ash/keyboard/ash_keyboard_controller.cc b/ash/keyboard/ash_keyboard_controller.cc
index 244ba5c..73997f2a 100644
--- a/ash/keyboard/ash_keyboard_controller.cc
+++ b/ash/keyboard/ash_keyboard_controller.cc
@@ -7,7 +7,6 @@
 #include "ash/keyboard/ash_keyboard_ui.h"
 #include "ash/keyboard/virtual_keyboard_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
@@ -54,7 +53,10 @@
       keyboard_ui_factory_ ? keyboard_ui_factory_->CreateKeyboardUI()
                            : std::make_unique<AshKeyboardUI>(this),
       virtual_keyboard_controller_.get());
-  ActivateKeyboard();
+
+  // Start preloading the virtual keyboard UI in the background, so that it
+  // shows up faster when needed.
+  keyboard_controller_->LoadKeyboardWindowInBackground();
 }
 
 void AshKeyboardController::DisableKeyboard() {
@@ -218,10 +220,6 @@
     return;
 
   switch (state) {
-    case session_manager::SessionState::OOBE:
-    case session_manager::SessionState::LOGIN_PRIMARY:
-      ActivateKeyboard();
-      break;
     case session_manager::SessionState::LOGGED_IN_NOT_ACTIVE:
     case session_manager::SessionState::ACTIVE:
       // Reload the keyboard on user profile change to refresh keyboard
@@ -238,42 +236,13 @@
 
 // private methods
 
-void AshKeyboardController::ActivateKeyboard() {
-  ActivateKeyboardForRoot(Shell::Get()->GetPrimaryRootWindowController());
-}
-
-void AshKeyboardController::ActivateKeyboardForRoot(
-    RootWindowController* controller) {
-  DCHECK(controller);
-  // If the keyboard is already activated, do nothing.
-  if (!keyboard_controller_->IsEnabled() ||
-      keyboard_controller_->GetRootWindow()) {
-    if (keyboard_controller_->GetRootWindow() != controller->GetRootWindow()) {
-      LOG(ERROR)
-          << "Tried to activate an already activated virtual keyboard on a "
-             "different root window";
-    }
-    return;
-  }
-
-  aura::Window* container =
-      controller->GetContainer(kShellWindowId_VirtualKeyboardContainer);
-  DCHECK(container);
-  keyboard_controller_->ActivateKeyboardInContainer(container);
-  keyboard_controller_->LoadKeyboardWindowInBackground();
-}
-
-void AshKeyboardController::DeactivateKeyboard() {
-  if (!keyboard_controller_->IsEnabled() ||
-      !keyboard_controller_->GetRootWindow()) {
-    return;
-  }
-  keyboard_controller_->DeactivateKeyboard();
-}
-
 void AshKeyboardController::OnRootWindowClosing(aura::Window* root_window) {
-  if (keyboard_controller_->GetRootWindow() == root_window)
-    DeactivateKeyboard();
+  if (keyboard_controller_->GetRootWindow() == root_window) {
+    aura::Window* new_parent =
+        virtual_keyboard_controller_->GetContainerForDefaultDisplay();
+    DCHECK_NE(root_window, new_parent);
+    keyboard_controller_->MoveToParentContainer(new_parent);
+  }
 }
 
 void AshKeyboardController::UpdateEnableFlag(bool was_enabled) {
diff --git a/ash/keyboard/ash_keyboard_controller.h b/ash/keyboard/ash_keyboard_controller.h
index 39e6da9..faaa0e2 100644
--- a/ash/keyboard/ash_keyboard_controller.h
+++ b/ash/keyboard/ash_keyboard_controller.h
@@ -26,7 +26,6 @@
 
 namespace ash {
 
-class RootWindowController;
 class SessionController;
 class VirtualKeyboardController;
 
@@ -101,15 +100,6 @@
     return virtual_keyboard_controller_.get();
   }
 
-  // Activates the keyboard controller for the primary root window controller.
-  void ActivateKeyboard();
-
-  // Activates the keyboard controller for |controller|.
-  void ActivateKeyboardForRoot(RootWindowController* controller);
-
-  // Deactivates the keyboard controller.
-  void DeactivateKeyboard();
-
   // Called whenever a root window is closing.
   // If the root window contains the virtual keyboard window, deactivates
   // the keyboard so that its window doesn't get destroyed as well.
diff --git a/ash/keyboard/ash_keyboard_ui.cc b/ash/keyboard/ash_keyboard_ui.cc
index d663b5b..3f510b8d 100644
--- a/ash/keyboard/ash_keyboard_ui.cc
+++ b/ash/keyboard/ash_keyboard_ui.cc
@@ -64,8 +64,9 @@
   }
 
   void Embed(base::UnguessableToken token, const gfx::Size& size) {
-    DVLOG(1) << "Embed contents. Size: " << size.ToString();
-    window()->SetBounds(gfx::Rect(size));
+    VLOG(1) << "Embed contents. Size: " << size.ToString();
+    if (!size.IsEmpty())
+      window()->SetBounds(gfx::Rect(size));
     if (!server_remote_view_host_) {
       server_remote_view_host_ = new ws::ServerRemoteViewHost(
           Shell::Get()->window_service_owner()->window_service());
@@ -102,6 +103,8 @@
     if (window == server_remote_view_host_->embedding_root())
       return;
 
+    VLOG(1) << "OnWindowBoundsChanged: " << new_bounds.ToString();
+
     // This happens when the client requests to resize the keyboard window,
     // typically through window.resizeTo in JS. Ash keyboard window bounds
     // should be updated to reflect the request.
diff --git a/ash/keyboard/virtual_keyboard_controller.cc b/ash/keyboard/virtual_keyboard_controller.cc
index 3f56f7f..0118713 100644
--- a/ash/keyboard/virtual_keyboard_controller.cc
+++ b/ash/keyboard/virtual_keyboard_controller.cc
@@ -9,6 +9,7 @@
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/ime/ime_controller.h"
 #include "ash/keyboard/ash_keyboard_controller.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -48,23 +49,6 @@
       chromeos::input_method::mojom::ImeKeyset::kNone);
 }
 
-void MoveKeyboardToDisplayInternal(const display::Display& display) {
-  // Remove the keyboard from curent root window controller
-  TRACE_EVENT0("vk", "MoveKeyboardToDisplayInternal");
-  auto* ash_keyboard_controller = Shell::Get()->ash_keyboard_controller();
-  ash_keyboard_controller->DeactivateKeyboard();
-
-  for (RootWindowController* controller :
-       Shell::Get()->GetAllRootWindowControllers()) {
-    if (display::Screen::GetScreen()
-            ->GetDisplayNearestWindow(controller->GetRootWindow())
-            .id() == display.id()) {
-      ash_keyboard_controller->ActivateKeyboardForRoot(controller);
-      break;
-    }
-  }
-}
-
 bool HasTouchableDisplay() {
   for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
     if (display.touch_support() == display::Display::TouchSupport::AVAILABLE)
@@ -73,16 +57,6 @@
   return false;
 }
 
-void MoveKeyboardToFirstTouchableDisplay() {
-  // Move the keyboard to the first display with touch capability.
-  for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
-    if (display.touch_support() == display::Display::TouchSupport::AVAILABLE) {
-      MoveKeyboardToDisplayInternal(display);
-      return;
-    }
-  }
-}
-
 }  // namespace
 
 VirtualKeyboardController::VirtualKeyboardController()
@@ -146,59 +120,42 @@
   UpdateKeyboardEnabled();
 }
 
-void VirtualKeyboardController::MoveKeyboardToDisplay(
+aura::Window* VirtualKeyboardController::GetContainerForDisplay(
     const display::Display& display) {
-  DCHECK(keyboard::KeyboardController::Get()->IsEnabled());
   DCHECK(display.is_valid());
 
-  TRACE_EVENT0("vk", "MoveKeyboardToDisplay");
-
-  aura::Window* keyboard_window =
-      keyboard::KeyboardController::Get()->GetKeyboardWindow();
-  DCHECK(keyboard_window);
-
-  const display::Screen* screen = display::Screen::GetScreen();
-  const display::Display current_display =
-      screen->GetDisplayNearestWindow(keyboard_window);
-
-  if (display.id() != current_display.id())
-    MoveKeyboardToDisplayInternal(display);
+  RootWindowController* controller =
+      Shell::Get()->GetRootWindowControllerWithDisplayId(display.id());
+  aura::Window* container =
+      controller->GetContainer(kShellWindowId_VirtualKeyboardContainer);
+  DCHECK(container);
+  return container;
 }
 
-void VirtualKeyboardController::MoveKeyboardToTouchableDisplay() {
-  DCHECK(keyboard::KeyboardController::Get()->IsEnabled());
-
-  TRACE_EVENT0("vk", "MoveKeyboardToTouchableDisplay");
-
-  aura::Window* keyboard_window =
-      keyboard::KeyboardController::Get()->GetKeyboardWindow();
-  DCHECK(keyboard_window);
-
+aura::Window* VirtualKeyboardController::GetContainerForDefaultDisplay() {
   const display::Screen* screen = display::Screen::GetScreen();
-  const display::Display current_display =
-      screen->GetDisplayNearestWindow(keyboard_window);
 
   if (wm::GetFocusedWindow()) {
-    // Move the virtual keyboard to the focused display if that display has
-    // touch capability or no other display has touch capability.
+    // Return the focused display if that display has touch capability or no
+    // other display has touch capability.
     const display::Display focused_display =
-        display::Screen::GetScreen()->GetDisplayNearestWindow(
-            wm::GetFocusedWindow());
-    if (current_display.id() != focused_display.id() &&
-        focused_display.is_valid() &&
+        screen->GetDisplayNearestWindow(wm::GetFocusedWindow());
+    if (focused_display.is_valid() &&
         (focused_display.touch_support() ==
              display::Display::TouchSupport::AVAILABLE ||
          !HasTouchableDisplay())) {
-      MoveKeyboardToDisplayInternal(focused_display);
-      return;
+      return GetContainerForDisplay(focused_display);
     }
   }
 
-  if (current_display.touch_support() !=
-      display::Display::TouchSupport::AVAILABLE) {
-    // The keyboard is currently on the display without touch capability.
-    MoveKeyboardToFirstTouchableDisplay();
+  // Otherwise, get the first touchable display.
+  for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
+    if (display.touch_support() == display::Display::TouchSupport::AVAILABLE)
+      return GetContainerForDisplay(display);
   }
+
+  // If there are no touchable displays, then just return the primary display.
+  return GetContainerForDisplay(screen->GetPrimaryDisplay());
 }
 
 void VirtualKeyboardController::UpdateDevices() {
diff --git a/ash/keyboard/virtual_keyboard_controller.h b/ash/keyboard/virtual_keyboard_controller.h
index f24e7e316b..92ab062 100644
--- a/ash/keyboard/virtual_keyboard_controller.h
+++ b/ash/keyboard/virtual_keyboard_controller.h
@@ -49,8 +49,9 @@
   void ToggleIgnoreExternalKeyboard();
 
   // keyboard::KeyboardLayoutDelegate:
-  void MoveKeyboardToDisplay(const display::Display& display) override;
-  void MoveKeyboardToTouchableDisplay() override;
+  aura::Window* GetContainerForDefaultDisplay() override;
+  aura::Window* GetContainerForDisplay(
+      const display::Display& display) override;
 
   // keyboard::KeyboardControllerObserver:
   void OnKeyboardEnabledChanged(bool is_enabled) override;
diff --git a/ash/keyboard/virtual_keyboard_controller_unittest.cc b/ash/keyboard/virtual_keyboard_controller_unittest.cc
index b542d0b..81a3a64b 100644
--- a/ash/keyboard/virtual_keyboard_controller_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_controller_unittest.cc
@@ -447,11 +447,11 @@
   ASSERT_TRUE(keyboard_controller_->IsKeyboardEnableRequested());
 }
 
-// Test for http://crbug.com/297858. |MoveKeyboardToTouchableDisplay| should
-// move keyboard to primary display if no display has touch capability and
+// Test for http://crbug.com/297858. |GetContainerForDefaultDisplay| should
+// return the primary display if no display has touch capability and
 // no window is focused.
 TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToPrimaryDisplayWhenNoDisplayHasTouch) {
+       DefaultContainerInPrimaryDisplayWhenNoDisplayHasTouch) {
   UpdateDisplay("500x500,500x500");
 
   EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
@@ -459,15 +459,15 @@
   EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
             GetSecondaryDisplay().touch_support());
 
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-
-  EXPECT_EQ(GetPrimaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetPrimaryRootWindow(), GetVirtualKeyboardController()
+                                        ->GetContainerForDefaultDisplay()
+                                        ->GetRootWindow());
 }
 
-// Test for http://crbug.com/297858. |MoveKeyboardToTouchableDisplay| should
+// Test for http://crbug.com/297858. |GetContainerForDefaultDisplay| should
 // move keyboard to focused display if no display has touch capability.
 TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToFocusedDisplayWhenNoDisplayHasTouch) {
+       DefaultContainerIsInFocusedDisplayWhenNoDisplayHasTouch) {
   UpdateDisplay("500x500,500x500");
 
   EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
@@ -476,16 +476,15 @@
             GetSecondaryDisplay().touch_support());
 
   CreateFocusedTestWindowInRootWindow(GetSecondaryRootWindow());
-
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-
-  EXPECT_EQ(GetSecondaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetSecondaryRootWindow(), GetVirtualKeyboardController()
+                                          ->GetContainerForDefaultDisplay()
+                                          ->GetRootWindow());
 }
 
-// Test for http://crbug.com/303429. |MoveKeyboardToTouchableDisplay| should
+// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
 // move keyboard to first touchable display when there is one.
 TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToFirstTouchableDisplay) {
+       DefaultContainerIsInFirstTouchableDisplay) {
   UpdateDisplay("500x500,500x500");
 
   // Make secondary display touchable.
@@ -498,16 +497,17 @@
   EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
             GetSecondaryDisplay().touch_support());
 
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-
-  EXPECT_EQ(GetSecondaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetSecondaryRootWindow(), GetVirtualKeyboardController()
+                                          ->GetContainerForDefaultDisplay()
+                                          ->GetRootWindow());
 }
 
-// Test for http://crbug.com/303429. |MoveKeyboardToTouchableDisplay| should
+// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
 // move keyboard to first touchable display when the focused display is not
 // touchable.
-TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToFirstTouchableDisplayIfFocusedDisplayIsNotTouchable) {
+TEST_F(
+    VirtualKeyboardControllerAlwaysEnabledTest,
+    DefaultContainerIsInFirstTouchableDisplayIfFocusedDisplayIsNotTouchable) {
   UpdateDisplay("500x500,500x500");
 
   // Make secondary display touchable.
@@ -523,14 +523,15 @@
   // Focus on primary display.
   CreateFocusedTestWindowInRootWindow(GetPrimaryRootWindow());
 
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-  EXPECT_EQ(GetSecondaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetSecondaryRootWindow(), GetVirtualKeyboardController()
+                                          ->GetContainerForDefaultDisplay()
+                                          ->GetRootWindow());
 }
 
-// Test for http://crbug.com/303429. |MoveKeyboardToTouchableDisplay| should
+// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
 // move keyborad to first touchable display when there is one.
 TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToFocusedDisplayIfTouchable) {
+       DefaultContainerIsInFocusedDisplayIfTouchable) {
   UpdateDisplay("500x500,500x500");
 
   // Make both displays touchable.
@@ -548,19 +549,20 @@
 
   // Focus on secondary display.
   CreateFocusedTestWindowInRootWindow(GetSecondaryRootWindow());
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-  EXPECT_EQ(GetSecondaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetSecondaryRootWindow(), GetVirtualKeyboardController()
+                                          ->GetContainerForDefaultDisplay()
+                                          ->GetRootWindow());
 
   // Focus on primary display.
   CreateFocusedTestWindowInRootWindow(GetPrimaryRootWindow());
-  GetVirtualKeyboardController()->MoveKeyboardToTouchableDisplay();
-  EXPECT_EQ(GetPrimaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetPrimaryRootWindow(), GetVirtualKeyboardController()
+                                        ->GetContainerForDefaultDisplay()
+                                        ->GetRootWindow());
 }
 
-// Test for http://crbug.com/303429. |MoveKeyboardToDisplay| should move
+// Test for http://crbug.com/303429. |GetContainerForDisplay| should move
 // keyboard to specified display even when it's not touchable.
-TEST_F(VirtualKeyboardControllerAlwaysEnabledTest,
-       MovesKeyboardToSpecifiedDisplay) {
+TEST_F(VirtualKeyboardControllerAlwaysEnabledTest, GetContainerForDisplay) {
   UpdateDisplay("500x500,500x500");
 
   // Make primary display touchable.
@@ -574,12 +576,16 @@
             GetSecondaryDisplay().touch_support());
 
   // Move to primary display.
-  GetVirtualKeyboardController()->MoveKeyboardToDisplay(GetPrimaryDisplay());
-  EXPECT_EQ(GetPrimaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetPrimaryRootWindow(),
+            GetVirtualKeyboardController()
+                ->GetContainerForDisplay(GetPrimaryDisplay())
+                ->GetRootWindow());
 
   // Move to secondary display.
-  GetVirtualKeyboardController()->MoveKeyboardToDisplay(GetSecondaryDisplay());
-  EXPECT_EQ(GetSecondaryRootWindow(), keyboard_controller_->GetRootWindow());
+  EXPECT_EQ(GetSecondaryRootWindow(),
+            GetVirtualKeyboardController()
+                ->GetContainerForDisplay(GetSecondaryDisplay())
+                ->GetRootWindow());
 }
 
 // Test for https://crbug.com/897007.
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index 2acd764..42ad582 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -29,8 +29,6 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       keyboard::switches::kEnableVirtualKeyboard);
   LoginTestBase::SetUp();
-
-  Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
 }
 
 void LoginKeyboardTestBase::ShowKeyboard() {
diff --git a/ash/magnifier/magnification_controller_unittest.cc b/ash/magnifier/magnification_controller_unittest.cc
index 86aef3e..c6e406c1 100644
--- a/ash/magnifier/magnification_controller_unittest.cc
+++ b/ash/magnifier/magnification_controller_unittest.cc
@@ -964,7 +964,8 @@
             old_keyboard_overscroll_value);
 }
 
-TEST_F(MagnificationControllerTest, TextfieldFocusedWithKeyboard) {
+// Disabled due to https://crbug.com/917113.
+TEST_F(MagnificationControllerTest, DISABLED_TextfieldFocusedWithKeyboard) {
   // Set up text input view.
   text_input_helper_.CreateAndShowTextInputView(gfx::Rect(500, 200, 80, 80));
   gfx::Rect text_input_bounds = text_input_helper_.GetTextInputViewBounds();
diff --git a/ash/media/media_notification_controller.cc b/ash/media/media_notification_controller.cc
index 3b4da98e..31804e9 100644
--- a/ash/media/media_notification_controller.cc
+++ b/ash/media/media_notification_controller.cc
@@ -40,10 +40,8 @@
   media_session::mojom::AudioFocusManagerPtr audio_focus_ptr;
   connector->BindInterface(media_session::mojom::kServiceName,
                            mojo::MakeRequest(&audio_focus_ptr));
-
-  media_session::mojom::MediaControllerManagerPtr controller_manager_ptr;
   connector->BindInterface(media_session::mojom::kServiceName,
-                           mojo::MakeRequest(&controller_manager_ptr));
+                           mojo::MakeRequest(&controller_manager_ptr_));
 
   media_session::mojom::AudioFocusObserverPtr audio_focus_observer;
   audio_focus_observer_binding_.Bind(mojo::MakeRequest(&audio_focus_observer));
diff --git a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
index dade063..0f46a43a 100644
--- a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.cc
@@ -224,14 +224,6 @@
   close_button_->set_paint_as_active(paint_as_active);
 }
 
-void FrameCaptionButtonContainerView::SetColorMode(
-    views::FrameCaptionButton::ColorMode color_mode) {
-  menu_button_->SetColorMode(color_mode);
-  minimize_button_->SetColorMode(color_mode);
-  size_button_->SetColorMode(color_mode);
-  close_button_->SetColorMode(color_mode);
-}
-
 void FrameCaptionButtonContainerView::SetBackgroundColor(
     SkColor background_color) {
   menu_button_->SetBackgroundColor(background_color);
diff --git a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.h b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.h
index 27309c7..79d70e9 100644
--- a/ash/public/cpp/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/public/cpp/caption_buttons/frame_caption_button_container_view.h
@@ -82,9 +82,6 @@
   // a repaint.
   void SetPaintAsActive(bool paint_as_active);
 
-  // Sets whether the buttons should use themed foreground color computation.
-  void SetColorMode(views::FrameCaptionButton::ColorMode color_mode);
-
   // Sets the background frame color that buttons should compute their color
   // respective to.
   void SetBackgroundColor(SkColor background_color);
diff --git a/ash/public/cpp/default_frame_header.cc b/ash/public/cpp/default_frame_header.cc
index c97ffb39..606c9c9 100644
--- a/ash/public/cpp/default_frame_header.cc
+++ b/ash/public/cpp/default_frame_header.cc
@@ -163,8 +163,16 @@
 }
 
 SkColor DefaultFrameHeader::GetTitleColor() const {
-  return color_utils::PickContrastingColor(
-      SK_ColorWHITE, SkColorSetRGB(40, 40, 40), GetCurrentFrameColor());
+  // Use IsDark() to change target colors instead of PickContrastingColor(), so
+  // that FrameCaptionButton::GetButtonColor() (which uses different target
+  // colors) can change between light/dark targets at the same time.  It looks
+  // bad when the title and caption buttons disagree about whether to be light
+  // or dark.
+  const SkColor frame_color = GetCurrentFrameColor();
+  const SkColor desired_color = color_utils::IsDark(frame_color)
+                                    ? SK_ColorWHITE
+                                    : SkColorSetRGB(40, 40, 40);
+  return color_utils::GetColorWithMinimumContrast(desired_color, frame_color);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc
index a21f6589..2c9e7d1 100644
--- a/ash/public/cpp/frame_header.cc
+++ b/ash/public/cpp/frame_header.cc
@@ -165,7 +165,6 @@
 void FrameHeader::SetBackButton(views::FrameCaptionButton* back_button) {
   back_button_ = back_button;
   if (back_button_) {
-    back_button_->SetColorMode(button_color_mode_);
     back_button_->SetBackgroundColor(GetCurrentFrameColor());
     back_button_->SetImage(views::CAPTION_BUTTON_ICON_BACK,
                            views::FrameCaptionButton::ANIMATE_NO,
@@ -204,12 +203,10 @@
 }
 
 void FrameHeader::UpdateCaptionButtonColors() {
-  caption_button_container_->SetColorMode(button_color_mode_);
-  caption_button_container_->SetBackgroundColor(GetCurrentFrameColor());
-  if (back_button_) {
-    back_button_->SetColorMode(button_color_mode_);
-    back_button_->SetBackgroundColor(GetCurrentFrameColor());
-  }
+  const SkColor frame_color = GetCurrentFrameColor();
+  caption_button_container_->SetBackgroundColor(frame_color);
+  if (back_button_)
+    back_button_->SetBackgroundColor(frame_color);
 }
 
 void FrameHeader::PaintTitleBar(gfx::Canvas* canvas) {
diff --git a/ash/public/cpp/frame_header.h b/ash/public/cpp/frame_header.h
index c113e26..a644e30 100644
--- a/ash/public/cpp/frame_header.h
+++ b/ash/public/cpp/frame_header.h
@@ -76,11 +76,6 @@
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
 
-  void set_button_color_mode(
-      views::FrameCaptionButton::ColorMode button_color_mode) {
-    button_color_mode_ = button_color_mode;
-  }
-
  protected:
   FrameHeader(views::Widget* target_widget, views::View* view);
 
@@ -125,9 +120,6 @@
 
   gfx::Rect GetTitleBounds() const;
 
-  views::FrameCaptionButton::ColorMode button_color_mode_ =
-      views::FrameCaptionButton::ColorMode::kDefault;
-
   // The widget that the caption buttons act on. This can be different from
   // |view_|'s widget.
   views::Widget* target_widget_;
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 205a5d4..d648153 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -69,7 +69,6 @@
     "notification_sms_sync.icon",
     "notification_stylus_battery_warning.icon",
     "notification_timer.icon",
-    "overview_text_filter_search.icon",
     "overview_window_close.icon",
     "overview_drop_target_plus.icon",
     "palette_action_capture_region.icon",
diff --git a/ash/resources/vector_icons/overview_text_filter_search.icon b/ash/resources/vector_icons/overview_text_filter_search.icon
deleted file mode 100644
index 363f869..0000000
--- a/ash/resources/vector_icons/overview_text_filter_search.icon
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 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.
-
-CANVAS_DIMENSIONS, 20,
-MOVE_TO, 13.01f, 11.81f,
-R_H_LINE_TO, -0.63f,
-R_LINE_TO, -0.22f, -0.22f,
-R_ARC_TO, 5.18f, 5.18f, 0, 0, 0, 1.26f, -3.39f,
-R_ARC_TO, 5.2f, 5.2f, 0, 1, 0, -5.2f, 5.2f,
-R_ARC_TO, 5.18f, 5.18f, 0, 0, 0, 3.39f, -1.26f,
-R_LINE_TO, 0.22f, 0.22f,
-R_V_LINE_TO, 0.63f,
-LINE_TO, 15.81f, 17,
-LINE_TO, 17, 15.81f,
-R_LINE_TO, -3.99f, -4,
-CLOSE,
-R_MOVE_TO, -4.8f, 0,
-R_ARC_TO, 3.6f, 3.6f, 0, 0, 1, -3.6f, -3.6f,
-R_ARC_TO, 3.6f, 3.6f, 0, 0, 1, 3.6f, -3.6f,
-R_ARC_TO, 3.6f, 3.6f, 0, 0, 1, 3.6f, 3.6f,
-R_ARC_TO, 3.6f, 3.6f, 0, 0, 1, -3.6f, 3.6f,
-CLOSE
diff --git a/ash/shelf/shelf_background_animator.cc b/ash/shelf/shelf_background_animator.cc
index 20167973..dc40995 100644
--- a/ash/shelf/shelf_background_animator.cc
+++ b/ash/shelf/shelf_background_animator.cc
@@ -54,47 +54,6 @@
   return color_profile;
 }
 
-// Gets the target color alpha value of shelf and shelf item according to the
-// given |background_type|.
-std::pair<int, int> GetTargetColorAlphaValues(
-    ShelfBackgroundType background_type) {
-  int target_shelf_color_alpha = SK_AlphaTRANSPARENT;
-  int target_item_color_alpha = SK_AlphaTRANSPARENT;
-
-  switch (background_type) {
-    case SHELF_BACKGROUND_DEFAULT:
-      target_shelf_color_alpha = kShelfTranslucentAlpha;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-    case SHELF_BACKGROUND_MAXIMIZED:
-      target_shelf_color_alpha = kShelfTranslucentMaximizedWindow;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-    case SHELF_BACKGROUND_APP_LIST:
-      target_shelf_color_alpha = kShelfTranslucentOverAppList;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-    case SHELF_BACKGROUND_SPLIT_VIEW:
-      target_shelf_color_alpha = ShelfBackgroundAnimator::kMaxAlpha;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-    case SHELF_BACKGROUND_OOBE:
-      target_shelf_color_alpha = SK_AlphaTRANSPARENT;
-      target_item_color_alpha = SK_AlphaOPAQUE;
-      break;
-    case SHELF_BACKGROUND_LOGIN:
-      target_shelf_color_alpha = SK_AlphaTRANSPARENT;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-    case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
-      target_shelf_color_alpha =
-          login_constants::kNonBlurredWallpaperBackgroundAlpha;
-      target_item_color_alpha = SK_AlphaTRANSPARENT;
-      break;
-  }
-  return std::pair<int, int>(target_shelf_color_alpha, target_item_color_alpha);
-}
-
 }  // namespace
 
 ShelfBackgroundAnimator::AnimationValues::AnimationValues() = default;
@@ -153,7 +112,6 @@
 void ShelfBackgroundAnimator::NotifyObserver(
     ShelfBackgroundAnimatorObserver* observer) {
   observer->UpdateShelfBackground(shelf_background_values_.current_color());
-  observer->UpdateShelfItemBackground(item_background_values_.current_color());
 }
 
 void ShelfBackgroundAnimator::PaintBackground(
@@ -179,9 +137,27 @@
   animator_.reset();
 }
 
+// Gets the target color alpha value of the shelf according to the given
+// |background_type|.
 int ShelfBackgroundAnimator::GetBackgroundAlphaValue(
     ShelfBackgroundType background_type) const {
-  return GetTargetColorAlphaValues(background_type).first;
+  switch (background_type) {
+    case SHELF_BACKGROUND_DEFAULT:
+      return kShelfTranslucentAlpha;
+    case SHELF_BACKGROUND_MAXIMIZED:
+      return kShelfTranslucentMaximizedWindow;
+    case SHELF_BACKGROUND_APP_LIST:
+      return kShelfTranslucentOverAppList;
+    case SHELF_BACKGROUND_SPLIT_VIEW:
+      return ShelfBackgroundAnimator::kMaxAlpha;
+    case SHELF_BACKGROUND_OOBE:
+      return SK_AlphaTRANSPARENT;
+    case SHELF_BACKGROUND_LOGIN:
+      return SK_AlphaTRANSPARENT;
+    case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
+      return login_constants::kNonBlurredWallpaperBackgroundAlpha;
+  }
+  return SK_AlphaTRANSPARENT;
 }
 
 void ShelfBackgroundAnimator::OnWallpaperColorsChanged() {
@@ -233,15 +209,11 @@
     return false;
 
   AnimationValues target_shelf_background_values;
-  AnimationValues target_item_background_values;
-  GetTargetValues(background_type, &target_shelf_background_values,
-                  &target_item_background_values);
+  GetTargetValues(background_type, &target_shelf_background_values);
 
   return previous_background_type_ == background_type &&
          shelf_background_values_.InitialValuesEqualTargetValuesOf(
-             target_shelf_background_values) &&
-         item_background_values_.InitialValuesEqualTargetValuesOf(
-             target_item_background_values);
+             target_shelf_background_values);
 }
 
 void ShelfBackgroundAnimator::CreateAnimator(
@@ -273,14 +245,12 @@
 
 void ShelfBackgroundAnimator::SetTargetValues(
     ShelfBackgroundType background_type) {
-  GetTargetValues(background_type, &shelf_background_values_,
-                  &item_background_values_);
+  GetTargetValues(background_type, &shelf_background_values_);
 }
 
 void ShelfBackgroundAnimator::GetTargetValues(
     ShelfBackgroundType background_type,
-    AnimationValues* shelf_background_values,
-    AnimationValues* item_background_values) const {
+    AnimationValues* shelf_background_values) const {
   // Fetches wallpaper color and darkens it.
   auto darken_wallpaper = [&](int darkening_alpha) {
     if (!wallpaper_controller_)
@@ -294,48 +264,36 @@
   };
 
   SkColor shelf_target_color = kShelfDefaultBaseColor;
-  SkColor item_target_color = kShelfDefaultBaseColor;
   switch (background_type) {
     case SHELF_BACKGROUND_DEFAULT:
     case SHELF_BACKGROUND_APP_LIST:
       shelf_target_color = darken_wallpaper(kShelfTranslucentColorDarkenAlpha);
-      item_target_color = shelf_target_color;
       break;
     case SHELF_BACKGROUND_MAXIMIZED:
       shelf_target_color = darken_wallpaper(kShelfOpaqueColorDarkenAlpha);
-      item_target_color = shelf_target_color;
       break;
     case SHELF_BACKGROUND_SPLIT_VIEW:
       shelf_target_color = darken_wallpaper(ShelfBackgroundAnimator::kMaxAlpha);
-      item_target_color = shelf_target_color;
       break;
     case SHELF_BACKGROUND_OOBE:
       shelf_target_color = SK_ColorTRANSPARENT;
-      item_target_color = gfx::kGoogleGrey100;
       break;
     case SHELF_BACKGROUND_LOGIN:
       shelf_target_color = SK_ColorTRANSPARENT;
-      item_target_color = SK_ColorTRANSPARENT;
       break;
     case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
       shelf_target_color = login_constants::kDefaultBaseColor;
-      item_target_color = login_constants::kDefaultBaseColor;
       break;
   }
 
-  std::pair<int, int> target_color_alpha_values =
-      GetTargetColorAlphaValues(background_type);
-  shelf_background_values->SetTargetValues(
-      SkColorSetA(shelf_target_color, target_color_alpha_values.first));
-  item_background_values->SetTargetValues(
-      SkColorSetA(item_target_color, target_color_alpha_values.second));
+  shelf_background_values->SetTargetValues(SkColorSetA(
+      shelf_target_color, GetBackgroundAlphaValue(background_type)));
 }
 
 void ShelfBackgroundAnimator::SetAnimationValues(double t) {
   DCHECK_GE(t, 0.0);
   DCHECK_LE(t, 1.0);
   shelf_background_values_.UpdateCurrentValues(t);
-  item_background_values_.UpdateCurrentValues(t);
   NotifyObservers();
 }
 
diff --git a/ash/shelf/shelf_background_animator.h b/ash/shelf/shelf_background_animator.h
index 478be51..022fbeb 100644
--- a/ash/shelf/shelf_background_animator.h
+++ b/ash/shelf/shelf_background_animator.h
@@ -146,8 +146,7 @@
   // Sets the target values for |shelf_background_values| and
   // |item_background_values| according to |background_type|.
   void GetTargetValues(ShelfBackgroundType background_type,
-                       AnimationValues* shelf_background_values,
-                       AnimationValues* item_background_values) const;
+                       AnimationValues* shelf_background_values) const;
 
   // Updates the animation values corresponding to the |t| value between 0 and
   // 1.
diff --git a/ash/shelf/shelf_background_animator_observer.h b/ash/shelf/shelf_background_animator_observer.h
index 6c45073..d2ee5fb 100644
--- a/ash/shelf/shelf_background_animator_observer.h
+++ b/ash/shelf/shelf_background_animator_observer.h
@@ -16,9 +16,6 @@
   // Called when the Shelf's background should be updated.
   virtual void UpdateShelfBackground(SkColor color) {}
 
-  // Called when the Shelf item (aka button) backgrounds should be updated.
-  virtual void UpdateShelfItemBackground(SkColor color) {}
-
  protected:
   virtual ~ShelfBackgroundAnimatorObserver() {}
 };
diff --git a/ash/shelf/shelf_background_animator_unittest.cc b/ash/shelf/shelf_background_animator_unittest.cc
index 08b0743..42dc95a 100644
--- a/ash/shelf/shelf_background_animator_unittest.cc
+++ b/ash/shelf/shelf_background_animator_unittest.cc
@@ -49,18 +49,11 @@
   // Convenience function to get the alpha value from |background_color_|.
   int GetBackgroundAlpha() const;
 
-  SkColor item_background_color() const { return item_background_color_; }
-
-  // Convenience function to get the alpha value from |item_background_color_|.
-  int GetItemBackgroundAlpha() const;
-
   // ShelfBackgroundObserver:
   void UpdateShelfBackground(SkColor color) override;
-  void UpdateShelfItemBackground(SkColor color) override;
 
  private:
   int background_color_ = SK_ColorTRANSPARENT;
-  int item_background_color_ = SK_ColorTRANSPARENT;
 
   DISALLOW_COPY_AND_ASSIGN(TestShelfBackgroundObserver);
 };
@@ -69,18 +62,10 @@
   return SkColorGetA(background_color_);
 }
 
-int TestShelfBackgroundObserver::GetItemBackgroundAlpha() const {
-  return SkColorGetA(item_background_color_);
-}
-
 void TestShelfBackgroundObserver::UpdateShelfBackground(SkColor color) {
   background_color_ = color;
 }
 
-void TestShelfBackgroundObserver::UpdateShelfItemBackground(SkColor color) {
-  item_background_color_ = color;
-}
-
 }  // namespace
 
 // Provides internal access to a ShelfBackgroundAnimator instance.
@@ -101,10 +86,6 @@
     return animator_->shelf_background_values_.target_color();
   }
 
-  SkColor item_background_target_color() const {
-    return animator_->item_background_values_.target_color();
-  }
-
  private:
   // The instance to provide internal access to.
   ShelfBackgroundAnimator* animator_;
@@ -168,7 +149,6 @@
 
 void ShelfBackgroundAnimatorTest::SetColorValuesOnObserver(SkColor color) {
   observer_.UpdateShelfBackground(color);
-  observer_.UpdateShelfItemBackground(color);
 }
 
 void ShelfBackgroundAnimatorTest::CompleteAnimations() {
@@ -201,7 +181,6 @@
   CompleteAnimations();
 
   EXPECT_NE(observer_.background_color(), kDummyColor);
-  EXPECT_NE(observer_.item_background_color(), kDummyColor);
 
   SetColorValuesOnObserver(kDummyColor);
   animator_->PaintBackground(SHELF_BACKGROUND_DEFAULT,
@@ -209,7 +188,6 @@
   CompleteAnimations();
 
   EXPECT_EQ(observer_.background_color(), kDummyColor);
-  EXPECT_EQ(observer_.item_background_color(), kDummyColor);
 }
 
 // Verify observers are updated with the current values when they are added.
@@ -220,7 +198,6 @@
   animator_->AddObserver(&observer_);
 
   EXPECT_NE(observer_.background_color(), kDummyColor);
-  EXPECT_NE(observer_.item_background_color(), kDummyColor);
 }
 
 // Verify the alpha values for the SHELF_BACKGROUND_DEFAULT state.
@@ -229,7 +206,6 @@
 
   EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, animator_->target_background_type());
   EXPECT_EQ(kShelfTranslucentAlpha, observer_.GetBackgroundAlpha());
-  EXPECT_EQ(0, observer_.GetItemBackgroundAlpha());
 }
 
 // Verify the alpha values for the SHELF_BACKGROUND_MAXIMIZED state.
@@ -238,7 +214,6 @@
 
   EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, animator_->target_background_type());
   EXPECT_EQ(kShelfTranslucentMaximizedWindow, observer_.GetBackgroundAlpha());
-  EXPECT_EQ(0, observer_.GetItemBackgroundAlpha());
 }
 
 // Verify the alpha values for the SHELF_BACKGROUND_SPLIT_VIEW state.
@@ -247,7 +222,6 @@
 
   EXPECT_EQ(SHELF_BACKGROUND_SPLIT_VIEW, animator_->target_background_type());
   EXPECT_EQ(kMaxAlpha, observer_.GetBackgroundAlpha());
-  EXPECT_EQ(0, observer_.GetItemBackgroundAlpha());
 }
 
 // Verify the alpha values for the SHELF_BACKGROUND_APP_LIST state.
@@ -256,7 +230,6 @@
 
   EXPECT_EQ(SHELF_BACKGROUND_APP_LIST, animator_->target_background_type());
   EXPECT_EQ(kShelfTranslucentOverAppList, observer_.GetBackgroundAlpha());
-  EXPECT_EQ(0, observer_.GetItemBackgroundAlpha());
 }
 
 TEST_F(ShelfBackgroundAnimatorTest,
@@ -313,7 +286,6 @@
   PaintBackground(SHELF_BACKGROUND_DEFAULT);
 
   EXPECT_NE(observer_.background_color(), kDummyColor);
-  EXPECT_NE(observer_.item_background_color(), kDummyColor);
 }
 
 class ShelfBackgroundTargetColorTest : public NoSessionAshTestBase {
@@ -356,8 +328,6 @@
   NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(SK_ColorTRANSPARENT));
 
   SimulateUserLogin("user1@test.com");
 
@@ -365,43 +335,31 @@
       session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(SK_ColorTRANSPARENT));
 
   // The shelf has a non-transparent background only when session state is
   // active.
   NotifySessionStateChanged(session_manager::SessionState::ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(kShelfDefaultBaseColor));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(kShelfDefaultBaseColor));
 
   NotifySessionStateChanged(session_manager::SessionState::LOCKED);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(SK_ColorTRANSPARENT));
 
   // Ensure the shelf background color is correct after unlocking.
   NotifySessionStateChanged(session_manager::SessionState::ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(kShelfDefaultBaseColor));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(kShelfDefaultBaseColor));
 
   NotifySessionStateChanged(session_manager::SessionState::LOGIN_SECONDARY);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(SK_ColorTRANSPARENT));
 
   // Ensure the shelf background color is correct after closing the user adding
   // screen.
   NotifySessionStateChanged(session_manager::SessionState::ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(kShelfDefaultBaseColor));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(kShelfDefaultBaseColor));
 }
 
 // Verify the target colors of the shelf and item backgrounds are updated based
@@ -420,8 +378,6 @@
   NotifySessionStateChanged(session_manager::SessionState::OOBE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(gfx::kGoogleGrey100));
 
   SimulateUserLogin("user1@test.com");
 
@@ -429,14 +385,10 @@
       session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(SK_ColorTRANSPARENT));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(SK_ColorTRANSPARENT));
 
   NotifySessionStateChanged(session_manager::SessionState::ACTIVE);
   EXPECT_EQ(GetBaseColor(test_api.shelf_background_target_color()),
             GetBaseColor(kShelfDefaultBaseColor));
-  EXPECT_EQ(GetBaseColor(test_api.item_background_target_color()),
-            GetBaseColor(kShelfDefaultBaseColor));
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index 9a400377..5d49eeb 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/menu_utils.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_prefs.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -150,8 +151,9 @@
       menu_items_(std::move(menu_items)),
       delegate_(delegate),
       display_id_(display_id) {
-  // Append shelf settings and wallpaper items if no shelf item was selected.
-  if (!delegate)
+  // Append shelf settings and wallpaper items to the menu if ShelfView or
+  // AppListButton are selected.
+  if (!delegate || delegate_->app_id() == kAppListId)
     AddLocalMenuItems(&menu_items_, display_id);
   menu_utils::PopulateMenuFromMojoMenuItems(this, this, menu_items_,
                                             &submenus_);
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 0710571..2184d73 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -8,6 +8,7 @@
 #include "ash/shelf/shelf_tooltip_bubble.h"
 #include "ash/shelf/shelf_tooltip_preview_bubble.h"
 #include "ash/shelf/shelf_view.h"
+#include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/strings/string16.h"
@@ -89,12 +90,14 @@
   const std::vector<aura::Window*> open_windows =
       shelf_view_->GetOpenWindowsForShelfView(view);
 
-  const base::string16 text = shelf_view_->GetTitleForView(view);
   if (chromeos::switches::ShouldShowShelfHoverPreviews() &&
       open_windows.size() > 0) {
-    bubble_ = new ShelfTooltipPreviewBubble(view, arrow, open_windows, this);
+    bubble_ = new ShelfTooltipPreviewBubble(
+        view, arrow, open_windows, this,
+        shelf_view_->shelf_widget()->GetShelfBackgroundColor());
   } else {
-    bubble_ = new ShelfTooltipBubble(view, arrow, text);
+    bubble_ =
+        new ShelfTooltipBubble(view, arrow, shelf_view_->GetTitleForView(view));
   }
 
   aura::Window* window = bubble_->GetWidget()->GetNativeWindow();
diff --git a/ash/shelf/shelf_tooltip_preview_bubble.cc b/ash/shelf/shelf_tooltip_preview_bubble.cc
index 8aa0333..8f0efee5 100644
--- a/ash/shelf/shelf_tooltip_preview_bubble.cc
+++ b/ash/shelf/shelf_tooltip_preview_bubble.cc
@@ -4,7 +4,9 @@
 
 #include "ash/shelf/shelf_tooltip_preview_bubble.h"
 
+#include "ash/shelf/shelf_widget.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/views/bubble/bubble_frame_view.h"
 
 namespace ash {
 
@@ -15,13 +17,17 @@
 constexpr int kTooltipPaddingLeftRight = 16;
 
 // The padding between individual previews.
-constexpr int kPreviewPadding = 22;
+constexpr int kPreviewPadding = 10;
+
+// The border radius of the whole bubble
+constexpr int kPreviewBorderRadius = 16;
 
 ShelfTooltipPreviewBubble::ShelfTooltipPreviewBubble(
     views::View* anchor,
     views::BubbleBorder::Arrow arrow,
     const std::vector<aura::Window*>& windows,
-    ShelfTooltipManager* manager)
+    ShelfTooltipManager* manager,
+    SkColor background_color)
     : ShelfTooltipBubbleBase(anchor, arrow), manager_(manager) {
   const ui::NativeTheme* theme = anchor_widget()->GetNativeTheme();
   for (auto* window : windows) {
@@ -33,6 +39,10 @@
   set_margins(gfx::Insets(kTooltipPaddingTop, kTooltipPaddingLeftRight,
                           kTooltipPaddingBottom, kTooltipPaddingLeftRight));
   views::BubbleDialogDelegateView::CreateBubble(this);
+
+  // This can only be set after bubble creation:
+  GetBubbleFrameView()->bubble_border()->SetCornerRadius(kPreviewBorderRadius);
+  GetBubbleFrameView()->bubble_border()->set_background_color(background_color);
 }
 
 ShelfTooltipPreviewBubble::~ShelfTooltipPreviewBubble() = default;
diff --git a/ash/shelf/shelf_tooltip_preview_bubble.h b/ash/shelf/shelf_tooltip_preview_bubble.h
index 214d101..f4dc3c4a 100644
--- a/ash/shelf/shelf_tooltip_preview_bubble.h
+++ b/ash/shelf/shelf_tooltip_preview_bubble.h
@@ -24,7 +24,8 @@
   ShelfTooltipPreviewBubble(views::View* anchor,
                             views::BubbleBorder::Arrow arrow,
                             const std::vector<aura::Window*>& windows,
-                            ShelfTooltipManager* manager);
+                            ShelfTooltipManager* manager,
+                            SkColor background_color);
   ~ShelfTooltipPreviewBubble() override;
 
  private:
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index c3d7f01..a743aaa 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -331,7 +331,6 @@
       bounds_animator_(std::make_unique<views::BoundsAnimator>(this)),
       tooltip_(this),
       focus_search_(std::make_unique<ShelfFocusSearch>(this)),
-      shelf_item_background_color_(kShelfDefaultBaseColor),
       weak_factory_(this) {
   DCHECK(model_);
   DCHECK(shelf_);
@@ -967,11 +966,6 @@
   return size;
 }
 
-void ShelfView::UpdateShelfItemBackground(SkColor color) {
-  shelf_item_background_color_ = color;
-  SchedulePaint();
-}
-
 void ShelfView::OnTabletModeChanged() {
   OnBoundsChanged(GetBoundsInScreen());
 }
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 0e97d799..0ace96f 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -243,9 +243,6 @@
   // or -1 if no separator is required.
   int GetSeparatorIndex() const;
 
-  // Updates the background for the shelf items.
-  void UpdateShelfItemBackground(SkColor color);
-
   // Update the layout when entering or exiting tablet mode. Have the owning
   // widget call this instead of observing changes ourselves to ensure this
   // happens after the tablet related changes in ShelfController.
@@ -604,10 +601,6 @@
   // Tracks UMA metrics based on shelf button press actions.
   ShelfButtonPressedMetricTracker shelf_button_pressed_metric_tracker_;
 
-  // Color used to paint the background behind the app list button and back
-  // button.
-  SkColor shelf_item_background_color_;
-
   // A reference to the view used as a separator between pinned and unpinned
   // items.
   views::Separator* separator_ = nullptr;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 321a0a1..d8e718f 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -2206,14 +2206,15 @@
   EXPECT_EQ(first_app_id, model_->items()[last_index].id);
 }
 
-// Tests that the app list button does not show a context menu on right click.
-TEST_F(ShelfViewTest, AppListButtonDoesNotShowContextMenu) {
+// Tests that the app list button does shows a context menu on right click.
+TEST_F(ShelfViewTest, AppListButtonDoesShowContextMenu) {
   ui::test::EventGenerator* generator = GetEventGenerator();
   const AppListButton* app_list_button = shelf_view_->GetAppListButton();
   generator->MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
   generator->PressRightButton();
-  EXPECT_FALSE(test_api_->CloseMenu());
+  EXPECT_TRUE(test_api_->CloseMenu());
 }
+
 // Test class that tests both context and application menus.
 class ShelfViewMenuTest : public ShelfViewTest,
                           public testing::WithParamInterface<bool> {
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 45c9819..36e0268 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -104,6 +104,8 @@
   // ShelfBackgroundAnimatorObserver:
   void UpdateShelfBackground(SkColor color) override;
 
+  SkColor GetShelfBackgroundColor() const;
+
  private:
   ShelfWidget* shelf_widget_;
   FocusCycler* focus_cycler_;
@@ -276,6 +278,10 @@
   UpdateOpaqueBackground();
 }
 
+SkColor ShelfWidget::DelegateView::GetShelfBackgroundColor() const {
+  return opaque_background_.background_color();
+}
+
 ShelfWidget::ShelfWidget(aura::Window* shelf_container, Shelf* shelf)
     : shelf_(shelf),
       background_animator_(SHELF_BACKGROUND_DEFAULT,
@@ -320,8 +326,6 @@
 
   views::Widget::AddObserver(this);
 
-  // Calls back into |this| and depends on |shelf_view_|.
-  background_animator_.AddObserver(this);
   background_animator_.AddObserver(delegate_view_);
   shelf_->AddObserver(this);
 }
@@ -349,7 +353,6 @@
 
   // Don't need to update the shelf background during shutdown.
   background_animator_.RemoveObserver(delegate_view_);
-  background_animator_.RemoveObserver(this);
   shelf_->RemoveObserver(this);
 
   // Don't need to observe focus/activation during shutdown.
@@ -399,9 +402,6 @@
 void ShelfWidget::PostCreateShelf() {
   SetFocusCycler(Shell::Get()->focus_cycler());
 
-  // Ensure the newly created |shelf_| gets current values.
-  background_animator_.NotifyObserver(this);
-
   shelf_layout_manager_->LayoutShelf();
   shelf_layout_manager_->UpdateAutoHideState();
   ShowIfHidden();
@@ -479,10 +479,6 @@
   }
 }
 
-void ShelfWidget::UpdateShelfItemBackground(SkColor color) {
-  shelf_view_->UpdateShelfItemBackground(color);
-}
-
 void ShelfWidget::WillDeleteShelfLayoutManager() {
   shelf_layout_manager_->RemoveObserver(this);
   shelf_layout_manager_ = nullptr;
@@ -532,6 +528,10 @@
   login_shelf_view_->UpdateAfterSessionStateChange(state);
 }
 
+SkColor ShelfWidget::GetShelfBackgroundColor() const {
+  return delegate_view_->GetShelfBackgroundColor();
+}
+
 void ShelfWidget::HideIfShown() {
   if (IsVisible())
     Hide();
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index d9310e9..1819182 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -11,7 +11,6 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/session/session_observer.h"
 #include "ash/shelf/shelf_background_animator.h"
-#include "ash/shelf/shelf_background_animator_observer.h"
 #include "ash/shelf/shelf_layout_manager_observer.h"
 #include "ash/shelf/shelf_observer.h"
 #include "base/macros.h"
@@ -38,7 +37,6 @@
 // early during RootWindowController initialization.
 class ASH_EXPORT ShelfWidget : public views::Widget,
                                public views::WidgetObserver,
-                               public ShelfBackgroundAnimatorObserver,
                                public ShelfLayoutManagerObserver,
                                public ShelfObserver,
                                public SessionObserver {
@@ -108,9 +106,6 @@
   // Overridden from views::WidgetObserver:
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
 
-  // ShelfBackgroundAnimatorObserver overrides:
-  void UpdateShelfItemBackground(SkColor color) override;
-
   // ShelfLayoutManagerObserver overrides:
   void WillDeleteShelfLayoutManager() override;
 
@@ -121,6 +116,8 @@
   // SessionObserver overrides:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
+  SkColor GetShelfBackgroundColor() const;
+
   // Internal implementation detail. Do not expose outside of tests.
   ShelfView* shelf_view_for_testing() const { return shelf_view_; }
   ShelfBackgroundAnimator* background_animator_for_testing() {
diff --git a/ash/shell.cc b/ash/shell.cc
index a7a8562..c7f9e7e 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1220,7 +1220,7 @@
   // since AshTouchTransformController listens on
   // WindowTreeHostManager::Observer::OnDisplaysInitialized().
   touch_transformer_controller_ = std::make_unique<AshTouchTransformController>(
-      display_configurator_.get(), display_manager_.get(),
+      display_manager_.get(),
       std::make_unique<display::DefaultTouchTransformSetter>());
 
   // |system_tray_model_| should be available before
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index f43552a..838caeb1 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -16,7 +16,7 @@
 
 ShellDelegateImpl::~ShellDelegateImpl() = default;
 
-bool ShellDelegateImpl::CanShowWindowForUser(aura::Window* window) const {
+bool ShellDelegateImpl::CanShowWindowForUser(const aura::Window* window) const {
   return true;
 }
 
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 105c7c0..ebeecb1 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -20,7 +20,7 @@
   ~ShellDelegateImpl() override;
 
   // ShellDelegate:
-  bool CanShowWindowForUser(aura::Window* window) const override;
+  bool CanShowWindowForUser(const aura::Window* window) const override;
   std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   ws::InputDeviceControllerClient* GetInputDeviceControllerClient() override;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 2ad1230..27618b5 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -33,7 +33,7 @@
 
   // Returns true if |window| can be shown for the delegate's concept of current
   // user.
-  virtual bool CanShowWindowForUser(aura::Window* window) const = 0;
+  virtual bool CanShowWindowForUser(const aura::Window* window) const = 0;
 
   // TODO(jamescook): Replace with a mojo-compatible interface.
   virtual std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() = 0;
diff --git a/ash/shell_delegate_mash.cc b/ash/shell_delegate_mash.cc
index 3d51a39..9c4da52 100644
--- a/ash/shell_delegate_mash.cc
+++ b/ash/shell_delegate_mash.cc
@@ -47,7 +47,7 @@
 
 ShellDelegateMash::~ShellDelegateMash() = default;
 
-bool ShellDelegateMash::CanShowWindowForUser(aura::Window* window) const {
+bool ShellDelegateMash::CanShowWindowForUser(const aura::Window* window) const {
   NOTIMPLEMENTED_LOG_ONCE();
   return true;
 }
diff --git a/ash/shell_delegate_mash.h b/ash/shell_delegate_mash.h
index 5dbc6df..91b3141 100644
--- a/ash/shell_delegate_mash.h
+++ b/ash/shell_delegate_mash.h
@@ -18,7 +18,7 @@
   ~ShellDelegateMash() override;
 
   // ShellDelegate:
-  bool CanShowWindowForUser(aura::Window* window) const override;
+  bool CanShowWindowForUser(const aura::Window* window) const override;
   std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   ws::InputDeviceControllerClient* GetInputDeviceControllerClient() override;
diff --git a/ash/sticky_keys/sticky_keys_overlay.cc b/ash/sticky_keys/sticky_keys_overlay.cc
index 294af54..6bec5d7c 100644
--- a/ash/sticky_keys/sticky_keys_overlay.cc
+++ b/ash/sticky_keys/sticky_keys_overlay.cc
@@ -212,7 +212,6 @@
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.accept_events = false;
   params.keep_on_top = true;
-  params.remove_standard_frame = true;
   params.bounds = CalculateOverlayBounds();
   params.parent = Shell::GetContainer(Shell::GetRootWindowForNewWindows(),
                                       kShellWindowId_OverlayContainer);
diff --git a/ash/system/accessibility/tray_accessibility_unittest.cc b/ash/system/accessibility/tray_accessibility_unittest.cc
index f872522d..d4decd7 100644
--- a/ash/system/accessibility/tray_accessibility_unittest.cc
+++ b/ash/system/accessibility/tray_accessibility_unittest.cc
@@ -9,8 +9,8 @@
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 #include "ash/system/tray/hover_highlight_view.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "components/prefs/pref_service.h"
@@ -71,7 +71,7 @@
   ~TrayAccessibilityTest() override = default;
 
   void CreateDetailedMenu() {
-    delegate_ = std::make_unique<UnifiedDetailedViewDelegate>(nullptr);
+    delegate_ = std::make_unique<DetailedViewDelegate>(nullptr);
     detailed_menu_ =
         std::make_unique<tray::AccessibilityDetailedView>(delegate_.get());
   }
diff --git a/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc b/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
index 389a476..a6016438 100644
--- a/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
+++ b/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
@@ -7,7 +7,7 @@
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/system/accessibility/tray_accessibility.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 namespace ash {
 
@@ -15,7 +15,7 @@
     UnifiedAccessibilityDetailedViewController(
         UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {
   Shell::Get()->accessibility_controller()->AddObserver(this);
 }
 
diff --git a/ash/system/audio/unified_audio_detailed_view_controller.cc b/ash/system/audio/unified_audio_detailed_view_controller.cc
index 9235b0a..a9f2931 100644
--- a/ash/system/audio/unified_audio_detailed_view_controller.cc
+++ b/ash/system/audio/unified_audio_detailed_view_controller.cc
@@ -5,7 +5,7 @@
 #include "ash/system/audio/unified_audio_detailed_view_controller.h"
 
 #include "ash/system/audio/audio_detailed_view.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 using chromeos::CrasAudioHandler;
 
@@ -14,7 +14,7 @@
 UnifiedAudioDetailedViewController::UnifiedAudioDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {
   DCHECK(CrasAudioHandler::IsInitialized());
   CrasAudioHandler::Get()->AddAudioObserver(this);
 }
diff --git a/ash/system/bluetooth/unified_bluetooth_detailed_view_controller.cc b/ash/system/bluetooth/unified_bluetooth_detailed_view_controller.cc
index b29c571..a92c17fc 100644
--- a/ash/system/bluetooth/unified_bluetooth_detailed_view_controller.cc
+++ b/ash/system/bluetooth/unified_bluetooth_detailed_view_controller.cc
@@ -12,8 +12,8 @@
 #include "ash/shell.h"
 #include "ash/system/bluetooth/bluetooth_detailed_view.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
 #include "base/stl_util.h"
 
 using device::mojom::BluetoothSystem;
@@ -55,7 +55,7 @@
 UnifiedBluetoothDetailedViewController::UnifiedBluetoothDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {
   Shell::Get()->tray_bluetooth_helper()->AddObserver(this);
 }
 
diff --git a/ash/system/cast/unified_cast_detailed_view_controller.cc b/ash/system/cast/unified_cast_detailed_view_controller.cc
index 6192ffd..30737d7 100644
--- a/ash/system/cast/unified_cast_detailed_view_controller.cc
+++ b/ash/system/cast/unified_cast_detailed_view_controller.cc
@@ -6,14 +6,14 @@
 
 #include "ash/shell.h"
 #include "ash/system/cast/tray_cast.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 namespace ash {
 
 UnifiedCastDetailedViewController::UnifiedCastDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {}
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {}
 
 UnifiedCastDetailedViewController::~UnifiedCastDetailedViewController() =
     default;
diff --git a/ash/system/ime/unified_ime_detailed_view_controller.cc b/ash/system/ime/unified_ime_detailed_view_controller.cc
index ced2af4e..4095e49 100644
--- a/ash/system/ime/unified_ime_detailed_view_controller.cc
+++ b/ash/system/ime/unified_ime_detailed_view_controller.cc
@@ -8,7 +8,7 @@
 #include "ash/ime/ime_controller.h"
 #include "ash/shell.h"
 #include "ash/system/ime/tray_ime_chromeos.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 namespace ash {
 
@@ -25,7 +25,7 @@
 UnifiedIMEDetailedViewController::UnifiedIMEDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {}
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {}
 
 UnifiedIMEDetailedViewController::~UnifiedIMEDetailedViewController() {}
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 9d08644..7a5e135 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -272,55 +272,44 @@
 
 // A list of available IMEs shown in the opt-in IME menu, which has a different
 // height depending on the number of IMEs in the list.
-class ImeMenuListView : public ImeListView, public DetailedViewDelegate {
+class ImeMenuListView : public ImeListView {
  public:
-  ImeMenuListView() : ImeListView(this, false /* use_unified_theme */) {
-    set_should_focus_ime_after_selection_with_keyboard(true);
-  }
-
+  ImeMenuListView() : ImeMenuListView(std::make_unique<Delegate>()) {}
   ~ImeMenuListView() override = default;
 
-  // DetailedViewDelegate:
-  void TransitionToMainView(bool restore_focus) override {}
-  void CloseBubble() override {}
-  SkColor GetBackgroundColor(ui::NativeTheme* native_theme) override {
-    return native_theme->GetSystemColor(
-        ui::NativeTheme::kColorId_BubbleBackground);
-  }
-  bool IsOverflowIndicatorEnabled() const override { return true; }
-  TriView* CreateTitleRow(int string_id) override { return nullptr; }
-  views::View* CreateTitleSeparator() override { return nullptr; }
-  void ShowStickyHeaderSeparator(views::View* view,
-                                 bool show_separator) override {}
-  views::Separator* CreateListSubHeaderSeparator() override { return nullptr; }
-  HoverHighlightView* CreateScrollListItem(
-      ViewClickListener* listener,
-      const gfx::VectorIcon& icon,
-      const base::string16& text) override {
-    return nullptr;
-  }
-  views::Button* CreateBackButton(views::ButtonListener* listener) override {
-    return nullptr;
-  }
-  views::Button* CreateInfoButton(views::ButtonListener* listener,
-                                  int info_accessible_name_id) override {
-    return nullptr;
-  }
-  views::Button* CreateSettingsButton(views::ButtonListener* listener,
-                                      int setting_accessible_name_id) override {
-    return nullptr;
-  }
-  views::Button* CreateHelpButton(views::ButtonListener* listener) override {
-    return nullptr;
+ private:
+  class Delegate : public DetailedViewDelegate {
+   public:
+    Delegate() : DetailedViewDelegate(nullptr /* tray_controller */) {}
+
+    // DetailedViewDelegate:
+    void TransitionToMainView(bool restore_focus) override {}
+    void CloseBubble() override {}
+    SkColor GetBackgroundColor(ui::NativeTheme* native_theme) override {
+      return native_theme->GetSystemColor(
+          ui::NativeTheme::kColorId_BubbleBackground);
+    }
+    bool IsOverflowIndicatorEnabled() const override { return true; }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  ImeMenuListView(std::unique_ptr<Delegate> delegate)
+      : ImeListView(delegate.get(), false /* use_unified_theme */) {
+    set_should_focus_ime_after_selection_with_keyboard(true);
+    delegate_ = std::move(delegate);
   }
 
- protected:
+  // ImeListView:
   void Layout() override {
     gfx::Range height_range = GetImeListViewRange();
     scroller()->ClipHeightTo(height_range.start(), height_range.end());
     ImeListView::Layout();
   }
 
+  std::unique_ptr<Delegate> delegate_;
+
   DISALLOW_COPY_AND_ASSIGN(ImeMenuListView);
 };
 
diff --git a/ash/system/locale/unified_locale_detailed_view_controller.cc b/ash/system/locale/unified_locale_detailed_view_controller.cc
index c668a0d9..84a82ea 100644
--- a/ash/system/locale/unified_locale_detailed_view_controller.cc
+++ b/ash/system/locale/unified_locale_detailed_view_controller.cc
@@ -5,7 +5,7 @@
 #include "ash/system/locale/unified_locale_detailed_view_controller.h"
 
 #include "ash/system/locale/locale_detailed_view.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 #include "base/logging.h"
 
 namespace ash {
@@ -13,7 +13,7 @@
 UnifiedLocaleDetailedViewController::UnifiedLocaleDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {}
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {}
 
 UnifiedLocaleDetailedViewController::~UnifiedLocaleDetailedViewController() =
     default;
diff --git a/ash/system/network/unified_network_detailed_view_controller.cc b/ash/system/network/unified_network_detailed_view_controller.cc
index d3882d0e..6b4190de 100644
--- a/ash/system/network/unified_network_detailed_view_controller.cc
+++ b/ash/system/network/unified_network_detailed_view_controller.cc
@@ -7,14 +7,14 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/network/network_list.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 namespace ash {
 
 UnifiedNetworkDetailedViewController::UnifiedNetworkDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)),
+          std::make_unique<DetailedViewDelegate>(tray_controller)),
       network_state_observer_(
           std::make_unique<TrayNetworkStateObserver>(this)) {}
 
diff --git a/ash/system/network/unified_vpn_detailed_view_controller.cc b/ash/system/network/unified_vpn_detailed_view_controller.cc
index c38f611..5f0e499 100644
--- a/ash/system/network/unified_vpn_detailed_view_controller.cc
+++ b/ash/system/network/unified_vpn_detailed_view_controller.cc
@@ -7,14 +7,14 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/network/vpn_list_view.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 namespace ash {
 
 UnifiedVPNDetailedViewController::UnifiedVPNDetailedViewController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)),
+          std::make_unique<DetailedViewDelegate>(tray_controller)),
       network_state_observer_(
           std::make_unique<TrayNetworkStateObserver>(this)) {}
 
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index e25cb6d2f..a630b1a 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -213,17 +213,6 @@
   return true;
 }
 
-void StatusAreaWidget::UpdateShelfItemBackground(SkColor color) {
-  unified_system_tray_->UpdateShelfItemBackground(color);
-  virtual_keyboard_tray_->UpdateShelfItemBackground(color);
-  ime_menu_tray_->UpdateShelfItemBackground(color);
-  select_to_speak_tray_->UpdateShelfItemBackground(color);
-  if (dictation_button_tray_)
-    dictation_button_tray_->UpdateShelfItemBackground(color);
-  palette_tray_->UpdateShelfItemBackground(color);
-  overview_button_tray_->UpdateShelfItemBackground(color);
-}
-
 void StatusAreaWidget::OnMouseEvent(ui::MouseEvent* event) {
   // Clicking anywhere except the virtual keyboard tray icon should hide the
   // virtual keyboard.
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 5cecd3e..736122b 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -102,9 +102,6 @@
   const ui::NativeTheme* GetNativeTheme() const override;
   bool OnNativeWidgetActivationChanged(bool active) override;
 
-  // ShelfBackgroundAnimatorObserver:
-  void UpdateShelfItemBackground(SkColor color) override;
-
   // TODO(jamescook): Introduce a test API instead of these methods.
   LogoutButtonTray* logout_button_tray_for_testing() {
     return logout_button_tray_.get();
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
index c8632df..0614815 100644
--- a/ash/system/toast/toast_overlay.cc
+++ b/ash/system/toast/toast_overlay.cc
@@ -231,7 +231,6 @@
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.accept_events = true;
   params.keep_on_top = true;
-  params.remove_standard_frame = true;
   params.bounds = CalculateOverlayBounds();
   // Show toasts above the app list and below the lock screen.
   params.parent = Shell::GetRootWindowForNewWindows()->GetChildById(
diff --git a/ash/system/unified/unified_detailed_view_delegate.cc b/ash/system/tray/detailed_view_delegate.cc
similarity index 85%
rename from ash/system/unified/unified_detailed_view_delegate.cc
rename to ash/system/tray/detailed_view_delegate.cc
index 98e6cc3d..9cc4ed8a 100644
--- a/ash/system/unified/unified_detailed_view_delegate.cc
+++ b/ash/system/tray/detailed_view_delegate.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 "ash/system/unified/unified_detailed_view_delegate.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -93,30 +93,30 @@
 
 }  // namespace
 
-UnifiedDetailedViewDelegate::UnifiedDetailedViewDelegate(
+DetailedViewDelegate::DetailedViewDelegate(
     UnifiedSystemTrayController* tray_controller)
     : tray_controller_(tray_controller) {}
 
-UnifiedDetailedViewDelegate::~UnifiedDetailedViewDelegate() = default;
+DetailedViewDelegate::~DetailedViewDelegate() = default;
 
-void UnifiedDetailedViewDelegate::TransitionToMainView(bool restore_focus) {
+void DetailedViewDelegate::TransitionToMainView(bool restore_focus) {
   tray_controller_->TransitionToMainView(restore_focus);
 }
 
-void UnifiedDetailedViewDelegate::CloseBubble() {
+void DetailedViewDelegate::CloseBubble() {
   tray_controller_->CloseBubble();
 }
 
-SkColor UnifiedDetailedViewDelegate::GetBackgroundColor(
+SkColor DetailedViewDelegate::GetBackgroundColor(
     ui::NativeTheme* native_theme) {
   return SK_ColorTRANSPARENT;
 }
 
-bool UnifiedDetailedViewDelegate::IsOverflowIndicatorEnabled() const {
+bool DetailedViewDelegate::IsOverflowIndicatorEnabled() const {
   return false;
 }
 
-TriView* UnifiedDetailedViewDelegate::CreateTitleRow(int string_id) {
+TriView* DetailedViewDelegate::CreateTitleRow(int string_id) {
   auto* tri_view = new TriView(kUnifiedTopShortcutSpacing);
 
   ConfigureTitleTriView(tri_view, TriView::Container::START);
@@ -137,7 +137,7 @@
   return tri_view;
 }
 
-views::View* UnifiedDetailedViewDelegate::CreateTitleSeparator() {
+views::View* DetailedViewDelegate::CreateTitleSeparator() {
   views::Separator* separator = new views::Separator();
   separator->SetColor(kUnifiedMenuSeparatorColor);
   separator->SetBorder(views::CreateEmptyBorder(
@@ -145,9 +145,8 @@
   return separator;
 }
 
-void UnifiedDetailedViewDelegate::ShowStickyHeaderSeparator(
-    views::View* view,
-    bool show_separator) {
+void DetailedViewDelegate::ShowStickyHeaderSeparator(views::View* view,
+                                                     bool show_separator) {
   if (show_separator) {
     const int separator_width = ash::TrayConstants::separator_width();
     view->SetBorder(views::CreatePaddedBorder(
@@ -162,7 +161,7 @@
   view->SchedulePaint();
 }
 
-views::Separator* UnifiedDetailedViewDelegate::CreateListSubHeaderSeparator() {
+views::Separator* DetailedViewDelegate::CreateListSubHeaderSeparator() {
   views::Separator* separator = new views::Separator();
   separator->SetColor(kUnifiedMenuSeparatorColor);
   separator->SetBorder(views::CreateEmptyBorder(
@@ -170,7 +169,7 @@
   return separator;
 }
 
-HoverHighlightView* UnifiedDetailedViewDelegate::CreateScrollListItem(
+HoverHighlightView* DetailedViewDelegate::CreateScrollListItem(
     ViewClickListener* listener,
     const gfx::VectorIcon& icon,
     const base::string16& text) {
@@ -184,19 +183,19 @@
   return item;
 }
 
-views::Button* UnifiedDetailedViewDelegate::CreateBackButton(
+views::Button* DetailedViewDelegate::CreateBackButton(
     views::ButtonListener* listener) {
   return new BackButton(listener);
 }
 
-views::Button* UnifiedDetailedViewDelegate::CreateInfoButton(
+views::Button* DetailedViewDelegate::CreateInfoButton(
     views::ButtonListener* listener,
     int info_accessible_name_id) {
   return new TopShortcutButton(listener, kUnifiedMenuInfoIcon,
                                info_accessible_name_id);
 }
 
-views::Button* UnifiedDetailedViewDelegate::CreateSettingsButton(
+views::Button* DetailedViewDelegate::CreateSettingsButton(
     views::ButtonListener* listener,
     int setting_accessible_name_id) {
   auto* button = new TopShortcutButton(listener, kUnifiedMenuSettingsIcon,
@@ -206,7 +205,7 @@
   return button;
 }
 
-views::Button* UnifiedDetailedViewDelegate::CreateHelpButton(
+views::Button* DetailedViewDelegate::CreateHelpButton(
     views::ButtonListener* listener) {
   auto* button = new TopShortcutButton(listener, vector_icons::kHelpOutlineIcon,
                                        IDS_ASH_STATUS_TRAY_HELP);
diff --git a/ash/system/tray/detailed_view_delegate.h b/ash/system/tray/detailed_view_delegate.h
index 0dc900b..75c146c 100644
--- a/ash/system/tray/detailed_view_delegate.h
+++ b/ash/system/tray/detailed_view_delegate.h
@@ -5,6 +5,8 @@
 #ifndef ASH_SYSTEM_TRAY_DETAILED_VIEW_DELEGATE_H_
 #define ASH_SYSTEM_TRAY_DETAILED_VIEW_DELEGATE_H_
 
+#include "ash/ash_export.h"
+#include "base/macros.h"
 #include "base/strings/string16.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -27,71 +29,76 @@
 
 class HoverHighlightView;
 class TriView;
+class UnifiedSystemTrayController;
 class ViewClickListener;
 
 // A delegate of TrayDetailedView that handles bubble related actions e.g.
 // transition to the main view, closing the bubble, etc.
-class DetailedViewDelegate {
+class ASH_EXPORT DetailedViewDelegate {
  public:
-  virtual ~DetailedViewDelegate() {}
+  explicit DetailedViewDelegate(UnifiedSystemTrayController* tray_controller);
+  virtual ~DetailedViewDelegate();
 
   // Transition to the main view from the detailed view. |restore_focus| is true
   // if the title row has keyboard focus before transition. If so, the main view
   // should focus on the corresponding element of the detailed view.
-  virtual void TransitionToMainView(bool restore_focus) = 0;
+  virtual void TransitionToMainView(bool restore_focus);
 
   // Close the bubble that contains the detailed view.
-  virtual void CloseBubble() = 0;
+  virtual void CloseBubble();
 
   // Get the background color of the detailed view.
-  virtual SkColor GetBackgroundColor(ui::NativeTheme* native_theme) = 0;
+  virtual SkColor GetBackgroundColor(ui::NativeTheme* native_theme);
 
   // Return true if overflow indicator of ScrollView is enabled.
-  virtual bool IsOverflowIndicatorEnabled() const = 0;
+  virtual bool IsOverflowIndicatorEnabled() const;
 
   // Return TriView used for the title row. It should have title label of
   // |string_id| in CENTER. TrayDetailedView will calls CreateBackButton() and
   // adds the returned view to START.
-  virtual TriView* CreateTitleRow(int string_id) = 0;
+  virtual TriView* CreateTitleRow(int string_id);
 
   // Return the separator used between the title row and the contents. Caller
   // takes ownership of the returned view.
-  virtual views::View* CreateTitleSeparator() = 0;
+  virtual views::View* CreateTitleSeparator();
 
   // Configure a |view| to have a visible separator below.
   virtual void ShowStickyHeaderSeparator(views::View* view,
-                                         bool show_separator) = 0;
+                                         bool show_separator);
 
   // Create a horizontal separator line to be drawn between rows in a detailed
   // view above the sub-header rows. Caller takes ownership of the returned
   // view.
-  virtual views::Separator* CreateListSubHeaderSeparator() = 0;
+  virtual views::Separator* CreateListSubHeaderSeparator();
 
   // Return a targetable row containing |icon| and |text|. Caller takes
   // ownership of the returned view.
-  virtual HoverHighlightView* CreateScrollListItem(
-      ViewClickListener* listener,
-      const gfx::VectorIcon& icon,
-      const base::string16& text) = 0;
+  virtual HoverHighlightView* CreateScrollListItem(ViewClickListener* listener,
+                                                   const gfx::VectorIcon& icon,
+                                                   const base::string16& text);
 
   // Return the back button used in the title row. Caller takes ownership of the
   // returned view.
-  virtual views::Button* CreateBackButton(views::ButtonListener* listener) = 0;
+  virtual views::Button* CreateBackButton(views::ButtonListener* listener);
 
   // Return the info button used in the title row. Caller takes ownership of the
   // returned view.
   virtual views::Button* CreateInfoButton(views::ButtonListener* listener,
-                                          int info_accessible_name_id) = 0;
+                                          int info_accessible_name_id);
 
   // Return the settings button used in the title row. Caller takes ownership of
   // the returned view.
-  virtual views::Button* CreateSettingsButton(
-      views::ButtonListener* listener,
-      int setting_accessible_name_id) = 0;
+  virtual views::Button* CreateSettingsButton(views::ButtonListener* listener,
+                                              int setting_accessible_name_id);
 
   // Return the help button used in the title row. Caller takes ownership of the
   // returned view.
-  virtual views::Button* CreateHelpButton(views::ButtonListener* listener) = 0;
+  virtual views::Button* CreateHelpButton(views::ButtonListener* listener);
+
+ private:
+  UnifiedSystemTrayController* const tray_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(DetailedViewDelegate);
 };
 
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 0d220d3..ce81ebd 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -115,13 +115,10 @@
 class TrayBackground : public views::Background {
  public:
   explicit TrayBackground(TrayBackgroundView* tray_background_view)
-      : tray_background_view_(tray_background_view),
-        color_(SK_ColorTRANSPARENT) {}
+      : tray_background_view_(tray_background_view) {}
 
   ~TrayBackground() override = default;
 
-  void set_color(SkColor color) { color_ = color; }
-
  private:
   // Overridden from views::Background.
   void Paint(gfx::Canvas* canvas, views::View* view) const override {
@@ -141,8 +138,6 @@
   // Reference to the TrayBackgroundView for which this is a background.
   TrayBackgroundView* tray_background_view_;
 
-  SkColor color_;
-
   DISALLOW_COPY_AND_ASSIGN(TrayBackground);
 };
 
@@ -424,11 +419,6 @@
   // Nothing to do here.
 }
 
-void TrayBackgroundView::UpdateShelfItemBackground(SkColor color) {
-  background_->set_color(color);
-  SchedulePaint();
-}
-
 views::View* TrayBackgroundView::GetBubbleAnchor() const {
   return tray_container_;
 }
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index 069917b..9efb8296 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -107,9 +107,6 @@
   // Updates the arrow visibility based on the launcher visibility.
   void UpdateBubbleViewArrow(TrayBubbleView* bubble_view);
 
-  // ShelfBackgroundAnimatorObserver:
-  void UpdateShelfItemBackground(SkColor color) override;
-
   // Updates the visibility of this tray's separator.
   void set_separator_visibility(bool visible) { separator_visible_ = visible; }
 
diff --git a/ash/system/unified/unified_detailed_view_delegate.h b/ash/system/unified/unified_detailed_view_delegate.h
deleted file mode 100644
index a810e7c..0000000
--- a/ash/system/unified/unified_detailed_view_delegate.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 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_SYSTEM_UNIFIED_UNIFIED_DETAILED_VIEW_DELEGATE_H_
-#define ASH_SYSTEM_UNIFIED_UNIFIED_DETAILED_VIEW_DELEGATE_H_
-
-#include "ash/ash_export.h"
-#include "ash/system/tray/detailed_view_delegate.h"
-#include "base/macros.h"
-
-namespace ash {
-
-class UnifiedSystemTrayController;
-
-// Default implementation of DetailedViewDelegate for UnifiedSystemTray.
-// TODO(tetsui): Combine with DetailedViewDelegate.  https://crbug.com/901713
-class ASH_EXPORT UnifiedDetailedViewDelegate : public DetailedViewDelegate {
- public:
-  explicit UnifiedDetailedViewDelegate(
-      UnifiedSystemTrayController* tray_controller);
-  ~UnifiedDetailedViewDelegate() override;
-
-  // DetailedViewDelegate:
-  void TransitionToMainView(bool restore_focus) override;
-  void CloseBubble() override;
-  SkColor GetBackgroundColor(ui::NativeTheme* native_theme) override;
-  bool IsOverflowIndicatorEnabled() const override;
-  TriView* CreateTitleRow(int string_id) override;
-  views::View* CreateTitleSeparator() override;
-  void ShowStickyHeaderSeparator(views::View* view,
-                                 bool show_separator) override;
-  views::Separator* CreateListSubHeaderSeparator() override;
-  HoverHighlightView* CreateScrollListItem(ViewClickListener* listener,
-                                           const gfx::VectorIcon& icon,
-                                           const base::string16& text) override;
-  views::Button* CreateBackButton(views::ButtonListener* listener) override;
-  views::Button* CreateInfoButton(views::ButtonListener* listener,
-                                  int info_accessible_name_id) override;
-  views::Button* CreateSettingsButton(views::ButtonListener* listener,
-                                      int setting_accessible_name_id) override;
-  views::Button* CreateHelpButton(views::ButtonListener* listener) override;
-
- private:
-  UnifiedSystemTrayController* const tray_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(UnifiedDetailedViewDelegate);
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_UNIFIED_UNIFIED_DETAILED_VIEW_DELEGATE_H_
diff --git a/ash/system/unified/unified_notifier_settings_controller.cc b/ash/system/unified/unified_notifier_settings_controller.cc
index 2e869e8..101ee46 100644
--- a/ash/system/unified/unified_notifier_settings_controller.cc
+++ b/ash/system/unified/unified_notifier_settings_controller.cc
@@ -6,8 +6,8 @@
 
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/message_center/notifier_settings_view.h"
+#include "ash/system/tray/detailed_view_delegate.h"
 #include "ash/system/tray/tray_detailed_view.h"
-#include "ash/system/unified/unified_detailed_view_delegate.h"
 #include "ui/message_center/message_center_observer.h"
 #include "ui/views/layout/box_layout.h"
 
@@ -48,7 +48,7 @@
 UnifiedNotifierSettingsController::UnifiedNotifierSettingsController(
     UnifiedSystemTrayController* tray_controller)
     : detailed_view_delegate_(
-          std::make_unique<UnifiedDetailedViewDelegate>(tray_controller)) {}
+          std::make_unique<DetailedViewDelegate>(tray_controller)) {}
 
 UnifiedNotifierSettingsController::~UnifiedNotifierSettingsController() =
     default;
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index bab83489..bc1b67f3a 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -16,7 +16,7 @@
 
 TestShellDelegate::~TestShellDelegate() = default;
 
-bool TestShellDelegate::CanShowWindowForUser(aura::Window* window) const {
+bool TestShellDelegate::CanShowWindowForUser(const aura::Window* window) const {
   return true;
 }
 
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 932e5bc4..f0604f8 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -18,7 +18,7 @@
   ~TestShellDelegate() override;
 
   // Overridden from ShellDelegate:
-  bool CanShowWindowForUser(aura::Window* window) const override;
+  bool CanShowWindowForUser(const aura::Window* window) const override;
   std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   ws::InputDeviceControllerClient* GetInputDeviceControllerClient() override;
diff --git a/ash/touch/ash_touch_transform_controller.cc b/ash/touch/ash_touch_transform_controller.cc
index 0b4216c3..acf533b 100644
--- a/ash/touch/ash_touch_transform_controller.cc
+++ b/ash/touch/ash_touch_transform_controller.cc
@@ -11,12 +11,9 @@
 namespace ash {
 
 AshTouchTransformController::AshTouchTransformController(
-    display::DisplayConfigurator* display_configurator,
     display::DisplayManager* display_manager,
     std::unique_ptr<display::TouchTransformSetter> setter)
-    : TouchTransformController(display_configurator,
-                               display_manager,
-                               std::move(setter)) {
+    : TouchTransformController(display_manager, std::move(setter)) {
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
 }
 
diff --git a/ash/touch/ash_touch_transform_controller.h b/ash/touch/ash_touch_transform_controller.h
index f6337ae..4506cd93 100644
--- a/ash/touch/ash_touch_transform_controller.h
+++ b/ash/touch/ash_touch_transform_controller.h
@@ -11,7 +11,6 @@
 #include "ui/display/manager/touch_transform_controller.h"
 
 namespace display {
-class DisplayConfigurator;
 class DisplayManager;
 }
 
@@ -24,7 +23,6 @@
       public WindowTreeHostManager::Observer {
  public:
   AshTouchTransformController(
-      display::DisplayConfigurator* display_configurator,
       display::DisplayManager* display_manager,
       std::unique_ptr<display::TouchTransformSetter> setter);
   ~AshTouchTransformController() override;
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc
index 5285463..2fd7a70 100644
--- a/ash/wm/ash_focus_rules.cc
+++ b/ash/wm/ash_focus_rules.cc
@@ -48,20 +48,20 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AshFocusRules, ::wm::FocusRules:
 
-bool AshFocusRules::IsToplevelWindow(aura::Window* window) const {
+bool AshFocusRules::IsToplevelWindow(const aura::Window* window) const {
   return ash::IsToplevelWindow(window);
 }
 
-bool AshFocusRules::SupportsChildActivation(aura::Window* window) const {
+bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const {
   return ash::IsActivatableShellWindowId(window->id());
 }
 
 bool AshFocusRules::IsWindowConsideredVisibleForActivation(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   return ash::IsWindowConsideredVisibleForActivation(window);
 }
 
-bool AshFocusRules::CanActivateWindow(aura::Window* window) const {
+bool AshFocusRules::CanActivateWindow(const aura::Window* window) const {
   // Clearing activation is always permissible.
   if (!window)
     return true;
@@ -85,7 +85,7 @@
   return true;
 }
 
-bool AshFocusRules::CanFocusWindow(aura::Window* window,
+bool AshFocusRules::CanFocusWindow(const aura::Window* window,
                                    const ui::Event* event) const {
   if (!window)
     return true;
diff --git a/ash/wm/ash_focus_rules.h b/ash/wm/ash_focus_rules.h
index 75f5f4f..03bdda8 100644
--- a/ash/wm/ash_focus_rules.h
+++ b/ash/wm/ash_focus_rules.h
@@ -20,12 +20,12 @@
 
  private:
   // ::wm::BaseFocusRules:
-  bool IsToplevelWindow(aura::Window* window) const override;
-  bool SupportsChildActivation(aura::Window* window) const override;
+  bool IsToplevelWindow(const aura::Window* window) const override;
+  bool SupportsChildActivation(const aura::Window* window) const override;
   bool IsWindowConsideredVisibleForActivation(
-      aura::Window* window) const override;
-  bool CanActivateWindow(aura::Window* window) const override;
-  bool CanFocusWindow(aura::Window* window,
+      const aura::Window* window) const override;
+  bool CanActivateWindow(const aura::Window* window) const override;
+  bool CanFocusWindow(const aura::Window* window,
                       const ui::Event* event) const override;
   aura::Window* GetNextActivatableWindow(aura::Window* ignore) const override;
 
diff --git a/ash/wm/focus_rules.cc b/ash/wm/focus_rules.cc
index 3bb9d40..12721b1 100644
--- a/ash/wm/focus_rules.cc
+++ b/ash/wm/focus_rules.cc
@@ -13,7 +13,7 @@
 
 namespace ash {
 
-bool IsToplevelWindow(aura::Window* window) {
+bool IsToplevelWindow(const aura::Window* window) {
   DCHECK(window);
   // The window must in a valid hierarchy.
   if (!window->GetRootWindow() || !window->parent())
@@ -24,7 +24,7 @@
   return IsActivatableShellWindowId(window->parent()->id());
 }
 
-bool IsWindowConsideredActivatable(aura::Window* window) {
+bool IsWindowConsideredActivatable(const aura::Window* window) {
   DCHECK(window);
   // Only toplevel windows can be activated.
   if (!IsToplevelWindow(window))
@@ -41,7 +41,7 @@
   return window->CanFocus();
 }
 
-bool IsWindowConsideredVisibleForActivation(aura::Window* window) {
+bool IsWindowConsideredVisibleForActivation(const aura::Window* window) {
   DCHECK(window);
   // If the |window| doesn't belong to the current active user and also doesn't
   // show for the current active user, then it should not be activated.
diff --git a/ash/wm/focus_rules.h b/ash/wm/focus_rules.h
index 4f5436b..90d607c 100644
--- a/ash/wm/focus_rules.h
+++ b/ash/wm/focus_rules.h
@@ -15,9 +15,10 @@
 
 // These functions provide the ash implementation wm::FocusRules. See
 // description there for details.
-ASH_EXPORT bool IsToplevelWindow(aura::Window* window);
-ASH_EXPORT bool IsWindowConsideredActivatable(aura::Window* window);
-ASH_EXPORT bool IsWindowConsideredVisibleForActivation(aura::Window* window);
+ASH_EXPORT bool IsToplevelWindow(const aura::Window* window);
+ASH_EXPORT bool IsWindowConsideredActivatable(const aura::Window* window);
+ASH_EXPORT bool IsWindowConsideredVisibleForActivation(
+    const aura::Window* window);
 
 }  // namespace ash
 
diff --git a/ash/wm/non_client_frame_controller.cc b/ash/wm/non_client_frame_controller.cc
index 725b714..226d9a3 100644
--- a/ash/wm/non_client_frame_controller.cc
+++ b/ash/wm/non_client_frame_controller.cc
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
 #include "services/ws/window_properties.h"
 #include "services/ws/window_service.h"
@@ -31,7 +32,6 @@
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/platform/aura_window_properties.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/property_utils.h"
 #include "ui/aura/mus/window_port_mus.h"
 #include "ui/aura/window.h"
@@ -53,6 +53,13 @@
                              kNonClientFrameControllerKey,
                              nullptr);
 
+bool DoesClientProvideFrame(
+    std::map<std::string, std::vector<uint8_t>>* properties) {
+  auto iter = properties->find(
+      ws::mojom::WindowManager::kClientProvidesFrame_InitProperty);
+  return iter != properties->end() && mojo::ConvertTo<bool>(iter->second);
+}
+
 // This class supports draggable app windows that paint their own custom frames.
 // It uses empty insets and doesn't paint anything.
 class EmptyDraggableNonClientFrameView : public views::NonClientFrameView {
@@ -91,13 +98,13 @@
 class WmNativeWidgetAura : public views::NativeWidgetAura {
  public:
   WmNativeWidgetAura(views::internal::NativeWidgetDelegate* delegate,
-                     bool remove_standard_frame)
+                     bool client_provides_frame)
       // The NativeWidget is mirroring the real Widget created in client code.
       // |is_parallel_widget_in_window_manager| is used to indicate this
       : views::NativeWidgetAura(delegate,
                                 true /* is_parallel_widget_in_window_manager */,
                                 Shell::Get()->aura_env()),
-        remove_standard_frame_(remove_standard_frame) {}
+        client_provides_frame_(client_provides_frame) {}
   ~WmNativeWidgetAura() override = default;
 
   void set_cursor(const ui::Cursor& cursor) { cursor_ = cursor; }
@@ -106,7 +113,7 @@
   views::NonClientFrameView* CreateNonClientFrameView() override {
     // TODO(sky): investigate why we have this. Seems this should be the same
     // as not specifying client area insets.
-    if (remove_standard_frame_) {
+    if (client_provides_frame_) {
       wm::InstallResizeHandleWindowTargeterForWindow(GetNativeWindow());
       return new EmptyDraggableNonClientFrameView();
     }
@@ -134,7 +141,7 @@
 
   gfx::Size GetMinimumSize() const override {
     aura::Window* window = GetNativeWindow();
-    if (window && remove_standard_frame_ &&
+    if (window && client_provides_frame_ &&
         window->GetProperty(aura::client::kMinimumSize)) {
       return *window->GetProperty(aura::client::kMinimumSize);
     }
@@ -142,7 +149,9 @@
   }
 
  private:
-  const bool remove_standard_frame_;
+  // True if the client has asked to be responsible for the window's frame. In
+  // this case, Ash won't create a NonClientFrameViewAsh.
+  const bool client_provides_frame_;
 
   // The cursor for this widget. CompoundEventFilter will retrieve this cursor
   // via GetCursor and update the CursorManager's active cursor as appropriate
@@ -238,7 +247,7 @@
   params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
   params.layer_type = ui::LAYER_SOLID_COLOR;
   WmNativeWidgetAura* native_widget =
-      new WmNativeWidgetAura(widget_, ShouldRemoveStandardFrame(*properties));
+      new WmNativeWidgetAura(widget_, DoesClientProvideFrame(properties));
   window_ = native_widget->GetNativeView();
   window_->SetProperty(kNonClientFrameControllerKey, this);
   window_->SetProperty(kWidgetCreationTypeKey, WidgetCreationType::FOR_CLIENT);
diff --git a/ash/wm/non_client_frame_controller_unittest.cc b/ash/wm/non_client_frame_controller_unittest.cc
index 9b438bf..5c40b90 100644
--- a/ash/wm/non_client_frame_controller_unittest.cc
+++ b/ash/wm/non_client_frame_controller_unittest.cc
@@ -95,7 +95,7 @@
   auto properties = CreatePropertiesForProxyWindow();
   properties[ws::mojom::WindowManager::kMinimumSize_Property] =
       mojo::ConvertTo<TransportType>(min_size);
-  properties[ws::mojom::WindowManager::kRemoveStandardFrame_InitProperty] =
+  properties[ws::mojom::WindowManager::kClientProvidesFrame_InitProperty] =
       mojo::ConvertTo<TransportType>(true);
   std::unique_ptr<aura::Window> window(
       GetWindowTreeTestHelper()->NewTopLevelWindow(
@@ -107,7 +107,7 @@
 TEST_F(NonClientFrameControllerTest, NonClientAreaShouldBeDraggable) {
   using TransportType = std::vector<uint8_t>;
   auto properties = CreatePropertiesForProxyWindow();
-  properties[ws::mojom::WindowManager::kRemoveStandardFrame_InitProperty] =
+  properties[ws::mojom::WindowManager::kClientProvidesFrame_InitProperty] =
       mojo::ConvertTo<TransportType>(true);
   std::unique_ptr<aura::Window> window(
       GetWindowTreeTestHelper()->NewTopLevelWindow(
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index e03a821..37f4b747 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -511,8 +511,7 @@
     }
     changed_selection_index = true;
   }
-  while (!changed_selection_index ||
-         (!out_of_bounds && window_list_[selected_index_]->dimmed())) {
+  while (!changed_selection_index) {
     switch (direction) {
       case WindowSelector::UP:
       case WindowSelector::LEFT:
@@ -593,22 +592,6 @@
     PositionWindows(/*animate=*/true);
 }
 
-void WindowGrid::FilterItems(const base::string16& pattern) {
-  base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents finder(pattern);
-  for (const auto& window : window_list_) {
-    if (finder.Search(window->GetWindow()->GetTitle(), nullptr, nullptr)) {
-      window->SetDimmed(false);
-    } else {
-      window->SetDimmed(true);
-      if (selection_widget_ && SelectedWindow() == window.get()) {
-        SelectedWindow()->set_selected(false);
-        selection_widget_.reset();
-        selector_shadow_.reset();
-      }
-    }
-  }
-}
-
 void WindowGrid::SetBoundsAndUpdatePositions(const gfx::Rect& bounds) {
   SetBoundsAndUpdatePositionsIgnoringWindow(bounds, nullptr);
 }
diff --git a/ash/wm/overview/window_grid.h b/ash/wm/overview/window_grid.h
index 12775a2..d529a39 100644
--- a/ash/wm/overview/window_grid.h
+++ b/ash/wm/overview/window_grid.h
@@ -102,12 +102,6 @@
   // all window items in the grid after removing the item.
   void RemoveItem(WindowSelectorItem* selector_item, bool reposition);
 
-  // Dims the items whose titles do not contain |pattern| and prevents their
-  // selection. The pattern has its accents removed and is converted to
-  // lowercase in a l10n sensitive context.
-  // If |pattern| is empty, no item is dimmed.
-  void FilterItems(const base::string16& pattern);
-
   // Sets bounds for the window grid and positions all windows in the grid.
   void SetBoundsAndUpdatePositions(const gfx::Rect& bounds_in_screen);
   void SetBoundsAndUpdatePositionsIgnoringWindow(
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index d9aedc8..90832b2d 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -6,24 +6,18 @@
 
 #include <algorithm>
 #include <functional>
-#include <memory>
-#include <set>
 #include <utility>
-#include <vector>
 
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
-#include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "ash/wm/overview/overview_window_drag_controller.h"
-#include "ash/wm/overview/rounded_rect_view.h"
 #include "ash/wm/overview/window_grid.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/overview/window_selector_delegate.h"
@@ -38,22 +32,11 @@
 #include "base/metrics/user_metrics.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/skia_util.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/border.h"
-#include "ui/views/controls/image_view.h"
-#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/window_util.h"
 
@@ -61,73 +44,6 @@
 
 namespace {
 
-// The amount of padding surrounding the text in the text filtering textbox.
-constexpr int kTextFilterHorizontalPadding = 6;
-
-// The height of the text filtering textbox.
-constexpr int kTextFilterHeight = 32;
-
-// The margin at the bottom to make sure the text filter layer is hidden.
-// This is needed because positioning the text filter directly touching the top
-// edge of the screen still allows the shadow to peek through.
-constexpr int kTextFieldBottomMargin = 2;
-
-// Distance from top of overview to the top of text filtering textbox as a
-// proportion of the total overview area.
-constexpr float kTextFilterTopScreenProportion = 0.02f;
-
-// Width of the text filter area.
-constexpr int kTextFilterWidth = 280;
-
-// The font delta used for text filtering textbox.
-constexpr int kTextFilterFontDelta = 1;
-
-// The color of the text and its background in the text filtering textbox.
-constexpr SkColor kTextFilterTextColor = SkColorSetARGB(0xFF, 0x3C, 0x40, 0x43);
-constexpr SkColor kTextFilterBackgroundColor = SK_ColorWHITE;
-
-// The color or search icon.
-constexpr SkColor kTextFilterIconColor = SkColorSetARGB(138, 0, 0, 0);
-
-// The size of search icon.
-constexpr int kTextFilterIconSize = 20;
-
-// The radius used for the rounded corners on the text filtering textbox.
-constexpr int kTextFilterCornerRadius = 16;
-
-// A comparator for locating a selector item for a given root.
-struct WindowSelectorItemForRoot {
-  explicit WindowSelectorItemForRoot(const aura::Window* root)
-      : root_window(root) {}
-
-  bool operator()(WindowSelectorItem* item) const {
-    return item->root_window() == root_window;
-  }
-
-  const aura::Window* root_window;
-};
-
-// A WidgetDelegate to specify the initialy focused view.
-class TextFilterWidgetDelegate : public views::WidgetDelegate {
- public:
-  TextFilterWidgetDelegate(views::Widget* widget, views::View* initial_focus)
-      : widget_(widget), initial_focus_(initial_focus) {}
-  ~TextFilterWidgetDelegate() override = default;
-
-  // WidgetDelegate:
-  void DeleteDelegate() override { delete this; }
-  views::Widget* GetWidget() override { return widget_; }
-  const views::Widget* GetWidget() const override { return widget_; }
-  bool ShouldAdvanceFocusToTopLevelWidget() const override { return true; }
-  views::View* GetInitiallyFocusedView() override { return initial_focus_; }
-
- private:
-  views::Widget* widget_;
-  views::View* initial_focus_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextFilterWidgetDelegate);
-};
-
 // Triggers a shelf visibility update on all root window controllers.
 void UpdateShelfVisibility() {
   for (aura::Window* root : Shell::GetAllRootWindows())
@@ -214,83 +130,6 @@
   return bounds;
 }
 
-gfx::Rect GetTextFilterPosition(aura::Window* root_window) {
-  const gfx::Rect total_bounds =
-      GetGridBoundsInScreen(root_window, /*divider_changed=*/false);
-  return gfx::Rect(
-      total_bounds.x() +
-          0.5 * (total_bounds.width() -
-                 std::min(kTextFilterWidth, total_bounds.width())),
-      total_bounds.y() + total_bounds.height() * kTextFilterTopScreenProportion,
-      std::min(kTextFilterWidth, total_bounds.width()), kTextFilterHeight);
-}
-
-// Initializes the text filter on the top of the main root window and requests
-// focus on its textfield. Uses |image| to place an icon to the left of the text
-// field.
-views::Widget* CreateTextFilter(views::TextfieldController* controller,
-                                aura::Window* root_window,
-                                const gfx::ImageSkia& image,
-                                int* text_filter_bottom) {
-  views::Widget* widget = new views::Widget;
-  views::Widget::InitParams params;
-  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
-  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  params.accept_events = true;
-  params.bounds = GetTextFilterPosition(root_window);
-  params.name = "OverviewModeTextFilter";
-  *text_filter_bottom = params.bounds.bottom() + kTextFieldBottomMargin;
-  params.parent = root_window->GetChildById(kShellWindowId_StatusContainer);
-
-  views::Textfield* textfield = new views::Textfield();
-  params.delegate = new TextFilterWidgetDelegate(widget, textfield);
-  widget->Init(params);
-
-  // Use |container| to specify the padding surrounding the text and to give
-  // the textfield rounded corners.
-  views::View* container =
-      new RoundedRectView(kTextFilterCornerRadius, kTextFilterBackgroundColor);
-  const gfx::FontList& font_list =
-      views::Textfield::GetDefaultFontList().Derive(
-          kTextFilterFontDelta, gfx::Font::FontStyle::NORMAL,
-          gfx::Font::Weight::NORMAL);
-  const int text_height = std::max(kTextFilterIconSize, font_list.GetHeight());
-  DCHECK(text_height);
-  const int vertical_padding = (params.bounds.height() - text_height) / 2;
-  auto* layout = container->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal,
-      gfx::Insets(vertical_padding, kTextFilterHorizontalPadding,
-                  vertical_padding, kTextFilterCornerRadius),
-      kTextFilterHorizontalPadding));
-
-  textfield->set_controller(controller);
-  textfield->SetBorder(views::NullBorder());
-  textfield->SetBackgroundColor(kTextFilterBackgroundColor);
-  textfield->SetTextColor(kTextFilterTextColor);
-  textfield->SetFontList(font_list);
-  textfield->SetAccessibleName(l10n_util::GetStringUTF16(
-      IDS_ASH_WINDOW_SELECTOR_INPUT_FILTER_ACCESSIBLE_NAME));
-
-  views::ImageView* image_view = new views::ImageView();
-  image_view->SetImage(image);
-
-  container->AddChildView(image_view);
-  container->AddChildView(textfield);
-  layout->SetFlexForView(textfield, 1);
-  widget->SetContentsView(container);
-
-  // The textfield initially contains no text, so shift its position to be
-  // outside the visible bounds of the screen.
-  gfx::Transform transform;
-  transform.Translate(0, -(*text_filter_bottom));
-  aura::Window* text_filter_widget_window = widget->GetNativeWindow();
-  text_filter_widget_window->layer()->SetOpacity(0);
-  text_filter_widget_window->SetTransform(transform);
-
-  return widget;
-}
-
 }  // namespace
 
 WindowSelector::WindowSelector(WindowSelectorDelegate* delegate)
@@ -298,6 +137,7 @@
       restore_focus_window_(wm::GetFocusedWindow()),
       overview_start_time_(base::Time::Now()) {
   DCHECK(delegate_);
+  Shell::Get()->AddPreTargetHandler(this);
 }
 
 WindowSelector::~WindowSelector() {
@@ -352,49 +192,52 @@
     grid_list_.push_back(std::move(grid));
   }
 
-  {
-    // The calls to WindowGrid::PrepareForOverview() and CreateTextFilter(...)
-    // requires some LayoutManagers to perform layouts so that windows are
-    // correctly visible and properly animated in overview mode. Otherwise
-    // these layouts should be suppressed during overview mode so they don't
-    // conflict with overview mode animations.
+  // The calls to WindowGrid::PrepareForOverview() requires some LayoutManagers
+  // to perform layouts so that windows are correctly visible and properly
+  // animated in overview mode. Otherwise these layouts should be suppressed
+  // during overview mode so they don't conflict with overview mode animations.
 
-    // Do not call PrepareForOverview until all items are added to window_list_
-    // as we don't want to cause any window updates until all windows in
-    // overview are observed. See http://crbug.com/384495.
-    for (std::unique_ptr<WindowGrid>& window_grid : grid_list_) {
-      window_grid->PrepareForOverview();
+  // Do not call PrepareForOverview until all items are added to window_list_
+  // as we don't want to cause any window updates until all windows in
+  // overview are observed. See http://crbug.com/384495.
+  for (std::unique_ptr<WindowGrid>& window_grid : grid_list_) {
+    window_grid->PrepareForOverview();
 
-      // Do not animate if there is any window that is being dragged in the
-      // grid.
-      if (enter_exit_overview_type_ == EnterExitOverviewType::kWindowDragged) {
-        window_grid->PositionWindows(/*animate=*/false);
-      } else if (enter_exit_overview_type_ ==
-                 EnterExitOverviewType::kWindowsMinimized) {
-        window_grid->PositionWindows(/*animate=*/false);
-        window_grid->SlideWindowsIn();
-      } else {
-        // EnterExitOverviewType::kSwipeFromShelf is an exit only type, so it
-        // should not appear here.
-        DCHECK_NE(enter_exit_overview_type_,
-                  EnterExitOverviewType::kSwipeFromShelf);
-        window_grid->CalculateWindowListAnimationStates(
-            /*selected_item=*/nullptr, OverviewTransition::kEnter);
-        window_grid->PositionWindows(/*animate=*/true, /*ignore_item=*/nullptr,
-                                     OverviewTransition::kEnter);
-      }
+    // Do not animate if there is any window that is being dragged in the
+    // grid.
+    if (enter_exit_overview_type_ == EnterExitOverviewType::kWindowDragged) {
+      window_grid->PositionWindows(/*animate=*/false);
+    } else if (enter_exit_overview_type_ ==
+               EnterExitOverviewType::kWindowsMinimized) {
+      window_grid->PositionWindows(/*animate=*/false);
+      window_grid->SlideWindowsIn();
+    } else {
+      // EnterExitOverviewType::kSwipeFromShelf is an exit only type, so it
+      // should not appear here.
+      DCHECK_NE(enter_exit_overview_type_,
+                EnterExitOverviewType::kSwipeFromShelf);
+      window_grid->CalculateWindowListAnimationStates(
+          /*selected_item=*/nullptr, OverviewTransition::kEnter);
+      window_grid->PositionWindows(/*animate=*/true, /*ignore_item=*/nullptr,
+                                   OverviewTransition::kEnter);
     }
-
-    // Image used for text filter textfield.
-    const gfx::ImageSkia search_image =
-        gfx::CreateVectorIcon(kOverviewTextFilterSearchIcon,
-                              kTextFilterIconSize, kTextFilterIconColor);
-
-    aura::Window* root_window = Shell::GetPrimaryRootWindow();
-    text_filter_widget_.reset(CreateTextFilter(this, root_window, search_image,
-                                               &text_filter_bottom_));
   }
 
+  // Create the widget that will receive focus while in overview mode for
+  // accessiblity purposes.
+  overview_focus_widget_ = std::make_unique<views::Widget>();
+  views::Widget::InitParams params;
+  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+  params.accept_events = false;
+  params.bounds = gfx::Rect(0, 0, 2, 2);
+  params.layer_type = ui::LAYER_NOT_DRAWN;
+  params.name = "OverviewModeFocusedWidget";
+  params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
+      kShellWindowId_StatusContainer);
+  overview_focus_widget_->Init(params);
+
   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_);
 
   Shell::Get()->split_view_controller()->AddObserver(this);
@@ -414,6 +257,8 @@
 // may cause other, unrelated classes, to make indirect calls to
 // restoring_minimized_windows() on a partially destructed object.
 void WindowSelector::Shutdown() {
+  Shell::Get()->RemovePreTargetHandler(this);
+
   // Stop observing screen metrics changes first to avoid auto-positioning
   // windows in response to work area changes from window activation.
   display::Screen::GetScreen()->RemoveObserver(this);
@@ -455,18 +300,6 @@
   UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview",
                              base::Time::Now() - overview_start_time_);
 
-  // Record metrics related to text filtering.
-  UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringStringLength",
-                           text_filter_string_length_);
-  UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringTextfieldCleared",
-                           num_times_textfield_cleared_);
-  if (text_filter_string_length_) {
-    UMA_HISTOGRAM_MEDIUM_TIMES(
-        "Ash.WindowSelector.TimeInOverviewWithTextFiltering",
-        base::Time::Now() - overview_start_time_);
-    UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ItemsWhenTextFilteringUsed",
-                             remaining_items);
-  }
 
   // Clearing the window list resets the ignored_by_shelf flag on the windows.
   grid_list_.clear();
@@ -551,7 +384,7 @@
     }
   }
   item->EnsureVisible();
-  wm::GetWindowState(window)->Activate();
+  ::wm::ActivateWindow(window);
 }
 
 void WindowSelector::SetBoundsForWindowGridsInScreenIgnoringWindow(
@@ -589,9 +422,10 @@
   grid->AddItem(window, reposition, animate);
   ++num_items_;
 
-  // Transfer focus from |window| to the text widget, to match the behavior of
-  // entering overview mode in the beginning.
-  wm::ActivateWindow(GetTextFilterWidgetWindow());
+  // Transfer focus from |window| to |overview_focus_widget_| to match the
+  // behavior of entering overview mode in the beginning.
+  DCHECK(overview_focus_widget_);
+  ::wm::ActivateWindow(GetOverviewFocusWindow());
 }
 
 void WindowSelector::RemoveWindowSelectorItem(WindowSelectorItem* item,
@@ -755,8 +589,8 @@
 void WindowSelector::OnStartingAnimationComplete(bool canceled) {
   if (!canceled) {
     UpdateMaskAndShadow(!canceled);
-    if (text_filter_widget_)
-      text_filter_widget_->Show();
+    if (overview_focus_widget_)
+      overview_focus_widget_->Show();
     for (auto& grid : grid_list_) {
       for (auto& window : grid->window_list())
         window->OnStartingAnimationComplete();
@@ -782,25 +616,13 @@
     aura::Window* gained_active,
     aura::Window* lost_active) {
   if (ignore_activations_ || !gained_active ||
-      gained_active == GetTextFilterWidgetWindow()) {
+      gained_active == GetOverviewFocusWindow()) {
     return;
   }
 
   auto* grid = GetGridWithRootWindow(gained_active->GetRootWindow());
   if (!grid)
     return;
-  const auto& windows = grid->window_list();
-
-  auto iter = std::find_if(
-      windows.begin(), windows.end(),
-      [gained_active](const std::unique_ptr<WindowSelectorItem>& window) {
-        return window->Contains(gained_active);
-      });
-
-  if (iter == windows.end() && showing_text_filter_ &&
-      lost_active == GetTextFilterWidgetWindow()) {
-    return;
-  }
 
   // Do not cancel overview mode if the window activation was caused by
   // snapping window to one side of the screen.
@@ -814,11 +636,25 @@
 
   // Don't restore focus on exit if a window was just activated.
   ResetFocusRestoreWindow(false);
+  const auto& windows = grid->window_list();
+  auto iter = std::find_if(
+      windows.begin(), windows.end(),
+      [gained_active](const std::unique_ptr<WindowSelectorItem>& window) {
+        return window->Contains(gained_active);
+      });
+
   if (iter != windows.end())
     selected_item_ = iter->get();
   CancelSelection();
 }
 
+aura::Window* WindowSelector::GetOverviewFocusWindow() {
+  if (overview_focus_widget_)
+    return overview_focus_widget_->GetNativeWindow();
+
+  return nullptr;
+}
+
 void WindowSelector::OnDisplayRemoved(const display::Display& display) {
   // TODO(flackr): Keep window selection active on remaining displays.
   CancelSelection();
@@ -874,64 +710,11 @@
     restore_focus_window_ = nullptr;
 }
 
-void WindowSelector::ContentsChanged(views::Textfield* sender,
-                                     const base::string16& new_contents) {
-  // If the user enters underline mode via CTRL+SHIFT+U, ContentsChanged
-  // will get called after shutdown has started. Prevent anything from
-  // happening if shutdown has started (grids have been cleared).
-  if (grid_list_.size() < 1)
+void WindowSelector::OnKeyEvent(ui::KeyEvent* event) {
+  if (event->type() != ui::ET_KEY_PRESSED)
     return;
 
-  text_filter_string_length_ = new_contents.length();
-  if (!text_filter_string_length_)
-    num_times_textfield_cleared_++;
-
-  bool should_show_text_filter = !new_contents.empty();
-  if (showing_text_filter_ != should_show_text_filter) {
-    aura::Window* text_filter_widget_window = GetTextFilterWidgetWindow();
-    ui::ScopedLayerAnimationSettings animation_settings(
-        text_filter_widget_window->layer()->GetAnimator());
-    animation_settings.SetPreemptionStrategy(
-        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-    animation_settings.SetTweenType(showing_text_filter_
-                                        ? gfx::Tween::FAST_OUT_LINEAR_IN
-                                        : gfx::Tween::LINEAR_OUT_SLOW_IN);
-
-    gfx::Transform transform;
-    if (should_show_text_filter) {
-      transform.Translate(0, 0);
-      text_filter_widget_window->layer()->SetOpacity(1);
-    } else {
-      transform.Translate(0, -text_filter_bottom_);
-      text_filter_widget_window->layer()->SetOpacity(0);
-    }
-
-    text_filter_widget_window->SetTransform(transform);
-    showing_text_filter_ = should_show_text_filter;
-  }
-  for (std::unique_ptr<WindowGrid>& grid : grid_list_)
-    grid->FilterItems(new_contents);
-
-  // If the selection widget is not active and the filter string is not empty,
-  // execute a Move() command so that it shows up on the first undimmed item.
-  if (grid_list_[selected_grid_index_]->is_selecting() || new_contents.empty())
-    return;
-  Move(WindowSelector::RIGHT, false);
-}
-
-bool WindowSelector::HandleKeyEvent(views::Textfield* sender,
-                                    const ui::KeyEvent& key_event) {
-  // Do not do anything with the events if none of the window grids have windows
-  // in them.
-  if (IsEmpty() && key_event.key_code() != ui::VKEY_BROWSER_BACK &&
-      key_event.key_code() != ui::VKEY_ESCAPE) {
-    return true;
-  }
-
-  if (key_event.type() != ui::ET_KEY_PRESSED)
-    return false;
-
-  switch (key_event.key_code()) {
+  switch (event->key_code()) {
     case ui::VKEY_BROWSER_BACK:
       FALLTHROUGH;
     case ui::VKEY_ESCAPE:
@@ -947,8 +730,8 @@
       break;
     case ui::VKEY_RIGHT:
     case ui::VKEY_TAB:
-      if (key_event.key_code() == ui::VKEY_RIGHT ||
-          !(key_event.flags() & ui::EF_SHIFT_DOWN)) {
+      if (event->key_code() == ui::VKEY_RIGHT ||
+          !(event->flags() & ui::EF_SHIFT_DOWN)) {
         num_key_presses_++;
         Move(WindowSelector::RIGHT, true);
         break;
@@ -959,10 +742,9 @@
       Move(WindowSelector::LEFT, true);
       break;
     case ui::VKEY_W:
-      if (!(key_event.flags() & ui::EF_CONTROL_DOWN) ||
+      if (!(event->flags() & ui::EF_CONTROL_DOWN) ||
           !grid_list_[selected_grid_index_]->is_selecting()) {
-        // Allow the textfield to handle 'W' key when not used with Ctrl.
-        return false;
+        return;
       }
       base::RecordAction(
           base::UserMetricsAction("WindowSelector_OverviewCloseKey"));
@@ -971,7 +753,7 @@
     case ui::VKEY_RETURN:
       // Ignore if no item is selected.
       if (!grid_list_[selected_grid_index_]->is_selecting())
-        return false;
+        return;
       UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ArrowKeyPresses",
                                num_key_presses_);
       UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.WindowSelector.KeyPressesOverItemsRatio",
@@ -982,10 +764,11 @@
       SelectWindow(grid_list_[selected_grid_index_]->SelectedWindow());
       break;
     default:
-      // Not a key we are interested in, allow the textfield to handle it.
-      return false;
+      break;
   }
-  return true;
+
+  event->SetHandled();
+  event->StopPropagation();
 }
 
 void WindowSelector::OnSplitViewStateChanged(
@@ -1025,25 +808,6 @@
                               /*divider_changed=*/true));
   }
   PositionWindows(/*animate=*/false);
-  RepositionTextFilterOnDisplayMetricsChange();
-}
-
-aura::Window* WindowSelector::GetTextFilterWidgetWindow() {
-  return text_filter_widget_->GetNativeWindow();
-}
-
-void WindowSelector::RepositionTextFilterOnDisplayMetricsChange() {
-  const gfx::Rect rect = GetTextFilterPosition(Shell::GetPrimaryRootWindow());
-  text_filter_bottom_ = rect.bottom() + kTextFieldBottomMargin;
-  text_filter_widget_->SetBounds(rect);
-
-  gfx::Transform transform;
-  transform.Translate(
-      0, text_filter_string_length_ == 0 ? -text_filter_bottom_ : 0);
-  aura::Window* text_filter_window = GetTextFilterWidgetWindow();
-  text_filter_window->layer()->SetOpacity(text_filter_string_length_ == 0 ? 0
-                                                                          : 1);
-  text_filter_window->SetTransform(transform);
 }
 
 void WindowSelector::ResetFocusRestoreWindow(bool focus) {
@@ -1102,7 +866,6 @@
                               /*divider_changed=*/false));
   }
   PositionWindows(/*animate=*/false);
-  RepositionTextFilterOnDisplayMetricsChange();
   if (split_view_drag_indicators_)
     split_view_drag_indicators_->OnDisplayBoundsChanged();
 }
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index ac93564..c1f886e 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -19,7 +19,7 @@
 #include "base/time/time.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
-#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/events/event_handler.h"
 #include "ui/wm/public/activation_change_observer.h"
 
 namespace gfx {
@@ -27,8 +27,11 @@
 class Rect;
 }  // namespace gfx
 
+namespace ui {
+class KeyEvent;
+}  // namespace ui
+
 namespace views {
-class Textfield;
 class Widget;
 }  // namespace views
 
@@ -47,7 +50,7 @@
 // one by clicking or tapping on it.
 class ASH_EXPORT WindowSelector : public display::DisplayObserver,
                                   public aura::WindowObserver,
-                                  public views::TextfieldController,
+                                  public ui::EventHandler,
                                   public SplitViewController::Observer {
  public:
   enum Direction { LEFT, UP, RIGHT, DOWN };
@@ -224,16 +227,15 @@
       aura::Window* gained_active,
       aura::Window* lost_active);
 
+  // Gets the window which keeps focus for the duration of overview mode.
+  aura::Window* GetOverviewFocusWindow();
+
   WindowSelectorDelegate* delegate() { return delegate_; }
 
   SplitViewDragIndicators* split_view_drag_indicators() {
     return split_view_drag_indicators_.get();
   }
 
-  views::Widget* text_filter_widget() { return text_filter_widget_.get(); }
-
-  int text_filter_bottom() const { return text_filter_bottom_; }
-
   EnterExitOverviewType enter_exit_overview_type() const {
     return enter_exit_overview_type_;
   }
@@ -259,11 +261,8 @@
   void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override;
   void OnWindowDestroying(aura::Window* window) override;
 
-  // views::TextfieldController:
-  void ContentsChanged(views::Textfield* sender,
-                       const base::string16& new_contents) override;
-  bool HandleKeyEvent(views::Textfield* sender,
-                      const ui::KeyEvent& key_event) override;
+  // ui::EventHandler:
+  void OnKeyEvent(ui::KeyEvent* event) override;
 
   // SplitViewController::Observer:
   void OnSplitViewStateChanged(SplitViewController::State previous_state,
@@ -273,13 +272,6 @@
  private:
   friend class WindowSelectorTest;
 
-  // Returns the aura::Window for |text_filter_widget_|.
-  aura::Window* GetTextFilterWidgetWindow();
-
-  // Repositions and resizes |text_filter_widget_| on
-  // DisplayMetricsChanged event.
-  void RepositionTextFilterOnDisplayMetricsChange();
-
   // |focus|, restores focus to the stored window.
   void ResetFocusRestoreWindow(bool focus);
 
@@ -307,7 +299,17 @@
   // A weak pointer to the window which was focused on beginning window
   // selection. If window selection is canceled the focus should be restored to
   // this window.
-  aura::Window* restore_focus_window_;
+  aura::Window* restore_focus_window_ = nullptr;
+
+  // A hidden window that receives focus while in overview mode. It is needed
+  // because accessibility needs something focused for it to work and we cannot
+  // use one of the overview windows otherwise ::wm::ActivateWindow will not
+  // work.
+  // TODO(sammiequon): Investigate if we can focus the |selection_widget_| in
+  // WindowGrid when it is created, or if we can focus a widget from the virtual
+  // desks UI when that is complete, or we may be able to add some mechanism to
+  // trigger accessibility events without a focused window.
+  std::unique_ptr<views::Widget> overview_focus_widget_;
 
   // True when performing operations that may cause window activations. This is
   // used to prevent handling the resulting expected activation. This is
@@ -335,25 +337,6 @@
   // The number of items in the overview.
   size_t num_items_ = 0;
 
-  // Indicates if the text filter is shown on screen (rather than above it).
-  bool showing_text_filter_ = false;
-
-  // Window text filter widget. As the user writes on it, we filter the items
-  // in the overview. It is also responsible for handling overview key events,
-  // such as enter key to select.
-  std::unique_ptr<views::Widget> text_filter_widget_;
-
-  // The current length of the string entered into the text filtering textfield.
-  size_t text_filter_string_length_ = 0;
-
-  // The number of times the text filtering textfield has been cleared of text
-  // during this overview mode session.
-  size_t num_times_textfield_cleared_ = 0;
-
-  // The distance between the top edge of the screen and the bottom edge of
-  // the text filtering textfield.
-  int text_filter_bottom_ = 0;
-
   // Stores the overview enter/exit type. See the enum declaration for
   // information on how these types affect overview mode.
   EnterExitOverviewType enter_exit_overview_type_ =
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index 3ee19d28..def8ad01e 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -40,9 +40,6 @@
 
 namespace {
 
-// Opacity for dimmed items.
-constexpr float kDimmedItemOpacity = 0.3f;
-
 // Opacity for fading out during closing a window.
 constexpr float kClosingItemOpacity = 0.8f;
 
@@ -429,66 +426,12 @@
   SetBounds(scaled_bounds, animation_type);
 }
 
-void WindowSelectorItem::SetDimmed(bool dimmed) {
-  dimmed_ = dimmed;
-  SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
-}
-
 void WindowSelectorItem::RestackItemWidget() {
   aura::Window* widget_window = item_widget_->GetNativeWindow();
   widget_window->parent()->StackChildAbove(widget_window,
                                            GetWindowForStacking());
 }
 
-void WindowSelectorItem::ButtonPressed(views::Button* sender,
-                                       const ui::Event& event) {
-  if (IsSlidingOutOverviewFromShelf())
-    return;
-
-  if (sender == caption_container_view_->GetCloseButton()) {
-    base::RecordAction(
-        base::UserMetricsAction("WindowSelector_OverviewCloseButton"));
-    if (Shell::Get()
-            ->tablet_mode_controller()
-            ->IsTabletModeWindowManagerEnabled()) {
-      base::RecordAction(
-          base::UserMetricsAction("Tablet_WindowCloseFromOverviewButton"));
-    }
-    CloseWindow();
-    return;
-  }
-
-  CHECK(sender == caption_container_view_->GetListenerButton());
-
-  // For other cases, the event is handled in OverviewWindowDragController.
-  if (!ShouldAllowSplitView())
-    window_selector_->SelectWindow(this);
-}
-
-void WindowSelectorItem::OnWindowBoundsChanged(
-    aura::Window* window,
-    const gfx::Rect& old_bounds,
-    const gfx::Rect& new_bounds,
-    ui::PropertyChangeReason reason) {
-  if (reason == ui::PropertyChangeReason::NOT_FROM_ANIMATION)
-    transform_window_.ResizeMinimizedWidgetIfNeeded();
-}
-
-void WindowSelectorItem::OnWindowDestroying(aura::Window* window) {
-  window->RemoveObserver(this);
-  transform_window_.OnWindowDestroyed();
-}
-
-void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
-  // TODO(flackr): Maybe add the new title to a vector of titles so that we can
-  // filter any of the titles the window had while in the overview session.
-  caption_container_view_->SetTitle(window->GetTitle());
-}
-
-void WindowSelectorItem::OnImplicitAnimationsCompleted() {
-  transform_window_.Close();
-}
-
 void WindowSelectorItem::HandlePressEvent(
     const gfx::Point& location_in_screen) {
   // We allow switching finger while dragging, but do not allow dragging two or more items.
@@ -662,6 +605,55 @@
                                       : OVERVIEW_ANIMATION_RESTORE_WINDOW_ZERO;
 }
 
+void WindowSelectorItem::ButtonPressed(views::Button* sender,
+                                       const ui::Event& event) {
+  if (IsSlidingOutOverviewFromShelf())
+    return;
+
+  if (sender == caption_container_view_->GetCloseButton()) {
+    base::RecordAction(
+        base::UserMetricsAction("WindowSelector_OverviewCloseButton"));
+    if (Shell::Get()
+            ->tablet_mode_controller()
+            ->IsTabletModeWindowManagerEnabled()) {
+      base::RecordAction(
+          base::UserMetricsAction("Tablet_WindowCloseFromOverviewButton"));
+    }
+    CloseWindow();
+    return;
+  }
+
+  CHECK_EQ(sender, caption_container_view_->GetListenerButton());
+
+  // For other cases, the event is handled in OverviewWindowDragController.
+  if (!ShouldAllowSplitView())
+    window_selector_->SelectWindow(this);
+}
+
+void WindowSelectorItem::OnWindowBoundsChanged(
+    aura::Window* window,
+    const gfx::Rect& old_bounds,
+    const gfx::Rect& new_bounds,
+    ui::PropertyChangeReason reason) {
+  if (reason == ui::PropertyChangeReason::NOT_FROM_ANIMATION)
+    transform_window_.ResizeMinimizedWidgetIfNeeded();
+}
+
+void WindowSelectorItem::OnWindowDestroying(aura::Window* window) {
+  window->RemoveObserver(this);
+  transform_window_.OnWindowDestroyed();
+}
+
+void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
+  // TODO(flackr): Maybe add the new title to a vector of titles so that we can
+  // filter any of the titles the window had while in the overview session.
+  caption_container_view_->SetTitle(window->GetTitle());
+}
+
+void WindowSelectorItem::OnImplicitAnimationsCompleted() {
+  transform_window_.Close();
+}
+
 float WindowSelectorItem::GetCloseButtonVisibilityForTesting() const {
   return caption_container_view_->GetCloseButton()->layer()->opacity();
 }
diff --git a/ash/wm/overview/window_selector_item.h b/ash/wm/overview/window_selector_item.h
index 277950a..d816765 100644
--- a/ash/wm/overview/window_selector_item.h
+++ b/ash/wm/overview/window_selector_item.h
@@ -144,11 +144,6 @@
   // Increases the bounds of the dragged item.
   void ScaleUpSelectedItem(OverviewAnimationType animation_type);
 
-  // Sets if the item is dimmed in the overview. Changing the value will also
-  // change the visibility of the transform windows.
-  void SetDimmed(bool dimmed);
-  bool dimmed() const { return dimmed_; }
-
   const gfx::Rect& target_bounds() const { return target_bounds_; }
 
   // Stacks the |item_widget_| in the correct place. |item_widget_| may be
@@ -172,20 +167,6 @@
   // If the window item represents a minimized window, update its content view.
   void UpdateItemContentViewForMinimizedWindow();
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
-  // aura::WindowObserver:
-  void OnWindowBoundsChanged(aura::Window* window,
-                             const gfx::Rect& old_bounds,
-                             const gfx::Rect& new_bounds,
-                             ui::PropertyChangeReason reason) override;
-  void OnWindowDestroying(aura::Window* window) override;
-  void OnWindowTitleChanged(aura::Window* window) override;
-
-  // ui::ImplicitAnimationObserver:
-  void OnImplicitAnimationsCompleted() override;
-
   // Handle the mouse/gesture event and facilitate dragging the item.
   void HandlePressEvent(const gfx::Point& location_in_screen);
   void HandleReleaseEvent(const gfx::Point& location_in_screen);
@@ -221,6 +202,25 @@
   void SetOpacity(float opacity);
   float GetOpacity();
 
+  OverviewAnimationType GetExitOverviewAnimationType();
+  OverviewAnimationType GetExitTransformAnimationType();
+
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // aura::WindowObserver:
+  void OnWindowBoundsChanged(aura::Window* window,
+                             const gfx::Rect& old_bounds,
+                             const gfx::Rect& new_bounds,
+                             ui::PropertyChangeReason reason) override;
+  void OnWindowDestroying(aura::Window* window) override;
+  void OnWindowTitleChanged(aura::Window* window) override;
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override;
+
+  WindowGrid* window_grid() { return window_grid_; }
+
   void set_should_animate_when_entering(bool should_animate) {
     should_animate_when_entering_ = should_animate;
   }
@@ -235,11 +235,6 @@
     return should_animate_when_exiting_;
   }
 
-  OverviewAnimationType GetExitOverviewAnimationType();
-  OverviewAnimationType GetExitTransformAnimationType();
-
-  WindowGrid* window_grid() { return window_grid_; }
-
   void set_should_restack_on_animation_end(bool val) {
     should_restack_on_animation_end_ = val;
   }
@@ -283,10 +278,6 @@
   // it visible while dragging around.
   void StartDrag();
 
-  // True if the item is being shown in the overview, false if it's being
-  // filtered.
-  bool dimmed_ = false;
-
   // The root window this item is being displayed on.
   aura::Window* root_window_;
 
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index b748b80..6b8d991 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -24,7 +24,6 @@
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shell.h"
-#include "ash/shell_test_api.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/caption_container_view.h"
@@ -39,7 +38,6 @@
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_divider.h"
-#include "ash/wm/splitview/split_view_drag_indicators.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -66,8 +64,6 @@
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
-#include "ui/events/test/events_test_utils.h"
-#include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/transform.h"
 #include "ui/gfx/transform_util.h"
@@ -314,14 +310,6 @@
     return ws->grid_list_[ws->selected_grid_index_]->is_selecting();
   }
 
-  bool showing_filter_widget() {
-    return window_selector()
-        ->text_filter_widget_->GetNativeWindow()
-        ->layer()
-        ->GetTargetTransform()
-        .IsIdentity();
-  }
-
   views::Widget* GetCloseButton(WindowSelectorItem* window) {
     return window->caption_container_view_->GetCloseButton()->GetWidget();
   }
@@ -348,14 +336,6 @@
             GetCloseButton(window_item)->GetNativeView())));
   }
 
-  void FilterItems(const base::StringPiece& pattern) {
-    window_selector()->ContentsChanged(nullptr, base::UTF8ToUTF16(pattern));
-  }
-
-  views::Widget* text_filter_widget() {
-    return window_selector()->text_filter_widget_.get();
-  }
-
   void SetGridBounds(WindowGrid* grid, const gfx::Rect& bounds) {
     grid->bounds_ = bounds;
   }
@@ -390,37 +370,6 @@
   DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest);
 };
 
-// Tests that the text field in the overview menu is repositioned and resized
-// after a screen rotation.
-TEST_F(WindowSelectorTest, OverviewScreenRotation) {
-  const gfx::Rect bounds(400, 300);
-  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
-
-  // In overview mode the windows should no longer overlap and the text filter
-  // widget should be focused.
-  ToggleOverview();
-
-  views::Widget* text_filter = text_filter_widget();
-  UpdateDisplay("400x300");
-
-  // The text filter position is calculated as:
-  // x: 0.5 * (total_bounds.width() -
-  //           std::min(kTextFilterWidth, total_bounds.width())).
-  // y: -kTextFilterHeight (since there's no text in the filter) - 2.
-  // w: std::min(kTextFilterWidth, total_bounds.width()).
-  // h: kTextFilterHeight.
-  gfx::Rect expected_bounds(60, -34, 280, 32);
-  EXPECT_EQ(expected_bounds, text_filter->GetClientAreaBoundsInScreen());
-
-  // Rotates the display, which triggers the WindowSelector's
-  // RepositionTextFilterOnDisplayMetricsChange method.
-  UpdateDisplay("400x300/r");
-
-  // Uses the same formulas as above using width = 300, height = 400.
-  expected_bounds = gfx::Rect(10, -34, 280, 32);
-  EXPECT_EQ(expected_bounds, text_filter->GetClientAreaBoundsInScreen());
-}
-
 // Tests that an a11y alert is sent on entering overview mode.
 TEST_F(WindowSelectorTest, A11yAlertOnOverviewMode) {
   const gfx::Rect bounds(400, 400);
@@ -468,10 +417,11 @@
   // Hide the cursor before entering overview to test that it will be shown.
   aura::client::GetCursorClient(root_window)->HideCursor();
 
-  // In overview mode the windows should no longer overlap and the text filter
-  // widget should be focused.
+  // In overview mode the windows should no longer overlap and the overview
+  // focus window should be focused.
   ToggleOverview();
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
+  EXPECT_EQ(window_selector()->GetOverviewFocusWindow(),
+            wm::GetFocusedWindow());
   EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
 
   // Clicking window 1 should activate it.
@@ -520,64 +470,6 @@
   EXPECT_EQ(mojom::WindowStateType::NORMAL, window_state->GetStateType());
 }
 
-// Tests that entering overview mode with an App-list active properly focuses
-// and activates the overview text filter window.
-TEST_F(WindowSelectorTest, TextFilterActive) {
-  const gfx::Rect bounds(400, 400);
-  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
-  wm::ActivateWindow(window.get());
-
-  EXPECT_TRUE(wm::IsActiveWindow(window.get()));
-  EXPECT_EQ(window.get(), wm::GetFocusedWindow());
-
-  // Pass an enum to satisfy the function, it is arbitrary and will not affect
-  // histograms.
-  GetAppListTestHelper()->ToggleAndRunLoop(GetPrimaryDisplay().id(),
-                                           app_list::kShelfButton);
-
-  // Activating overview cancels the App-list which normally would activate the
-  // previously active |window1|. Overview mode should properly transfer focus
-  // and activation to the text filter widget.
-  ToggleOverview();
-  EXPECT_FALSE(wm::IsActiveWindow(window.get()));
-  EXPECT_TRUE(wm::IsActiveWindow(wm::GetFocusedWindow()));
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
-}
-
-// Verifies the whether overview mode is still active after the text filter
-// window loses activation in certain circumstances.
-TEST_F(WindowSelectorTest, TextFilterDeactivated) {
-  UpdateDisplay("600x600");
-  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
-
-  const gfx::Rect bounds(400, 400);
-  std::unique_ptr<aura::Window> window(CreateWindow(bounds));
-  wm::ActivateWindow(window.get());
-
-  // Click somewhere on the screen not on the shelf and not on the overview
-  // window. This will cause a activation change which should close overview
-  // mode.
-  ToggleOverview();
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
-                                     gfx::Point(500, 400));
-  generator.ClickLeftButton();
-  ASSERT_FALSE(IsSelecting());
-
-  // Click somewhere on the screen not on the shelf and not on the overview
-  // window. This will cause a activation change but will not close overview
-  // mode since a overview to home launcher drag is underway.
-  ToggleOverview();
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
-  Shell::Get()
-      ->app_list_controller()
-      ->home_launcher_gesture_handler()
-      ->OnPressEvent(HomeLauncherGestureHandler::Mode::kSlideUpToShow,
-                     gfx::Point());
-  generator.ClickLeftButton();
-  EXPECT_TRUE(IsSelecting());
-}
-
 // Tests that the ordering of windows is stable across different overview
 // sessions even when the windows have the same bounds.
 TEST_F(WindowSelectorTest, WindowsOrder) {
@@ -617,7 +509,8 @@
   wm::ActivateWindow(window1.get());
   EXPECT_EQ(window1.get(), wm::GetFocusedWindow());
   ToggleOverview();
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
+  EXPECT_EQ(window_selector()->GetOverviewFocusWindow(),
+            wm::GetFocusedWindow());
   ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                      window2.get());
   generator.GestureTapAt(
@@ -1071,7 +964,7 @@
 }
 
 // Tests that a newly created window aborts overview.
-TEST_F(WindowSelectorTest, NewWindowCancelsOveriew) {
+TEST_F(WindowSelectorTest, NewWindowCancelsOverview) {
   const gfx::Rect bounds(400, 400);
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
   std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
@@ -1084,7 +977,7 @@
 }
 
 // Tests that a window activation exits overview mode.
-TEST_F(WindowSelectorTest, ActivationCancelsOveriew) {
+TEST_F(WindowSelectorTest, ActivationCancelsOverview) {
   const gfx::Rect bounds(400, 400);
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
   std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
@@ -1109,9 +1002,10 @@
   wm::ActivateWindow(window.get());
   EXPECT_EQ(window.get(), wm::GetFocusedWindow());
 
-  // In overview mode, the text filter widget should be focused.
+  // In overview mode, the overview focus window should be focused.
   ToggleOverview();
-  EXPECT_EQ(text_filter_widget()->GetNativeWindow(), wm::GetFocusedWindow());
+  EXPECT_EQ(window_selector()->GetOverviewFocusWindow(),
+            wm::GetFocusedWindow());
 
   // If canceling overview mode, focus should be restored.
   ToggleOverview();
@@ -1450,13 +1344,15 @@
 }
 
 // Regression test for clusterfuzz crash. https://crbug.com/920568
-TEST_F(WindowSelectorTest, FilterThenPressEscapeTwice) {
+TEST_F(WindowSelectorTest, TypeThenPressEscapeTwice) {
   std::unique_ptr<aura::Window> window(CreateTestWindow());
   ToggleOverview();
 
-  // Type some characters to show the filter list.
-  FilterItems("abc");
-  EXPECT_TRUE(showing_filter_widget());
+  // Type some characters.
+  SendKey(ui::VKEY_A);
+  SendKey(ui::VKEY_B);
+  SendKey(ui::VKEY_C);
+  EXPECT_TRUE(window_selector()->GetOverviewFocusWindow());
 
   // Pressing escape twice should not crash.
   SendKey(ui::VKEY_ESCAPE);
@@ -1585,136 +1481,6 @@
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
 }
 
-// Creates three windows and tests filtering them by title.
-TEST_F(WindowSelectorTest, BasicTextFiltering) {
-  gfx::Rect bounds(0, 0, 100, 100);
-  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window0(CreateWindow(bounds));
-  base::string16 window2_title = base::UTF8ToUTF16("Highway to test");
-  base::string16 window1_title = base::UTF8ToUTF16("For those about to test");
-  base::string16 window0_title = base::UTF8ToUTF16("We salute you");
-  window0->SetTitle(window0_title);
-  window1->SetTitle(window1_title);
-  window2->SetTitle(window2_title);
-  ToggleOverview();
-
-  EXPECT_FALSE(selection_widget_active());
-  EXPECT_FALSE(showing_filter_widget());
-  FilterItems("Test");
-
-  // The selection widget should appear when filtering starts, and should be
-  // selecting one of the matching windows above.
-  EXPECT_TRUE(selection_widget_active());
-  EXPECT_TRUE(showing_filter_widget());
-  // window0 does not contain the text "test".
-  EXPECT_NE(GetSelectedWindow(), window0.get());
-
-  // Window 0 has no "test" on it so it should be the only dimmed item.
-  const int grid_index = 0;
-  EXPECT_TRUE(GetWindowItemForWindow(grid_index, window0.get())->dimmed());
-  EXPECT_FALSE(GetWindowItemForWindow(grid_index, window1.get())->dimmed());
-  EXPECT_FALSE(GetWindowItemForWindow(grid_index, window2.get())->dimmed());
-
-  // No items match the search.
-  FilterItems("I'm testing 'n testing");
-  EXPECT_TRUE(GetWindowItemForWindow(grid_index, window0.get())->dimmed());
-  EXPECT_TRUE(GetWindowItemForWindow(grid_index, window1.get())->dimmed());
-  EXPECT_TRUE(GetWindowItemForWindow(grid_index, window2.get())->dimmed());
-
-  // All the items should match the empty string. The filter widget should also
-  // disappear.
-  FilterItems("");
-  EXPECT_FALSE(showing_filter_widget());
-  EXPECT_FALSE(GetWindowItemForWindow(grid_index, window0.get())->dimmed());
-  EXPECT_FALSE(GetWindowItemForWindow(grid_index, window1.get())->dimmed());
-  EXPECT_FALSE(GetWindowItemForWindow(grid_index, window2.get())->dimmed());
-
-  FilterItems("Foo");
-
-  EXPECT_NE(1.0f, window0->layer()->GetTargetOpacity());
-  EXPECT_NE(1.0f, window1->layer()->GetTargetOpacity());
-  EXPECT_NE(1.0f, window2->layer()->GetTargetOpacity());
-
-  ToggleOverview();
-
-  EXPECT_EQ(1.0f, window0->layer()->GetTargetOpacity());
-  EXPECT_EQ(1.0f, window1->layer()->GetTargetOpacity());
-  EXPECT_EQ(1.0f, window2->layer()->GetTargetOpacity());
-}
-
-// Tests selecting in the overview with dimmed and undimmed items.
-TEST_F(WindowSelectorTest, TextFilteringSelection) {
-  gfx::Rect bounds(0, 0, 100, 100);
-  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window0(CreateWindow(bounds));
-  base::string16 window2_title = base::UTF8ToUTF16("Rock and roll");
-  base::string16 window1_title = base::UTF8ToUTF16("Rock and");
-  base::string16 window0_title = base::UTF8ToUTF16("Rock");
-  window0->SetTitle(window0_title);
-  window1->SetTitle(window1_title);
-  window2->SetTitle(window2_title);
-  ToggleOverview();
-  EXPECT_TRUE(SelectWindow(window0.get()));
-  EXPECT_TRUE(selection_widget_active());
-
-  // Dim the first item, the selection should jump to the next item.
-  FilterItems("Rock and");
-  EXPECT_NE(GetSelectedWindow(), window0.get());
-
-  // Cycle the selection, the dimmed window should not be selected.
-  EXPECT_FALSE(SelectWindow(window0.get()));
-
-  // Dimming all the items should hide the selection widget.
-  FilterItems("Pop");
-  EXPECT_FALSE(selection_widget_active());
-
-  // Undimming one window should automatically select it.
-  FilterItems("Rock and roll");
-  EXPECT_EQ(GetSelectedWindow(), window2.get());
-}
-
-// Tests that transferring focus from the text filter to a window that is not a
-// top level window does not cancel overview mode.
-TEST_F(WindowSelectorTest, ShowTextFilterMenu) {
-  gfx::Rect bounds(0, 0, 100, 100);
-  std::unique_ptr<aura::Window> window0(CreateWindow(bounds));
-  base::string16 window0_title = base::UTF8ToUTF16("Test");
-  window0->SetTitle(window0_title);
-  wm::GetWindowState(window0.get())->Minimize();
-  ToggleOverview();
-
-  EXPECT_FALSE(selection_widget_active());
-  EXPECT_FALSE(showing_filter_widget());
-  FilterItems("Test");
-
-  EXPECT_TRUE(selection_widget_active());
-  EXPECT_TRUE(showing_filter_widget());
-
-  // Open system bubble shifting focus from the text filter.
-  GetPrimaryUnifiedSystemTray()->ShowBubble(false /* show_by_click */);
-
-  base::RunLoop().RunUntilIdle();
-
-  // This should not cancel overview mode.
-  ASSERT_TRUE(IsSelecting());
-  EXPECT_TRUE(selection_widget_active());
-  EXPECT_TRUE(showing_filter_widget());
-
-  // Click text filter to bring focus back.
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  gfx::Point point_in_text_filter =
-      text_filter_widget()->GetWindowBoundsInScreen().CenterPoint();
-  generator->MoveMouseTo(point_in_text_filter);
-  generator->ClickLeftButton();
-  EXPECT_TRUE(IsSelecting());
-
-  // Cancel overview mode.
-  ToggleOverview();
-  ASSERT_FALSE(IsSelecting());
-}
-
 // Tests clicking on the desktop itself to cancel overview mode.
 TEST_F(WindowSelectorTest, CancelOverviewOnMouseClick) {
   // Overview disabled by default.
@@ -1864,6 +1630,7 @@
   ToggleOverview();
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(window_selector());
+  ASSERT_EQ(3u, grids().size());
   EXPECT_TRUE(grids()[0]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[1]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[2]->IsNoItemsIndicatorLabelVisibleForTesting());
@@ -1886,7 +1653,7 @@
   ToggleOverview();
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(window_selector());
-  EXPECT_EQ(3u, grids().size());
+  ASSERT_EQ(3u, grids().size());
   EXPECT_FALSE(grids()[0]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[1]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[2]->IsNoItemsIndicatorLabelVisibleForTesting());
@@ -1902,8 +1669,8 @@
   // is still open. The non primary root grids are empty however.
   item2->CloseWindow();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(window_selector());
-  EXPECT_EQ(3u, grids().size());
+  ASSERT_TRUE(window_selector());
+  ASSERT_EQ(3u, grids().size());
   EXPECT_FALSE(grids()[0]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[1]->IsNoItemsIndicatorLabelVisibleForTesting());
   EXPECT_FALSE(grids()[2]->IsNoItemsIndicatorLabelVisibleForTesting());
@@ -1918,16 +1685,6 @@
   EXPECT_FALSE(window_selector());
 }
 
-// Verify that pressing and releasing keys does not show the overview textbox
-// when there are no windows opened.
-TEST_F(WindowSelectorTest, TextfilterHiddenWhenNoWindows) {
-  ToggleOverview();
-  ASSERT_TRUE(window_selector());
-
-  SendKey(ui::VKEY_J);
-  EXPECT_FALSE(showing_filter_widget());
-}
-
 // Tests window list animation states are correctly updated.
 TEST_F(WindowSelectorTest, SetWindowListAnimationStates) {
   const gfx::Rect bounds(400, 400);
@@ -2608,24 +2365,6 @@
   ToggleOverview();
 }
 
-// Verify that the system does not crash when exiting overview mode after
-// pressing CTRL+SHIFT+U.
-TEST_F(WindowSelectorTest, ExitInUnderlineMode) {
-  std::unique_ptr<aura::Window> window(
-      CreateWindow(gfx::Rect(10, 10, 200, 200)));
-
-  ToggleOverview();
-
-  // Enter underline mode on the text selector by generating CTRL+SHIFT+U
-  // sequence.
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->PressKey(ui::VKEY_U, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
-  generator->ReleaseKey(ui::VKEY_U, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
-
-  // Test that leaving overview mode cleans up properly.
-  ToggleOverview();
-}
-
 // Tests that the shadows in overview mode are placed correctly.
 TEST_F(WindowSelectorTest, ShadowBounds) {
   // Helper function to check if the bounds of a shadow owned by |shadow_parent|
@@ -2785,8 +2524,7 @@
 
 // Verify that PIP windows will be excluded from the overview, but not hidden.
 TEST_F(WindowSelectorTest, PipWindowShownButExcludedFromOverview) {
-  const gfx::Rect bounds(200, 200);
-  std::unique_ptr<aura::Window> pip_window(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> pip_window(CreateWindow(gfx::Rect(200, 200)));
 
   wm::WindowState* window_state = wm::GetWindowState(pip_window.get());
   const wm::WMEvent enter_pip(wm::WM_EVENT_PIP);
@@ -2963,6 +2701,11 @@
         ->split_view_divider_->GetDividerBoundsInScreen(is_dragging);
   }
 
+  gfx::Rect GetWorkAreaInScreen(aura::Window* window) {
+    return screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
+        window);
+  }
+
   // Drags a window selector item |item| from its center or one of its corners
   // to |end_location|. This should be used over
   // DragWindowTo(WindowSelectorItem*, gfx::Point) when testing snapping a
@@ -3024,23 +2767,6 @@
     return window;
   }
 
-  IndicatorState indicator_state() {
-    DCHECK(window_selector());
-    return window_selector()
-        ->split_view_drag_indicators()
-        ->current_indicator_state();
-  }
-
-  int GetEdgeInset(int screen_width) const {
-    return screen_width * kHighlightScreenPrimaryAxisRatio +
-           kHighlightScreenEdgePaddingDp;
-  }
-
-  bool IsPreviewAreaShowing() {
-    return indicator_state() == IndicatorState::kPreviewAreaLeft ||
-           indicator_state() == IndicatorState::kPreviewAreaRight;
-  }
-
  private:
   class SplitViewTestWindowDelegate : public aura::test::TestWindowDelegate {
    public:
@@ -3096,10 +2822,7 @@
   // Drag |window3| selector item to snap to right.
   WindowSelectorItem* selector_item3 =
       GetWindowItemForWindow(grid_index, window3.get());
-  const gfx::Rect work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
-  const gfx::Point end_location3(work_area_rect.width(), 0);
+  const gfx::Point end_location3(GetWorkAreaInScreen(window3.get()).width(), 0);
   DragWindowTo(selector_item3, end_location3);
 
   EXPECT_EQ(split_view_controller()->state(),
@@ -3108,90 +2831,6 @@
   EXPECT_FALSE(window_selector_controller()->IsSelecting());
 }
 
-TEST_F(SplitViewWindowSelectorTest, Dragging) {
-  Shell::Get()->aura_env()->set_throttle_input_on_resize_for_testing(false);
-
-  ui::test::EventGenerator* generator = GetEventGenerator();
-
-  std::unique_ptr<aura::Window> right_window = CreateTestWindow();
-  std::unique_ptr<aura::Window> left_window = CreateTestWindow();
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  WindowSelectorItem* left_selector_item =
-      GetWindowItemForWindow(0, left_window.get());
-  WindowSelectorItem* right_selector_item =
-      GetWindowItemForWindow(0, right_window.get());
-
-  // The inset on each side of the screen which is a snap region. Items dragged
-  // to and released under this region will get snapped.
-  const int drag_offset = 5;
-  const int drag_offset_snap_region = 48;
-  const int minimum_drag_offset = 96;
-  const int screen_width =
-      screen_util::GetDisplayWorkAreaBoundsInParent(left_window.get()).width();
-  const int edge_inset = GetEdgeInset(screen_width);
-  // The selector item has a margin which does not accept events. Inset any
-  // event aimed at the selector items edge so events will reach it.
-  const int selector_item_inset = 20;
-
-  // Check the two windows set up have a region which is under no snap region, a
-  // region that is under the left snap region and a region that is under the
-  // right snap region.
-  ASSERT_GT(left_selector_item->target_bounds().CenterPoint().x(), edge_inset);
-  ASSERT_LT(
-      left_selector_item->target_bounds().origin().x() + selector_item_inset,
-      edge_inset);
-  ASSERT_GT(right_selector_item->target_bounds().right() - selector_item_inset,
-            screen_width - edge_inset);
-
-  // Verify if the drag is not started in either snap region, the drag still
-  // must move by |drag_offset| before split view acknowledges the drag (ie.
-  // starts moving the selector item).
-  generator->set_current_screen_location(
-      left_selector_item->target_bounds().CenterPoint());
-  generator->PressLeftButton();
-  const gfx::Rect left_original_bounds = left_selector_item->target_bounds();
-  generator->MoveMouseBy(drag_offset - 1, 0);
-  EXPECT_EQ(left_original_bounds, left_selector_item->target_bounds());
-  generator->MoveMouseBy(1, 0);
-  EXPECT_NE(left_original_bounds, left_selector_item->target_bounds());
-  generator->ReleaseLeftButton();
-
-  // Verify if the drag is started in the left snap region, the drag needs to
-  // move by |drag_offset_snap_region| towards the right side of the screen
-  // before split view acknowledges the drag (shows the preview area).
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-  generator->set_current_screen_location(gfx::Point(
-      left_selector_item->target_bounds().origin().x() + selector_item_inset,
-      left_selector_item->target_bounds().CenterPoint().y()));
-  generator->PressLeftButton();
-  generator->MoveMouseBy(-drag_offset, 0);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  generator->MoveMouseBy(drag_offset_snap_region, 0);
-  generator->MoveMouseBy(-minimum_drag_offset, 0);
-  EXPECT_TRUE(IsPreviewAreaShowing());
-  // Drag back to the middle before releasing so that we stay in overview mode
-  // on release.
-  generator->MoveMouseTo(left_original_bounds.CenterPoint());
-  generator->ReleaseLeftButton();
-
-  // Verify if the drag is started in the right snap region, the drag needs to
-  // move by |drag_offset_snap_region| towards the left side of the screen
-  // before split view acknowledges the drag.
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-  generator->set_current_screen_location(gfx::Point(
-      right_selector_item->target_bounds().right() - selector_item_inset,
-      right_selector_item->target_bounds().CenterPoint().y()));
-  generator->PressLeftButton();
-  generator->MoveMouseBy(drag_offset, 0);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  generator->MoveMouseBy(-drag_offset_snap_region, 0);
-  generator->MoveMouseBy(minimum_drag_offset, 0);
-  EXPECT_TRUE(IsPreviewAreaShowing());
-}
-
 // Verify the correct behavior when dragging windows in overview mode.
 TEST_F(SplitViewWindowSelectorTest, OverviewDragControllerBehavior) {
   Shell::Get()->aura_env()->set_throttle_input_on_resize_for_testing(false);
@@ -3483,9 +3122,7 @@
   const gfx::Point center(window_width / 2, 0);
   window_selector()->Drag(selector_item, center);
   EXPECT_EQ(SplitViewController::NO_SNAP, split_view_controller()->state());
-  EXPECT_EQ(screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-                window1.get()),
-            GetGridBounds());
+  EXPECT_EQ(GetWorkAreaInScreen(window1.get()), GetGridBounds());
 
   // Snap window1 to the left and initialize dragging for window2.
   window_selector()->Drag(selector_item, left);
@@ -3620,255 +3257,6 @@
   EXPECT_FALSE(window_selector_controller()->IsSelecting());
 }
 
-// Verify the split view preview area becomes visible when expected.
-TEST_F(SplitViewWindowSelectorTest, PreviewAreaVisibility) {
-  std::unique_ptr<aura::Window> window = CreateTestWindow();
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  const int screen_width =
-      screen_util::GetDisplayWorkAreaBoundsInParent(window.get()).width();
-  const int edge_inset = GetEdgeInset(screen_width);
-
-  // Verify the preview area is visible when |selector_item|'s x is in the
-  // range [0, edge_inset] or [screen_width - edge_inset - 1, screen_width].
-  const int grid_index = 0;
-  WindowSelectorItem* selector_item =
-      GetWindowItemForWindow(grid_index, window.get());
-  const gfx::Point start_location(selector_item->target_bounds().CenterPoint());
-  // Drag horizontally to avoid activating drag to close.
-  const int y = start_location.y();
-  window_selector()->InitiateDrag(selector_item, start_location);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  window_selector()->Drag(selector_item, gfx::Point(edge_inset + 1, y));
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  window_selector()->Drag(selector_item, gfx::Point(edge_inset, y));
-  EXPECT_TRUE(IsPreviewAreaShowing());
-
-  window_selector()->Drag(selector_item,
-                          gfx::Point(screen_width - edge_inset - 2, y));
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  window_selector()->Drag(selector_item,
-                          gfx::Point(screen_width - edge_inset - 1, y));
-  EXPECT_TRUE(IsPreviewAreaShowing());
-
-  // Drag back to |start_location| before compeleting the drag, otherwise
-  // |selector_time| will snap to the right and the system will enter splitview,
-  // making |window_drag_controller()| nullptr.
-  window_selector()->Drag(selector_item, start_location);
-  window_selector()->CompleteDrag(selector_item, start_location);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-}
-
-// Verify that the preview area never shows up when dragging a unsnappable
-// window.
-TEST_F(SplitViewWindowSelectorTest, PreviewAreaVisibilityUnsnappableWindow) {
-  std::unique_ptr<aura::Window> window = CreateUnsnappableWindow();
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  const int screen_width =
-      screen_util::GetDisplayWorkAreaBoundsInParent(window.get()).width();
-
-  const int grid_index = 0;
-  WindowSelectorItem* selector_item =
-      GetWindowItemForWindow(grid_index, window.get());
-  const gfx::Point start_location(selector_item->target_bounds().CenterPoint());
-  window_selector()->InitiateDrag(selector_item, start_location);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  window_selector()->Drag(selector_item, gfx::Point(0, 1));
-  EXPECT_FALSE(IsPreviewAreaShowing());
-  window_selector()->Drag(selector_item, gfx::Point(screen_width, 1));
-  EXPECT_FALSE(IsPreviewAreaShowing());
-
-  window_selector()->CompleteDrag(selector_item, start_location);
-  EXPECT_FALSE(IsPreviewAreaShowing());
-}
-
-// Verify that the split view overview overlay has the expected state.
-TEST_F(SplitViewWindowSelectorTest, SplitViewDragIndicatorsState) {
-  const gfx::Rect bounds(400, 400);
-  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
-  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  const int screen_width =
-      screen_util::GetDisplayWorkAreaBoundsInParent(window1.get()).width();
-  const int edge_inset = GetEdgeInset(screen_width);
-
-  // Verify that when are no snapped windows, the indicator is visible once
-  // there is a long press or after the drag has started.
-  const int grid_index = 0;
-  WindowSelectorItem* selector_item =
-      GetWindowItemForWindow(grid_index, window1.get());
-  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
-  window_selector()->InitiateDrag(selector_item, start_location);
-  EXPECT_EQ(IndicatorState::kNone, indicator_state());
-  window_selector()->StartSplitViewDragMode(start_location);
-  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
-
-  // Reset the gesture so we stay in overview mode.
-  window_selector()->ResetDraggedWindowGesture();
-
-  // Verify the indicator is visible once the item starts moving, and becomes a
-  // preview area once we reach the left edge of the screen. Drag horizontal to
-  // avoid activating drag to close.
-  const int y_position = start_location.y();
-  window_selector()->InitiateDrag(selector_item, start_location);
-  EXPECT_EQ(IndicatorState::kNone, indicator_state());
-  window_selector()->Drag(selector_item,
-                          gfx::Point(edge_inset + 1, y_position));
-  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
-  window_selector()->Drag(selector_item, gfx::Point(edge_inset, y_position));
-  EXPECT_EQ(IndicatorState::kPreviewAreaLeft, indicator_state());
-
-  // Snap window to the left.
-  window_selector()->CompleteDrag(selector_item,
-                                  gfx::Point(edge_inset, y_position));
-  ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive());
-  ASSERT_EQ(SplitViewController::LEFT_SNAPPED,
-            split_view_controller()->state());
-
-  // Verify that when there is a left snapped window, dragging an item to the
-  // right will show the right preview area.
-  selector_item = GetWindowItemForWindow(grid_index, window2.get());
-  start_location = selector_item->target_bounds().CenterPoint();
-  window_selector()->InitiateDrag(selector_item, start_location);
-  EXPECT_EQ(IndicatorState::kNone, indicator_state());
-  window_selector()->Drag(selector_item,
-                          gfx::Point(screen_width - 1, y_position));
-  EXPECT_EQ(IndicatorState::kPreviewAreaRight, indicator_state());
-  window_selector()->CompleteDrag(selector_item, start_location);
-}
-
-// Verify that the split view drag indicator is shown when expected when
-// attempting to drag a unsnappable window.
-TEST_F(SplitViewWindowSelectorTest,
-       SplitViewDragIndicatorVisibilityUnsnappableWindow) {
-  std::unique_ptr<aura::Window> unsnappable_window = CreateUnsnappableWindow();
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  const int grid_index = 0;
-  WindowSelectorItem* selector_item =
-      GetWindowItemForWindow(grid_index, unsnappable_window.get());
-  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
-  window_selector()->InitiateDrag(selector_item, start_location);
-  window_selector()->StartSplitViewDragMode(start_location);
-  EXPECT_EQ(IndicatorState::kCannotSnap, indicator_state());
-  const gfx::Point end_location1(0, 0);
-  window_selector()->Drag(selector_item, end_location1);
-  EXPECT_EQ(IndicatorState::kCannotSnap, indicator_state());
-  window_selector()->CompleteDrag(selector_item, end_location1);
-  EXPECT_EQ(IndicatorState::kNone, indicator_state());
-}
-
-// Verify when the split view drag indicators state changes, the expected
-// indicators will become visible or invisible.
-TEST_F(SplitViewWindowSelectorTest, SplitViewDragIndicatorsVisibility) {
-  auto indicator = std::make_unique<SplitViewDragIndicators>();
-
-  auto to_int = [](IndicatorType type) { return static_cast<int>(type); };
-
-  // Helper function to which checks that all indicator types passed in |mask|
-  // are visible, and those that are not are not visible.
-  auto check_helper = [](SplitViewDragIndicators* svdi, int mask) {
-    const std::vector<IndicatorType> types = {
-        IndicatorType::kLeftHighlight, IndicatorType::kLeftText,
-        IndicatorType::kRightHighlight, IndicatorType::kRightText};
-    for (auto type : types) {
-      if ((static_cast<int>(type) & mask) > 0)
-        EXPECT_TRUE(svdi->GetIndicatorTypeVisibilityForTesting(type));
-      else
-        EXPECT_FALSE(svdi->GetIndicatorTypeVisibilityForTesting(type));
-    }
-  };
-
-  // Check each state has the correct views displayed. Pass and empty point as
-  // the location since there is no need to reparent the widget. Verify that
-  // nothing is shown in the none state.
-  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
-  check_helper(indicator.get(), 0);
-
-  const int all = to_int(IndicatorType::kLeftHighlight) |
-                  to_int(IndicatorType::kLeftText) |
-                  to_int(IndicatorType::kRightHighlight) |
-                  to_int(IndicatorType::kRightText);
-  // Verify that everything is visible in the dragging and cannot snap states.
-  indicator->SetIndicatorState(IndicatorState::kDragArea, gfx::Point());
-  check_helper(indicator.get(), all);
-  indicator->SetIndicatorState(IndicatorState::kCannotSnap, gfx::Point());
-  check_helper(indicator.get(), all);
-
-  // Verify that only one highlight shows up for the preview area states.
-  indicator->SetIndicatorState(IndicatorState::kPreviewAreaLeft, gfx::Point());
-  check_helper(indicator.get(), to_int(IndicatorType::kLeftHighlight));
-  indicator->SetIndicatorState(IndicatorState::kPreviewAreaRight, gfx::Point());
-  check_helper(indicator.get(), to_int(IndicatorType::kRightHighlight));
-}
-
-// Verify that the split view drag indicators widget reparents when starting a
-// drag on a different display.
-TEST_F(SplitViewWindowSelectorTest, SplitViewDragIndicatorsWidgetReparenting) {
-  // Add two displays and one window on each display.
-  UpdateDisplay("600x600,600x600");
-  // DisplayConfigurationObserver enables mirror mode when tablet mode is
-  // enabled. Disable mirror mode to test multiple displays.
-  display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
-  base::RunLoop().RunUntilIdle();
-
-  auto root_windows = Shell::Get()->GetAllRootWindows();
-  ASSERT_EQ(2u, root_windows.size());
-
-  const gfx::Rect primary_screen_bounds(0, 0, 600, 600);
-  const gfx::Rect secondary_screen_bounds(600, 0, 600, 600);
-  std::unique_ptr<aura::Window> primary_screen_window(
-      CreateWindow(primary_screen_bounds));
-  std::unique_ptr<aura::Window> secondary_screen_window(
-      CreateWindow(secondary_screen_bounds));
-
-  ToggleOverview();
-  ASSERT_TRUE(window_selector_controller()->IsSelecting());
-
-  // Select an item on the primary display and verify the drag indicators
-  // widget's parent is the primary root window.
-  WindowSelectorItem* selector_item =
-      GetWindowItemForWindow(0, primary_screen_window.get());
-  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
-  window_selector()->InitiateDrag(selector_item, start_location);
-  window_selector()->Drag(selector_item, gfx::Point(100, start_location.y()));
-  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
-  EXPECT_EQ(root_windows[0], window_selector()
-                                 ->split_view_drag_indicators()
-                                 ->widget_->GetNativeView()
-                                 ->GetRootWindow());
-  // Drag the item in a way that neither opens the window nor activates
-  // splitview mode.
-  window_selector()->Drag(selector_item, primary_screen_bounds.CenterPoint());
-  window_selector()->CompleteDrag(selector_item,
-                                  primary_screen_bounds.CenterPoint());
-  ASSERT_TRUE(window_selector());
-  ASSERT_FALSE(split_view_controller()->IsSplitViewModeActive());
-
-  // Select an item on the secondary display and verify the indicators widget
-  // has reparented to the secondary root window.
-  selector_item = GetWindowItemForWindow(1, secondary_screen_window.get());
-  start_location = gfx::Point(selector_item->target_bounds().CenterPoint());
-  window_selector()->InitiateDrag(selector_item, start_location);
-  window_selector()->Drag(selector_item, gfx::Point(800, start_location.y()));
-  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
-  EXPECT_EQ(root_windows[1], window_selector()
-                                 ->split_view_drag_indicators()
-                                 ->widget_->GetNativeView()
-                                 ->GetRootWindow());
-  window_selector()->CompleteDrag(selector_item, start_location);
-}
-
 // Test the overview window drag functionalities when screen rotates.
 TEST_F(SplitViewWindowSelectorTest, SplitViewRotationTest) {
   using svc = SplitViewController;
@@ -3903,9 +3291,7 @@
   // Test that dragging |window2| to the right of the screen snaps it to right.
   WindowSelectorItem* selector_item2 =
       GetWindowItemForWindow(grid_index, window2.get());
-  gfx::Rect work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  gfx::Rect work_area_rect = GetWorkAreaInScreen(window2.get());
   gfx::Point end_location2(work_area_rect.width(), work_area_rect.height());
   DragWindowTo(selector_item2, end_location2);
   EXPECT_EQ(split_view_controller()->state(), svc::BOTH_SNAPPED);
@@ -3933,9 +3319,7 @@
 
   // Test that dragging |window2| to the bottom of the screen snaps it to right.
   selector_item2 = GetWindowItemForWindow(grid_index, window2.get());
-  work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  work_area_rect = GetWorkAreaInScreen(window2.get());
   end_location2 = gfx::Point(work_area_rect.width(), work_area_rect.height());
   DragWindowTo(selector_item2, end_location2, SelectorItemLocation::ORIGIN);
   EXPECT_EQ(split_view_controller()->state(), svc::BOTH_SNAPPED);
@@ -3963,9 +3347,7 @@
 
   // Test that dragging |window2| to the right of the screen snaps it to left.
   selector_item2 = GetWindowItemForWindow(grid_index, window2.get());
-  work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  work_area_rect = GetWorkAreaInScreen(window2.get());
   end_location2 = gfx::Point(work_area_rect.width(), work_area_rect.height());
   DragWindowTo(selector_item2, end_location2, SelectorItemLocation::ORIGIN);
   EXPECT_EQ(split_view_controller()->state(), svc::BOTH_SNAPPED);
@@ -3993,9 +3375,7 @@
 
   // Test that dragging |window2| to the bottom of the screen snaps it to left.
   selector_item2 = GetWindowItemForWindow(grid_index, window2.get());
-  work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  work_area_rect = GetWorkAreaInScreen(window2.get());
   end_location2 = gfx::Point(work_area_rect.width(), work_area_rect.height());
   DragWindowTo(selector_item2, end_location2);
   EXPECT_EQ(split_view_controller()->state(), svc::BOTH_SNAPPED);
@@ -4205,9 +3585,7 @@
 
   // Drag the divider toward closing the overview window grid.
   divider_bounds = GetSplitViewDividerBounds(false /*is_dragging=*/);
-  const gfx::Rect display_bounds =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  const gfx::Rect display_bounds = GetWorkAreaInScreen(window2.get());
   split_view_controller()->StartResize(divider_bounds.CenterPoint());
   split_view_controller()->EndResize(display_bounds.bottom_right());
 
@@ -4287,9 +3665,7 @@
   // Drag |window2| selector item to snap to right.
   WindowSelectorItem* selector_item2 =
       GetWindowItemForWindow(grid_index, window2.get());
-  const gfx::Rect work_area_rect =
-      screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer(
-          window2.get());
+  const gfx::Rect work_area_rect = GetWorkAreaInScreen(window2.get());
   gfx::Point end_location2 =
       gfx::Point(work_area_rect.width(), work_area_rect.height());
   DragWindowTo(selector_item2, end_location2);
diff --git a/ash/wm/property_util.cc b/ash/wm/property_util.cc
index eeaba30..2f50cbc 100644
--- a/ash/wm/property_util.cc
+++ b/ash/wm/property_util.cc
@@ -51,12 +51,6 @@
   return true;
 }
 
-bool ShouldRemoveStandardFrame(const InitProperties& properties) {
-  auto iter = properties.find(
-      ws::mojom::WindowManager::kRemoveStandardFrame_InitProperty);
-  return iter != properties.end() && mojo::ConvertTo<bool>(iter->second);
-}
-
 void ApplyProperties(
     aura::Window* window,
     aura::PropertyConverter* property_converter,
diff --git a/ash/wm/property_util.h b/ash/wm/property_util.h
index 3729c3f42c..3287b02 100644
--- a/ash/wm/property_util.h
+++ b/ash/wm/property_util.h
@@ -53,8 +53,6 @@
 
 bool GetWindowPreferredSize(const InitProperties& properties, gfx::Size* size);
 
-bool ShouldRemoveStandardFrame(const InitProperties& properties);
-
 // Applies |properties| to |window| using |property_converter|.
 void ApplyProperties(
     aura::Window* window,
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index 048412a..bfdfbde 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -629,14 +629,13 @@
                                             aura::Window* gained_active,
                                             aura::Window* lost_active) {
   // This may be called while SnapWindow is still underway because SnapWindow
-  // will end the overview start animations which will cause the overview text
-  // filter to be activated.
-  aura::Window* text_filter_widget =
-      GetWindowSelector()
-          ? GetWindowSelector()->text_filter_widget()->GetNativeWindow()
-          : nullptr;
+  // will end the overview start animations which will cause the overview focus
+  // window to be activated.
+  aura::Window* overview_focus_window =
+      GetWindowSelector() ? GetWindowSelector()->GetOverviewFocusWindow()
+                          : nullptr;
   DCHECK(IsSplitViewModeActive() ||
-         (text_filter_widget && text_filter_widget == gained_active));
+         (overview_focus_window && overview_focus_window == gained_active));
 
   // If |gained_active| was activated as a side effect of a window disposition
   // change, do nothing. For example, when a snapped window is closed, another
diff --git a/ash/wm/splitview/split_view_drag_indicators.h b/ash/wm/splitview/split_view_drag_indicators.h
index 5d2e151..e2f1710 100644
--- a/ash/wm/splitview/split_view_drag_indicators.h
+++ b/ash/wm/splitview/split_view_drag_indicators.h
@@ -93,7 +93,7 @@
   }
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(SplitViewWindowSelectorTest,
+  FRIEND_TEST_ALL_PREFIXES(SplitViewDragIndicatorsTest,
                            SplitViewDragIndicatorsWidgetReparenting);
   class RotatedImageLabelView;
   class SplitViewDragIndicatorsView;
diff --git a/ash/wm/splitview/split_view_drag_indicators_unittest.cc b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
new file mode 100644
index 0000000..ab1e15e
--- /dev/null
+++ b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
@@ -0,0 +1,410 @@
+// 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 "ash/wm/splitview/split_view_drag_indicators.h"
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/window_grid.h"
+#include "ash/wm/overview/window_selector.h"
+#include "ash/wm/overview/window_selector_controller.h"
+#include "ash/wm/overview/window_selector_item.h"
+#include "ash/wm/splitview/split_view_constants.h"
+#include "ash/wm/splitview/split_view_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "services/ws/public/mojom/window_tree_constants.mojom.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+class SplitViewDragIndicatorsTest : public AshTestBase {
+ public:
+  SplitViewDragIndicatorsTest() = default;
+  ~SplitViewDragIndicatorsTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    // Ensure calls to EnableTabletModeWindowManager complete.
+    base::RunLoop().RunUntilIdle();
+    Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void ToggleOverview() {
+    auto* window_selector_controller =
+        Shell::Get()->window_selector_controller();
+    window_selector_controller->ToggleOverview();
+    if (!window_selector_controller->IsSelecting()) {
+      window_selector_ = nullptr;
+      split_view_drag_indicators_ = nullptr;
+      return;
+    }
+
+    window_selector_ =
+        Shell::Get()->window_selector_controller()->window_selector();
+    ASSERT_TRUE(window_selector_);
+    split_view_drag_indicators_ =
+        window_selector_->split_view_drag_indicators();
+  }
+
+  SplitViewController* split_view_controller() {
+    return Shell::Get()->split_view_controller();
+  }
+
+  IndicatorState indicator_state() {
+    DCHECK(split_view_drag_indicators_);
+    return split_view_drag_indicators_->current_indicator_state();
+  }
+
+  bool IsPreviewAreaShowing() {
+    return indicator_state() == IndicatorState::kPreviewAreaLeft ||
+           indicator_state() == IndicatorState::kPreviewAreaRight;
+  }
+
+  WindowSelectorItem* GetOverviewItemForWindow(aura::Window* window,
+                                               int grid_index = 0) {
+    auto& windows =
+        window_selector_->grid_list_for_testing()[grid_index]->window_list();
+    auto iter =
+        std::find_if(windows.cbegin(), windows.cend(),
+                     [window](const std::unique_ptr<WindowSelectorItem>& item) {
+                       return item->Contains(window);
+                     });
+    if (iter == windows.end())
+      return nullptr;
+    return iter->get();
+  }
+
+  int GetEdgeInset(int screen_width) const {
+    return screen_width * kHighlightScreenPrimaryAxisRatio +
+           kHighlightScreenEdgePaddingDp;
+  }
+
+  // Creates a window which cannot be snapped by splitview.
+  std::unique_ptr<aura::Window> CreateUnsnappableWindow() {
+    std::unique_ptr<aura::Window> window(CreateTestWindow());
+    window->SetProperty(aura::client::kResizeBehaviorKey,
+                        ws::mojom::kResizeBehaviorNone);
+    return window;
+  }
+
+ protected:
+  SplitViewDragIndicators* split_view_drag_indicators_ = nullptr;
+  WindowSelector* window_selector_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SplitViewDragIndicatorsTest);
+};
+
+TEST_F(SplitViewDragIndicatorsTest, Dragging) {
+  Shell::Get()->aura_env()->set_throttle_input_on_resize_for_testing(false);
+  UpdateDisplay("800x600");
+  const int screen_width = 800;
+  const int edge_inset = GetEdgeInset(screen_width);
+  std::unique_ptr<aura::Window> right_window(CreateTestWindow());
+  std::unique_ptr<aura::Window> left_window(CreateTestWindow());
+  ui::test::EventGenerator* generator = GetEventGenerator();
+
+  ToggleOverview();
+  WindowSelectorItem* left_selector_item =
+      GetOverviewItemForWindow(left_window.get());
+  WindowSelectorItem* right_selector_item =
+      GetOverviewItemForWindow(right_window.get());
+
+  // The inset on each side of the screen which is a snap region. Items dragged
+  // to and released under this region will get snapped.
+  const int drag_offset = 5;
+  const int drag_offset_snap_region = 48;
+  const int minimum_drag_offset = 96;
+  // The selector item has a margin which does not accept events. Inset any
+  // event aimed at the selector items edge so events will reach it.
+  const int selector_item_inset = 20;
+
+  // Check the two windows set up have a region which is under no snap region, a
+  // region that is under the left snap region and a region that is under the
+  // right snap region.
+  ASSERT_GT(left_selector_item->target_bounds().CenterPoint().x(), edge_inset);
+  ASSERT_LT(
+      left_selector_item->target_bounds().origin().x() + selector_item_inset,
+      edge_inset);
+  ASSERT_GT(right_selector_item->target_bounds().right() - selector_item_inset,
+            screen_width - edge_inset);
+
+  // Verify if the drag is not started in either snap region, the drag still
+  // must move by |drag_offset| before split view acknowledges the drag (ie.
+  // starts moving the selector item).
+  generator->set_current_screen_location(
+      left_selector_item->target_bounds().CenterPoint());
+  generator->PressLeftButton();
+  const gfx::Rect left_original_bounds = left_selector_item->target_bounds();
+  generator->MoveMouseBy(drag_offset - 1, 0);
+  EXPECT_EQ(left_original_bounds, left_selector_item->target_bounds());
+  generator->MoveMouseBy(1, 0);
+  EXPECT_NE(left_original_bounds, left_selector_item->target_bounds());
+  generator->ReleaseLeftButton();
+
+  // Verify if the drag is started in the left snap region, the drag needs to
+  // move by |drag_offset_snap_region| towards the right side of the screen
+  // before split view acknowledges the drag (shows the preview area).
+  ASSERT_TRUE(Shell::Get()->window_selector_controller()->IsSelecting());
+  generator->set_current_screen_location(gfx::Point(
+      left_selector_item->target_bounds().origin().x() + selector_item_inset,
+      left_selector_item->target_bounds().CenterPoint().y()));
+  generator->PressLeftButton();
+  generator->MoveMouseBy(-drag_offset, 0);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  generator->MoveMouseBy(drag_offset_snap_region, 0);
+  generator->MoveMouseBy(-minimum_drag_offset, 0);
+  EXPECT_TRUE(IsPreviewAreaShowing());
+  // Drag back to the middle before releasing so that we stay in overview mode
+  // on release.
+  generator->MoveMouseTo(left_original_bounds.CenterPoint());
+  generator->ReleaseLeftButton();
+
+  // Verify if the drag is started in the right snap region, the drag needs to
+  // move by |drag_offset_snap_region| towards the left side of the screen
+  // before split view acknowledges the drag.
+  ASSERT_TRUE(Shell::Get()->window_selector_controller()->IsSelecting());
+  generator->set_current_screen_location(gfx::Point(
+      right_selector_item->target_bounds().right() - selector_item_inset,
+      right_selector_item->target_bounds().CenterPoint().y()));
+  generator->PressLeftButton();
+  generator->MoveMouseBy(drag_offset, 0);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  generator->MoveMouseBy(-drag_offset_snap_region, 0);
+  generator->MoveMouseBy(minimum_drag_offset, 0);
+  EXPECT_TRUE(IsPreviewAreaShowing());
+}
+
+// Verify the split view preview area becomes visible when expected.
+TEST_F(SplitViewDragIndicatorsTest, PreviewAreaVisibility) {
+  UpdateDisplay("800x600");
+  const int screen_width = 800;
+  const int edge_inset = GetEdgeInset(screen_width);
+  std::unique_ptr<aura::Window> window(CreateTestWindow());
+  ToggleOverview();
+
+  // Verify the preview area is visible when |selector_item|'s x is in the
+  // range [0, edge_inset] or [screen_width - edge_inset - 1, screen_width].
+  WindowSelectorItem* selector_item = GetOverviewItemForWindow(window.get());
+  ASSERT_TRUE(selector_item);
+  const gfx::Point start_location(selector_item->target_bounds().CenterPoint());
+  // Drag horizontally to avoid activating drag to close.
+  const int y = start_location.y();
+  window_selector_->InitiateDrag(selector_item, start_location);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  window_selector_->Drag(selector_item, gfx::Point(edge_inset + 1, y));
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  window_selector_->Drag(selector_item, gfx::Point(edge_inset, y));
+  EXPECT_TRUE(IsPreviewAreaShowing());
+
+  window_selector_->Drag(selector_item,
+                         gfx::Point(screen_width - edge_inset - 2, y));
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  window_selector_->Drag(selector_item,
+                         gfx::Point(screen_width - edge_inset - 1, y));
+  EXPECT_TRUE(IsPreviewAreaShowing());
+
+  // Drag back to |start_location| before compeleting the drag, otherwise
+  // |selector_time| will snap to the right and the system will enter splitview,
+  // making |window_drag_controller()| nullptr.
+  window_selector_->Drag(selector_item, start_location);
+  window_selector_->CompleteDrag(selector_item, start_location);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+}
+
+// Verify that the preview area never shows up when dragging a unsnappable
+// window.
+TEST_F(SplitViewDragIndicatorsTest, PreviewAreaVisibilityUnsnappableWindow) {
+  UpdateDisplay("800x600");
+  const int screen_width = 800;
+  std::unique_ptr<aura::Window> window(CreateUnsnappableWindow());
+  ToggleOverview();
+
+  WindowSelectorItem* selector_item = GetOverviewItemForWindow(window.get());
+  const gfx::Point start_location(selector_item->target_bounds().CenterPoint());
+  window_selector_->InitiateDrag(selector_item, start_location);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  window_selector_->Drag(selector_item, gfx::Point(0, 1));
+  EXPECT_FALSE(IsPreviewAreaShowing());
+  window_selector_->Drag(selector_item, gfx::Point(screen_width, 1));
+  EXPECT_FALSE(IsPreviewAreaShowing());
+
+  window_selector_->CompleteDrag(selector_item, start_location);
+  EXPECT_FALSE(IsPreviewAreaShowing());
+}
+
+// Verify that the split view overview overlay has the expected state.
+TEST_F(SplitViewDragIndicatorsTest, SplitViewDragIndicatorsState) {
+  UpdateDisplay("800x600");
+  const int screen_width = 800;
+  const int edge_inset = GetEdgeInset(screen_width);
+  std::unique_ptr<aura::Window> window1(CreateTestWindow());
+  std::unique_ptr<aura::Window> window2(CreateTestWindow());
+  ToggleOverview();
+
+  // Verify that when are no snapped windows, the indicator is visible once
+  // there is a long press or after the drag has started.
+  WindowSelectorItem* selector_item = GetOverviewItemForWindow(window1.get());
+  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
+  window_selector_->InitiateDrag(selector_item, start_location);
+  EXPECT_EQ(IndicatorState::kNone, indicator_state());
+  window_selector_->StartSplitViewDragMode(start_location);
+  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
+
+  // Reset the gesture so we stay in overview mode.
+  window_selector_->ResetDraggedWindowGesture();
+
+  // Verify the indicator is visible once the item starts moving, and becomes a
+  // preview area once we reach the left edge of the screen. Drag horizontal to
+  // avoid activating drag to close.
+  const int y_position = start_location.y();
+  window_selector_->InitiateDrag(selector_item, start_location);
+  EXPECT_EQ(IndicatorState::kNone, indicator_state());
+  window_selector_->Drag(selector_item, gfx::Point(edge_inset + 1, y_position));
+  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
+  window_selector_->Drag(selector_item, gfx::Point(edge_inset, y_position));
+  EXPECT_EQ(IndicatorState::kPreviewAreaLeft, indicator_state());
+
+  // Snap window to the left.
+  window_selector_->CompleteDrag(selector_item,
+                                 gfx::Point(edge_inset, y_position));
+  ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  ASSERT_EQ(SplitViewController::LEFT_SNAPPED,
+            split_view_controller()->state());
+
+  // Verify that when there is a left snapped window, dragging an item to the
+  // right will show the right preview area.
+  selector_item = GetOverviewItemForWindow(window2.get());
+  start_location = selector_item->target_bounds().CenterPoint();
+  window_selector_->InitiateDrag(selector_item, start_location);
+  EXPECT_EQ(IndicatorState::kNone, indicator_state());
+  window_selector_->Drag(selector_item,
+                         gfx::Point(screen_width - 1, y_position));
+  EXPECT_EQ(IndicatorState::kPreviewAreaRight, indicator_state());
+  window_selector_->CompleteDrag(selector_item, start_location);
+}
+
+// Verify that the split view drag indicator is shown when expected when
+// attempting to drag a unsnappable window.
+TEST_F(SplitViewDragIndicatorsTest,
+       SplitViewDragIndicatorVisibilityUnsnappableWindow) {
+  std::unique_ptr<aura::Window> unsnappable_window(CreateUnsnappableWindow());
+  ToggleOverview();
+
+  WindowSelectorItem* selector_item =
+      GetOverviewItemForWindow(unsnappable_window.get());
+  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
+  window_selector_->InitiateDrag(selector_item, start_location);
+  window_selector_->StartSplitViewDragMode(start_location);
+  EXPECT_EQ(IndicatorState::kCannotSnap, indicator_state());
+  const gfx::Point end_location1(0, 0);
+  window_selector_->Drag(selector_item, end_location1);
+  EXPECT_EQ(IndicatorState::kCannotSnap, indicator_state());
+  window_selector_->CompleteDrag(selector_item, end_location1);
+  EXPECT_EQ(IndicatorState::kNone, indicator_state());
+}
+
+// Verify when the split view drag indicators state changes, the expected
+// indicators will become visible or invisible.
+TEST_F(SplitViewDragIndicatorsTest, SplitViewDragIndicatorsVisibility) {
+  auto indicator = std::make_unique<SplitViewDragIndicators>();
+
+  auto to_int = [](IndicatorType type) { return static_cast<int>(type); };
+
+  // Helper function to which checks that all indicator types passed in |mask|
+  // are visible, and those that are not are not visible.
+  auto check_helper = [](SplitViewDragIndicators* svdi, int mask) {
+    const std::vector<IndicatorType> types = {
+        IndicatorType::kLeftHighlight, IndicatorType::kLeftText,
+        IndicatorType::kRightHighlight, IndicatorType::kRightText};
+    for (auto type : types) {
+      if ((static_cast<int>(type) & mask) > 0)
+        EXPECT_TRUE(svdi->GetIndicatorTypeVisibilityForTesting(type));
+      else
+        EXPECT_FALSE(svdi->GetIndicatorTypeVisibilityForTesting(type));
+    }
+  };
+
+  // Check each state has the correct views displayed. Pass and empty point as
+  // the location since there is no need to reparent the widget. Verify that
+  // nothing is shown in the none state.
+  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
+  check_helper(indicator.get(), 0);
+
+  const int all = to_int(IndicatorType::kLeftHighlight) |
+                  to_int(IndicatorType::kLeftText) |
+                  to_int(IndicatorType::kRightHighlight) |
+                  to_int(IndicatorType::kRightText);
+  // Verify that everything is visible in the dragging and cannot snap states.
+  indicator->SetIndicatorState(IndicatorState::kDragArea, gfx::Point());
+  check_helper(indicator.get(), all);
+  indicator->SetIndicatorState(IndicatorState::kCannotSnap, gfx::Point());
+  check_helper(indicator.get(), all);
+
+  // Verify that only one highlight shows up for the preview area states.
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaLeft, gfx::Point());
+  check_helper(indicator.get(), to_int(IndicatorType::kLeftHighlight));
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaRight, gfx::Point());
+  check_helper(indicator.get(), to_int(IndicatorType::kRightHighlight));
+}
+
+// Verify that the split view drag indicators widget reparents when starting a
+// drag on a different display.
+TEST_F(SplitViewDragIndicatorsTest, SplitViewDragIndicatorsWidgetReparenting) {
+  // Add two displays and one window on each display.
+  UpdateDisplay("600x600,600x600");
+  // DisplayConfigurationObserver enables mirror mode when tablet mode is
+  // enabled. Disable mirror mode to test multiple displays.
+  display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
+  base::RunLoop().RunUntilIdle();
+
+  auto root_windows = Shell::Get()->GetAllRootWindows();
+  ASSERT_EQ(2u, root_windows.size());
+
+  const gfx::Rect primary_screen_bounds(0, 0, 600, 600);
+  const gfx::Rect secondary_screen_bounds(600, 0, 600, 600);
+  auto primary_screen_window(CreateTestWindow(primary_screen_bounds));
+  auto secondary_screen_window(CreateTestWindow(secondary_screen_bounds));
+
+  ToggleOverview();
+
+  // Select an item on the primary display and verify the drag indicators
+  // widget's parent is the primary root window.
+  WindowSelectorItem* selector_item =
+      GetOverviewItemForWindow(primary_screen_window.get());
+  gfx::Point start_location(selector_item->target_bounds().CenterPoint());
+  window_selector_->InitiateDrag(selector_item, start_location);
+  window_selector_->Drag(selector_item, gfx::Point(100, start_location.y()));
+  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
+  EXPECT_EQ(root_windows[0], window_selector_->split_view_drag_indicators()
+                                 ->widget_->GetNativeView()
+                                 ->GetRootWindow());
+  // Drag the item in a way that neither opens the window nor activates
+  // splitview mode.
+  window_selector_->Drag(selector_item, primary_screen_bounds.CenterPoint());
+  window_selector_->CompleteDrag(selector_item,
+                                 primary_screen_bounds.CenterPoint());
+  ASSERT_TRUE(Shell::Get()->window_selector_controller()->IsSelecting());
+  ASSERT_FALSE(split_view_controller()->IsSplitViewModeActive());
+
+  // Select an item on the secondary display and verify the indicators widget
+  // has reparented to the secondary root window.
+  selector_item = GetOverviewItemForWindow(secondary_screen_window.get(), 1);
+  start_location = gfx::Point(selector_item->target_bounds().CenterPoint());
+  window_selector_->InitiateDrag(selector_item, start_location);
+  window_selector_->Drag(selector_item, gfx::Point(800, start_location.y()));
+  EXPECT_EQ(IndicatorState::kDragArea, indicator_state());
+  EXPECT_EQ(root_windows[1], window_selector_->split_view_drag_indicators()
+                                 ->widget_->GetNativeView()
+                                 ->GetRootWindow());
+  window_selector_->CompleteDrag(selector_item, start_location);
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index 8d468507..0fd582e 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -153,10 +153,6 @@
     AshTestBase::SetUp();
   }
 
-  void ActivateKeyboard() {
-    Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
-  }
-
   aura::Window* OpenToplevelTestWindow(bool modal) {
     views::Widget* widget = views::Widget::CreateWindowWithContext(
         new TestWindow(modal), CurrentContext());
@@ -680,7 +676,6 @@
 // positioned into the visible area.
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedFromKeyboard) {
-  ActivateKeyboard();
   const gfx::Rect& container_bounds = GetModalContainer()->bounds();
   // Place the window at the bottom of the screen.
   gfx::Size modal_size(100, 100);
@@ -717,7 +712,6 @@
 // if centered.
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedButNotCroppedFromKeyboard) {
-  ActivateKeyboard();
   const gfx::Rect& container_bounds = GetModalContainer()->bounds();
   const gfx::Size screen_size = Shell::GetPrimaryRootWindow()->bounds().size();
   // Place the window at the bottom of the screen.
@@ -751,7 +745,6 @@
 // if not centered.
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedButNotCroppedFromKeyboardIfNotCentered) {
-  ActivateKeyboard();
   const gfx::Size screen_size = Shell::GetPrimaryRootWindow()->bounds().size();
   // Place the window at the bottom of the screen.
   gfx::Size modal_size(100, screen_size.height() - 70);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f7f6c8f..21c8dd5 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -737,13 +737,9 @@
     "task/sequence_manager/task_queue.h",
     "task/sequence_manager/task_queue_impl.cc",
     "task/sequence_manager/task_queue_impl.h",
-    "task/sequence_manager/task_queue_proxy.cc",
-    "task/sequence_manager/task_queue_proxy.h",
     "task/sequence_manager/task_queue_selector.cc",
     "task/sequence_manager/task_queue_selector.h",
     "task/sequence_manager/task_queue_selector_logic.h",
-    "task/sequence_manager/task_queue_task_runner.cc",
-    "task/sequence_manager/task_queue_task_runner.h",
     "task/sequence_manager/task_time_observer.h",
     "task/sequence_manager/tasks.cc",
     "task/sequence_manager/tasks.h",
@@ -1319,7 +1315,6 @@
       "android/sys_utils.h",
       "android/task_scheduler/post_task_android.cc",
       "android/task_scheduler/post_task_android.h",
-      "android/task_scheduler/single_thread_task_runner_android.cc",
       "android/task_scheduler/task_runner_android.cc",
       "android/task_scheduler/task_runner_android.h",
       "android/time_utils.cc",
@@ -2887,7 +2882,6 @@
       "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
       "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
       "android/java/src/org/chromium/base/task/PostTask.java",
-      "android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
       "android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
     ]
 
@@ -3234,6 +3228,7 @@
       "android/child_process_binding_types.h",
       "android/library_loader/library_load_from_apk_status_codes.h",
       "android/library_loader/library_loader_hooks.h",
+      "android/task_scheduler/task_runner_android.h",
       "memory/memory_pressure_listener.h",
       "metrics/histogram_base.h",
       "task/task_traits.h",
diff --git a/base/allocator/allocator_shim.cc b/base/allocator/allocator_shim.cc
index df302070..1233ae2a 100644
--- a/base/allocator/allocator_shim.cc
+++ b/base/allocator/allocator_shim.cc
@@ -170,7 +170,7 @@
 #if defined(OS_MACOSX)
     context = malloc_default_zone();
 #endif
-    ptr = chain_head->alloc_aligned_function(chain_head, size, alignment,
+    ptr = chain_head->alloc_aligned_function(chain_head, alignment, size,
                                              context);
   } while (!ptr && CallNewHandler(size));
   return ptr;
diff --git a/base/allocator/allocator_shim_override_cpp_symbols.h b/base/allocator/allocator_shim_override_cpp_symbols.h
index 09c83ff..1228f5e 100644
--- a/base/allocator/allocator_shim_override_cpp_symbols.h
+++ b/base/allocator/allocator_shim_override_cpp_symbols.h
@@ -123,9 +123,8 @@
   ShimCppDelete(p);
 }
 
-SHIM_ALWAYS_EXPORT ALIGN_LINKAGE void* ALIGN_NEW_ARR(
-    std::size_t size,
-    ALIGN_VAL_T alignment) __THROW {
+SHIM_ALWAYS_EXPORT ALIGN_LINKAGE void* ALIGN_NEW_ARR(std::size_t size,
+                                                     ALIGN_VAL_T alignment) {
   return ShimCppAlignedNew(size, static_cast<size_t>(alignment));
 }
 
diff --git a/base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java
index 4b13479e..98ca82e 100644
--- a/base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java
@@ -17,7 +17,7 @@
      * @param traits The TaskTraits associated with this SequencedTaskRunnerImpl.
      */
     SequencedTaskRunnerImpl(TaskTraits traits) {
-        super(traits, "SequencedTaskRunnerImpl");
+        super(traits, "SequencedTaskRunnerImpl", TaskRunnerType.SEQUENCED);
     }
 
     @Override
diff --git a/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java
index e8cc18c..2934bca 100644
--- a/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java
@@ -26,7 +26,7 @@
      * @param traits The TaskTraits associated with this SingleThreadTaskRunnerImpl.
      */
     public SingleThreadTaskRunnerImpl(Handler handler, TaskTraits traits) {
-        super(traits, "SingleThreadTaskRunnerImpl");
+        super(traits, "SingleThreadTaskRunnerImpl", TaskRunnerType.SINGLE_THREAD);
         mHandler = handler;
     }
 
@@ -44,6 +44,4 @@
         // if |mHandler| is null then pre-native task execution is not supported.
         if (mHandler != null) mHandler.post(mRunPreNativeTaskClosure);
     }
-
-    private native boolean nativeBelongsToCurrentThread(long nativeTaskRunnerAndroid);
 }
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index 6cb227c5..d059767 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -19,9 +19,10 @@
 public class TaskRunnerImpl implements TaskRunner {
     @Nullable
     private final TaskTraits mTaskTraits;
+    private final String mTraceEvent;
+    private final @TaskRunnerType int mTaskRunnerType;
     private final Object mLock = new Object();
     protected long mNativeTaskRunnerAndroid;
-    private final String mTraceEvent;
     protected final Runnable mRunPreNativeTaskClosure = this::runPreNativeTask;
     private boolean mIsDestroying;
 
@@ -32,16 +33,20 @@
      * @param traits The TaskTraits associated with this TaskRunnerImpl.
      */
     TaskRunnerImpl(TaskTraits traits) {
-        this(traits, "TaskRunnerImpl");
+        this(traits, "TaskRunnerImpl", TaskRunnerType.BASE);
     }
 
     /**
      * @param traits The TaskTraits associated with this TaskRunnerImpl.
      * @param traceCategory Specifies which subclass is this instance for logging purposes.
+     * @param taskRunnerType Specifies which subclass is this instance for initialising the correct
+     *         native scheduler.
      */
-    protected TaskRunnerImpl(TaskTraits traits, String traceCategory) {
+    protected TaskRunnerImpl(
+            TaskTraits traits, String traceCategory, @TaskRunnerType int taskRunnerType) {
         mTaskTraits = traits;
         mTraceEvent = traceCategory + ".PreNativeTask.run";
+        mTaskRunnerType = taskRunnerType;
     }
 
     @Override
@@ -107,9 +112,10 @@
     public void initNativeTaskRunner() {
         synchronized (mLock) {
             if (mPreNativeTasks != null) {
-                mNativeTaskRunnerAndroid = nativeInit(mTaskTraits.mPrioritySetExplicitly,
-                        mTaskTraits.mPriority, mTaskTraits.mMayBlock, mTaskTraits.mExtensionId,
-                        mTaskTraits.mExtensionData);
+                mNativeTaskRunnerAndroid =
+                        nativeInit(mTaskRunnerType, mTaskTraits.mPrioritySetExplicitly,
+                                mTaskTraits.mPriority, mTaskTraits.mMayBlock,
+                                mTaskTraits.mExtensionId, mTaskTraits.mExtensionData);
                 for (Runnable task : mPreNativeTasks) {
                     nativePostTask(mNativeTaskRunnerAndroid, task);
                 }
@@ -119,8 +125,10 @@
     }
 
     // NB due to Proguard obfuscation it's easiest to pass the traits via arguments.
-    private static native long nativeInit(boolean prioritySetExplicitly, int priority,
-            boolean mayBlock, byte extensionId, byte[] extensionData);
+    private native long nativeInit(@TaskRunnerType int taskRunnerType,
+            boolean prioritySetExplicitly, int priority, boolean mayBlock, byte extensionId,
+            byte[] extensionData);
     private native void nativeDestroy(long nativeTaskRunnerAndroid);
     private native void nativePostTask(long nativeTaskRunnerAndroid, Runnable task);
+    protected native boolean nativeBelongsToCurrentThread(long nativeTaskRunnerAndroid);
 }
diff --git a/base/android/task_scheduler/single_thread_task_runner_android.cc b/base/android/task_scheduler/single_thread_task_runner_android.cc
deleted file mode 100644
index 6f5e250..0000000
--- a/base/android/task_scheduler/single_thread_task_runner_android.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/android/task_scheduler/post_task_android.h"
-#include "base/android/task_scheduler/task_runner_android.h"
-#include "base/task/post_task.h"
-#include "jni/SingleThreadTaskRunnerImpl_jni.h"
-
-namespace base {
-
-jlong JNI_SingleThreadTaskRunnerImpl_Init(
-    JNIEnv* env,
-    jboolean priority_set_explicitly,
-    jint priority,
-    jboolean may_block,
-    jbyte extension_id,
-    const base::android::JavaParamRef<jbyteArray>& extension_data) {
-  return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(
-      CreateSingleThreadTaskRunnerWithTraits(PostTaskAndroid::CreateTaskTraits(
-          env, priority_set_explicitly, priority, may_block, extension_id,
-          extension_data))));
-}
-
-}  // namespace base
diff --git a/base/android/task_scheduler/task_runner_android.cc b/base/android/task_scheduler/task_runner_android.cc
index 901521a..0f5b1d5 100644
--- a/base/android/task_scheduler/task_runner_android.cc
+++ b/base/android/task_scheduler/task_runner_android.cc
@@ -13,15 +13,29 @@
 
 jlong JNI_TaskRunnerImpl_Init(
     JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    jint task_runner_type,
     jboolean priority_set_explicitly,
     jint priority,
     jboolean may_block,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data) {
-  return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(
-      CreateTaskRunnerWithTraits(PostTaskAndroid::CreateTaskTraits(
-          env, priority_set_explicitly, priority, may_block, extension_id,
-          extension_data))));
+  TaskTraits task_traits = PostTaskAndroid::CreateTaskTraits(
+      env, priority_set_explicitly, priority, may_block, extension_id,
+      extension_data);
+  scoped_refptr<TaskRunner> task_runner;
+  switch (static_cast<TaskRunnerType>(task_runner_type)) {
+    case TaskRunnerType::BASE:
+      task_runner = CreateTaskRunnerWithTraits(task_traits);
+      break;
+    case TaskRunnerType::SEQUENCED:
+      task_runner = CreateSequencedTaskRunnerWithTraits(task_traits);
+      break;
+    case TaskRunnerType::SINGLE_THREAD:
+      task_runner = CreateSingleThreadTaskRunnerWithTraits(task_traits);
+      break;
+  }
+  return reinterpret_cast<intptr_t>(new TaskRunnerAndroid(task_runner));
 }
 
 TaskRunnerAndroid::TaskRunnerAndroid(scoped_refptr<TaskRunner> task_runner)
diff --git a/base/android/task_scheduler/task_runner_android.h b/base/android/task_scheduler/task_runner_android.h
index 7350cfb..ebec5b8 100644
--- a/base/android/task_scheduler/task_runner_android.h
+++ b/base/android/task_scheduler/task_runner_android.h
@@ -10,6 +10,9 @@
 
 namespace base {
 
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.task
+enum class TaskRunnerType { BASE, SEQUENCED, SINGLE_THREAD };
+
 // Native implementation backing TaskRunnerImpl.java which posts java tasks onto
 // a C++ TaskRunner.
 class TaskRunnerAndroid {
diff --git a/base/message_loop/message_loop_impl.cc b/base/message_loop/message_loop_impl.cc
index a7e30b2..a08e3d6 100644
--- a/base/message_loop/message_loop_impl.cc
+++ b/base/message_loop/message_loop_impl.cc
@@ -110,11 +110,13 @@
 }
 
 void MessageLoopImpl::Controller::StartScheduling() {
+  DCHECK_CALLED_ON_VALID_THREAD(message_loop_->bound_thread_checker_);
   if (operations_controller_.StartAcceptingOperations())
     message_loop_->ScheduleWork();
 }
 
 void MessageLoopImpl::Controller::DisconnectFromParent() {
+  DCHECK_CALLED_ON_VALID_THREAD(message_loop_->bound_thread_checker_);
   ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait_for_fast_ops;
   operations_controller_.ShutdownAndWaitForZeroOperations();
 }
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index a68565c..b78df5840 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -40,6 +40,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
@@ -722,6 +723,13 @@
     return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
   }
 
+#if defined(OS_LINUX)
+  // Since we use clone() directly, it does not call any pthread_aftork()
+  // callbacks, we explicitly clear tid cache here (normally this call is
+  // done as pthread_aftork() callback).  See crbug.com/902514.
+  base::internal::ClearTidCache();
+#endif  // defined(OS_LINUX)
+
   return 0;
 }
 #endif  // defined(OS_LINUX) || defined(OS_NACL_NONSFI)
diff --git a/base/strings/string16.h b/base/strings/string16.h
index a86baa2..3cb6c7c 100644
--- a/base/strings/string16.h
+++ b/base/strings/string16.h
@@ -38,6 +38,16 @@
 
 #if defined(WCHAR_T_IS_UTF16)
 
+// Define a macro for wrapping construction of char16 arrays and string16s from
+// a literal string. This indirection allows for an easier migration of
+// base::char16 to char16_t on platforms where WCHAR_T_IS_UTF16, as only a one
+// character change to the macro will be necessary.
+// This macro does not exist when WCHAR_T_IS_UTF32, as it is currently not
+// possible to create a char array form a literal in this case.
+// TODO(https://crbug.com/911896): Remove this macro once base::char16 is
+// char16_t on all platforms.
+#define STRING16_LITERAL(x) L##x
+
 namespace base {
 
 typedef wchar_t char16;
diff --git a/base/strings/string16_unittest.cc b/base/strings/string16_unittest.cc
index 0d2ca808..0e41864 100644
--- a/base/strings/string16_unittest.cc
+++ b/base/strings/string16_unittest.cc
@@ -8,10 +8,19 @@
 #include "base/strings/string16.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
 
+#if defined(WCHAR_T_IS_UTF16)
+TEST(String16Test, String16Literal) {
+  static constexpr char16 kHelloWorld[] = STRING16_LITERAL("Hello, World");
+  string16 hello_world = kHelloWorld;
+  EXPECT_EQ(kHelloWorld, hello_world);
+}
+#endif
+
 // We define a custom operator<< for string16 so we can use it with logging.
 // This tests that conversion.
 TEST(String16Test, OutputStream) {
diff --git a/base/task/common/operations_controller.cc b/base/task/common/operations_controller.cc
index 44fc8d9..7787cdf 100644
--- a/base/task/common/operations_controller.cc
+++ b/base/task/common/operations_controller.cc
@@ -8,9 +8,7 @@
 namespace base {
 namespace internal {
 
-OperationsController::OperationsController() {
-  DETACH_FROM_SEQUENCE(owning_sequence_checker_);
-}
+OperationsController::OperationsController() = default;
 
 OperationsController::~OperationsController() {
 #if DCHECK_IS_ON()
@@ -26,7 +24,6 @@
 }
 
 bool OperationsController::StartAcceptingOperations() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_checker_);
   // Release semantics are required to ensure that all memory accesses made on
   // this thread happen-before any others done on a thread which is later
   // allowed to perform an operation.
@@ -59,7 +56,6 @@
 }
 
 void OperationsController::ShutdownAndWaitForZeroOperations() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_checker_);
   // Acquire semantics are required to guarantee that all memory side-effects
   // made by other threads that were allowed to perform operations are
   // synchronized with this thread before it returns from this method.
diff --git a/base/task/common/operations_controller.h b/base/task/common/operations_controller.h
index 1a1ad915..e68e78d 100644
--- a/base/task/common/operations_controller.h
+++ b/base/task/common/operations_controller.h
@@ -8,7 +8,6 @@
 #include <atomic>
 #include <cstdint>
 
-#include "base/sequence_checker.h"
 #include "base/synchronization/waitable_event.h"
 
 namespace base {
@@ -48,10 +47,9 @@
 //   }
 // }
 //
-// Attention: StartAcceptingOperations() and ShutdownAndWaitForZeroOperations()
-// must be called form the same Sequence.
-//
-// This class is thread-safe (but see attention note above).
+// This class is thread-safe.
+// But note that StartAcceptingOperations can never be called after
+// ShutdownAndWaitForZeroOperations.
 class BASE_EXPORT OperationsController {
  public:
   // The owner of an OperationToken which evaluates to true can safely perform
@@ -94,10 +92,6 @@
   // an invalid token). Returns true if an attempt to perform an operation was
   // made and denied before StartAcceptingOperations() was called. Can be called
   // at most once, never after ShutdownAndWaitForZeroOperations().
-  //
-  // Note that if this returns true, the caller may perform an operation to
-  // replace the ones denied (safe since ShutdownAndWaitForZeroOperations() has
-  // to be invoked on the same sequence).
   bool StartAcceptingOperations();
 
   // Returns a RAII like object that implicitly converts to true if operations
@@ -111,8 +105,6 @@
   // all the ongoing operations to complete.
   //
   // Attention: Can only be called once.
-  // Attention: Must be called from the same Sequence as
-  // StartAcceptingOperations() (if called).
   void ShutdownAndWaitForZeroOperations();
 
  private:
@@ -151,9 +143,6 @@
 
   std::atomic<uint32_t> state_and_count_{0};
   WaitableEvent shutdown_complete_;
-  // Verifies that StartAcceptingOperations() and
-  // ShutdownAndWaitForZeroOperations() are performed on the same sequence.
-  SEQUENCE_CHECKER(owning_sequence_checker_);
 };
 
 }  // namespace internal
diff --git a/base/task/sequence_manager/task_queue.cc b/base/task/sequence_manager/task_queue.cc
index 30c36dd..ce72109 100644
--- a/base/task/sequence_manager/task_queue.cc
+++ b/base/task/sequence_manager/task_queue.cc
@@ -10,8 +10,6 @@
 #include "base/task/sequence_manager/associated_thread_id.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
-#include "base/task/sequence_manager/task_queue_proxy.h"
-#include "base/task/sequence_manager/task_queue_task_runner.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 
@@ -20,12 +18,32 @@
 
 namespace {
 
+class NullTaskRunner final : public SingleThreadTaskRunner {
+ public:
+  NullTaskRunner() {}
+
+  bool PostDelayedTask(const Location& location,
+                       OnceClosure callback,
+                       TimeDelta delay) override {
+    return false;
+  }
+
+  bool PostNonNestableDelayedTask(const Location& location,
+                                  OnceClosure callback,
+                                  TimeDelta delay) override {
+    return false;
+  }
+
+  bool RunsTasksInCurrentSequence() const override { return false; }
+
+ private:
+  // Ref-counted
+  ~NullTaskRunner() override = default;
+};
+
 // TODO(kraynov): Move NullTaskRunner from //base/test to //base.
 scoped_refptr<SingleThreadTaskRunner> CreateNullTaskRunner() {
-  return MakeRefCounted<internal::TaskQueueTaskRunner>(
-      MakeRefCounted<internal::TaskQueueProxy>(
-          nullptr, MakeRefCounted<internal::AssociatedThreadId>()),
-      kTaskTypeNone);
+  return MakeRefCounted<NullTaskRunner>();
 }
 
 }  // namespace
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index b81c261..b183da3d 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -9,8 +9,6 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
-#include "base/task/sequence_manager/task_queue_proxy.h"
-#include "base/task/sequence_manager/task_queue_task_runner.h"
 #include "base/task/sequence_manager/time_domain.h"
 #include "base/task/sequence_manager/work_queue.h"
 #include "base/time/time.h"
@@ -43,6 +41,49 @@
 
 namespace internal {
 
+TaskQueueImpl::GuardedTaskPoster::GuardedTaskPoster(TaskQueueImpl* outer)
+    : outer_(outer) {}
+
+TaskQueueImpl::GuardedTaskPoster::~GuardedTaskPoster() {}
+
+bool TaskQueueImpl::GuardedTaskPoster::PostTask(PostedTask task) {
+  auto token = operations_controller_.TryBeginOperation();
+  if (!token)
+    return false;
+
+  outer_->PostTask(std::move(task));
+  return true;
+}
+
+TaskQueueImpl::TaskRunner::TaskRunner(
+    scoped_refptr<GuardedTaskPoster> task_poster,
+    scoped_refptr<AssociatedThreadId> associated_thread,
+    int task_type)
+    : task_poster_(std::move(task_poster)),
+      associated_thread_(std::move(associated_thread)),
+      task_type_(task_type) {}
+
+TaskQueueImpl::TaskRunner::~TaskRunner() {}
+
+bool TaskQueueImpl::TaskRunner::PostDelayedTask(const Location& location,
+                                                OnceClosure callback,
+                                                TimeDelta delay) {
+  return task_poster_->PostTask(PostedTask(std::move(callback), location, delay,
+                                           Nestable::kNestable, task_type_));
+}
+
+bool TaskQueueImpl::TaskRunner::PostNonNestableDelayedTask(
+    const Location& location,
+    OnceClosure callback,
+    TimeDelta delay) {
+  return task_poster_->PostTask(PostedTask(std::move(callback), location, delay,
+                                           Nestable::kNonNestable, task_type_));
+}
+
+bool TaskQueueImpl::TaskRunner::RunsTasksInCurrentSequence() const {
+  return associated_thread_->IsBoundToCurrentThread();
+}
+
 TaskQueueImpl::TaskQueueImpl(SequenceManagerImpl* sequence_manager,
                              TimeDomain* time_domain,
                              const TaskQueue::Spec& spec)
@@ -51,17 +92,17 @@
       associated_thread_(sequence_manager
                              ? sequence_manager->associated_thread()
                              : AssociatedThreadId::CreateBound()),
+      task_poster_(MakeRefCounted<GuardedTaskPoster>(this)),
       any_thread_(time_domain),
       main_thread_only_(this, time_domain),
-      proxy_(MakeRefCounted<TaskQueueProxy>(this, associated_thread_)),
       should_monitor_quiescence_(spec.should_monitor_quiescence),
       should_notify_observers_(spec.should_notify_observers),
       delayed_fence_allowed_(spec.delayed_fence_allowed) {
   DCHECK(time_domain);
   // SequenceManager can't be set later, so we need to prevent task runners
   // from posting any tasks.
-  if (!sequence_manager)
-    proxy_->DetachFromTaskQueueImpl();
+  if (sequence_manager_)
+    task_poster_->StartAcceptingOperations();
 }
 
 TaskQueueImpl::~TaskQueueImpl() {
@@ -98,13 +139,13 @@
 
 scoped_refptr<SingleThreadTaskRunner> TaskQueueImpl::CreateTaskRunner(
     int task_type) const {
-  // |proxy_| pointer is const, hence no need for lock.
-  return MakeRefCounted<TaskQueueTaskRunner>(proxy_, task_type);
+  return MakeRefCounted<TaskRunner>(task_poster_, associated_thread_,
+                                    task_type);
 }
 
 void TaskQueueImpl::UnregisterTaskQueue() {
   // Detach task runners.
-  proxy_->DetachFromTaskQueueImpl();
+  task_poster_->ShutdownAndWaitForZeroOperations();
 
   TaskDeque immediate_incoming_queue;
 
@@ -150,16 +191,12 @@
   return name_;
 }
 
-bool TaskQueueImpl::RunsTasksInCurrentSequence() const {
-  return associated_thread_->IsBoundToCurrentThread();
-}
+void TaskQueueImpl::PostTask(PostedTask task) {
+  CurrentThread current_thread =
+      associated_thread_->IsBoundToCurrentThread()
+          ? TaskQueueImpl::CurrentThread::kMainThread
+          : TaskQueueImpl::CurrentThread::kNotMainThread;
 
-void TaskQueueImpl::PostTask(PostedTask task, CurrentThread current_thread) {
-  DCHECK_EQ(current_thread == CurrentThread::kMainThread,
-            RunsTasksInCurrentSequence());
-  // This method can only be called if task queue is able to accept tasks,
-  // i.e. has a sequence manager and not being unregistered. This is enforced
-  // by |proxy_| which is detached if this condition not met.
   if (task.delay.is_zero()) {
     PostImmediateTaskImpl(std::move(task), current_thread);
   } else {
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 873fec0..fa16a45b 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -17,6 +17,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/pending_task.h"
 #include "base/task/common/intrusive_heap.h"
+#include "base/task/common/operations_controller.h"
 #include "base/task/sequence_manager/associated_thread_id.h"
 #include "base/task/sequence_manager/enqueue_order.h"
 #include "base/task/sequence_manager/lazily_deallocated_deque.h"
@@ -35,7 +36,6 @@
 namespace internal {
 
 class SequenceManagerImpl;
-class TaskQueueProxy;
 class WorkQueue;
 class WorkQueueSets;
 
@@ -104,8 +104,6 @@
 
   // TaskQueue implementation.
   const char* GetName() const;
-  bool RunsTasksInCurrentSequence() const;
-  void PostTask(PostedTask task, CurrentThread current_thread);
   std::unique_ptr<TaskQueue::QueueEnabledVoter> CreateQueueEnabledVoter(
       scoped_refptr<TaskQueue> owning_task_queue);
   bool IsQueueEnabled() const;
@@ -259,6 +257,63 @@
   friend class WorkQueue;
   friend class WorkQueueTest;
 
+  // A TaskQueueImpl instance can be destroyed or unregistered before all its
+  // associated TaskRunner instances are (they are refcounted). Thus we need a
+  // way to prevent TaskRunner instances from posting further tasks. This class
+  // guards PostTask calls using an OperationsController.
+  // This class is ref-counted as both the TaskQueueImpl instance and all
+  // associated TaskRunner instances share the same GuardedTaskPoster instance.
+  // When TaskQueueImpl shuts down it calls ShutdownAndWaitForZeroOperations(),
+  // preventing further PostTask calls being made to the underlying
+  // TaskQueueImpl.
+  class GuardedTaskPoster : public RefCountedThreadSafe<GuardedTaskPoster> {
+   public:
+    explicit GuardedTaskPoster(TaskQueueImpl* outer);
+
+    bool PostTask(PostedTask task);
+
+    void StartAcceptingOperations() {
+      operations_controller_.StartAcceptingOperations();
+    }
+
+    void ShutdownAndWaitForZeroOperations() {
+      operations_controller_.ShutdownAndWaitForZeroOperations();
+    }
+
+   private:
+    friend class RefCountedThreadSafe<GuardedTaskPoster>;
+
+    ~GuardedTaskPoster();
+
+    base::internal::OperationsController operations_controller_;
+    // Pointer might be stale, access guarded by |operations_controller_|
+    TaskQueueImpl* const outer_;
+  };
+
+  class TaskRunner : public SingleThreadTaskRunner {
+   public:
+    explicit TaskRunner(scoped_refptr<GuardedTaskPoster> task_poster,
+                        scoped_refptr<AssociatedThreadId> associated_thread,
+                        int task_type);
+
+    bool PostDelayedTask(const Location& location,
+                         OnceClosure callback,
+                         TimeDelta delay) final;
+    bool PostNonNestableDelayedTask(const Location& location,
+                                    OnceClosure callback,
+                                    TimeDelta delay) final;
+    bool RunsTasksInCurrentSequence() const final;
+
+   private:
+    ~TaskRunner() final;
+
+    bool PostTask(PostedTask task) const;
+
+    const scoped_refptr<GuardedTaskPoster> task_poster_;
+    const scoped_refptr<AssociatedThreadId> associated_thread_;
+    const int task_type_;
+  };
+
   struct AnyThread {
     explicit AnyThread(TimeDomain* time_domain);
     ~AnyThread();
@@ -337,6 +392,8 @@
     bool is_enabled_for_test;
   };
 
+  void PostTask(PostedTask task);
+
   void PostImmediateTaskImpl(PostedTask task, CurrentThread current_thread);
   void PostDelayedTaskImpl(PostedTask task, CurrentThread current_thread);
 
@@ -392,6 +449,8 @@
 
   scoped_refptr<AssociatedThreadId> associated_thread_;
 
+  const scoped_refptr<GuardedTaskPoster> task_poster_;
+
   mutable Lock any_thread_lock_;
   AnyThread any_thread_;
   struct AnyThread& any_thread() {
@@ -413,10 +472,6 @@
     return main_thread_only_;
   }
 
-  // Proxy which allows TaskQueueTaskRunner to dispatch tasks and it can be
-  // detached from TaskQueueImpl to leave dangling task runners behind sefely.
-  const scoped_refptr<TaskQueueProxy> proxy_;
-
   mutable Lock immediate_incoming_queue_lock_;
   TaskDeque immediate_incoming_queue_;
   TaskDeque& immediate_incoming_queue() {
diff --git a/base/task/sequence_manager/task_queue_proxy.cc b/base/task/sequence_manager/task_queue_proxy.cc
deleted file mode 100644
index b8cdca51..0000000
--- a/base/task/sequence_manager/task_queue_proxy.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/task/sequence_manager/task_queue_proxy.h"
-
-#include "base/task/sequence_manager/associated_thread_id.h"
-#include "base/task/sequence_manager/sequence_manager_impl.h"
-#include "base/task/sequence_manager/task_queue_impl.h"
-
-namespace base {
-namespace sequence_manager {
-namespace internal {
-
-TaskQueueProxy::TaskQueueProxy(
-    TaskQueueImpl* task_queue_impl,
-    scoped_refptr<AssociatedThreadId> associated_thread)
-    : task_queue_impl_(task_queue_impl),
-      associated_thread_(std::move(associated_thread)) {}
-
-TaskQueueProxy::~TaskQueueProxy() = default;
-
-bool TaskQueueProxy::PostTask(PostedTask task) const {
-  // NOTE: Task's destructor might attempt to post another task,
-  // so ensure it never happens inside this lock.
-  if (RunsTasksInCurrentSequence()) {
-    if (!task_queue_impl_)
-      return false;
-
-    task_queue_impl_->PostTask(std::move(task),
-                               TaskQueueImpl::CurrentThread::kMainThread);
-    return true;
-  } else {
-    AutoLock lock(lock_);
-    if (!task_queue_impl_)
-      return false;
-
-    task_queue_impl_->PostTask(std::move(task),
-                               TaskQueueImpl::CurrentThread::kNotMainThread);
-    return true;
-  }
-}
-
-bool TaskQueueProxy::RunsTasksInCurrentSequence() const {
-  return associated_thread_->IsBoundToCurrentThread();
-}
-
-void TaskQueueProxy::DetachFromTaskQueueImpl() {
-  DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
-  // |task_queue_impl_| can be read from the main thread without a lock,
-  // but a lock is needed when we write to it.
-  AutoLock lock(lock_);
-  task_queue_impl_ = nullptr;
-}
-
-}  // namespace internal
-}  // namespace sequence_manager
-}  // namespace base
diff --git a/base/task/sequence_manager/task_queue_proxy.h b/base/task/sequence_manager/task_queue_proxy.h
deleted file mode 100644
index 88084217..0000000
--- a/base/task/sequence_manager/task_queue_proxy.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2018 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 BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_PROXY_H_
-#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_PROXY_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/optional.h"
-#include "base/synchronization/lock.h"
-#include "base/task/sequence_manager/moveable_auto_lock.h"
-#include "base/task/sequence_manager/task_queue.h"
-#include "base/task/sequence_manager/tasks.h"
-
-namespace base {
-namespace sequence_manager {
-namespace internal {
-
-class AssociatedThreadId;
-class TaskQueueImpl;
-
-// Task runners are ref-counted and unaccountable, so we need a safe way
-// to delete a task queue while associated task runners might be still around.
-// When TaskQueueImpl goes away, this proxy becomes a stub and later on gets
-// ref-count-destructed once no TaskQueueTaskRunner remains.
-// NOTE: Instances must be constructed or detached only by TaskQueueImpl,
-// unless |task_queue_impl| is null (which is useful for stub task runners).
-class TaskQueueProxy : public RefCountedThreadSafe<TaskQueueProxy> {
- public:
-  TaskQueueProxy(TaskQueueImpl* task_queue_impl,
-                 scoped_refptr<AssociatedThreadId> associated_thread);
-
-  // May be called on any thread.
-  bool PostTask(PostedTask task) const;
-  bool RunsTasksInCurrentSequence() const;
-
-  // PostTask will reject any task after this call.
-  // Must be called on main thread only.
-  void DetachFromTaskQueueImpl();
-
- private:
-  friend class RefCountedThreadSafe<TaskQueueProxy>;
-  ~TaskQueueProxy();
-
-  mutable Lock lock_;
-  TaskQueueImpl* task_queue_impl_;  // Not owned.
-  const scoped_refptr<AssociatedThreadId> associated_thread_;
-};
-
-}  // namespace internal
-}  // namespace sequence_manager
-}  // namespace base
-
-#endif  // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_PROXY_H_
diff --git a/base/task/sequence_manager/task_queue_task_runner.cc b/base/task/sequence_manager/task_queue_task_runner.cc
deleted file mode 100644
index a82b1476..0000000
--- a/base/task/sequence_manager/task_queue_task_runner.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/task/sequence_manager/task_queue_task_runner.h"
-
-#include "base/task/sequence_manager/task_queue.h"
-#include "base/task/sequence_manager/task_queue_proxy.h"
-
-namespace base {
-namespace sequence_manager {
-namespace internal {
-
-TaskQueueTaskRunner::TaskQueueTaskRunner(
-    scoped_refptr<TaskQueueProxy> task_queue_proxy,
-    int task_type)
-    : task_queue_proxy_(std::move(task_queue_proxy)), task_type_(task_type) {}
-
-TaskQueueTaskRunner::~TaskQueueTaskRunner() {}
-
-bool TaskQueueTaskRunner::PostDelayedTask(const Location& location,
-                                          OnceClosure callback,
-                                          TimeDelta delay) {
-  return task_queue_proxy_->PostTask(PostedTask(
-      std::move(callback), location, delay, Nestable::kNestable, task_type_));
-}
-
-bool TaskQueueTaskRunner::PostNonNestableDelayedTask(const Location& location,
-                                                     OnceClosure callback,
-                                                     TimeDelta delay) {
-  return task_queue_proxy_->PostTask(PostedTask(std::move(callback), location,
-                                                delay, Nestable::kNonNestable,
-                                                task_type_));
-}
-
-bool TaskQueueTaskRunner::RunsTasksInCurrentSequence() const {
-  return task_queue_proxy_->RunsTasksInCurrentSequence();
-}
-
-}  // namespace internal
-}  // namespace sequence_manager
-}  // namespace base
diff --git a/base/task/sequence_manager/task_queue_task_runner.h b/base/task/sequence_manager/task_queue_task_runner.h
deleted file mode 100644
index d679e9f..0000000
--- a/base/task/sequence_manager/task_queue_task_runner.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 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 BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_TASK_RUNNER_H_
-#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_TASK_RUNNER_H_
-
-#include "base/single_thread_task_runner.h"
-
-namespace base {
-namespace sequence_manager {
-namespace internal {
-
-class TaskQueueProxy;
-
-// TODO(kraynov): Post tasks to a TaskQueue solely using this task runner and
-// drop SingleThreadTaskRunner implementation in the TaskQueue class.
-// See https://crbug.com/865411.
-class BASE_EXPORT TaskQueueTaskRunner : public SingleThreadTaskRunner {
- public:
-  TaskQueueTaskRunner(scoped_refptr<TaskQueueProxy> task_queue_proxy,
-                      int task_type);
-
-  bool PostDelayedTask(const Location& location,
-                       OnceClosure callback,
-                       TimeDelta delay) override;
-  bool PostNonNestableDelayedTask(const Location& location,
-                                  OnceClosure callback,
-                                  TimeDelta delay) override;
-  bool RunsTasksInCurrentSequence() const override;
-
- private:
-  ~TaskQueueTaskRunner() override;  // Ref-counted.
-
-  const scoped_refptr<TaskQueueProxy> task_queue_proxy_;
-  const int task_type_;
-
-  DISALLOW_COPY_AND_ASSIGN(TaskQueueTaskRunner);
-};
-
-}  // namespace internal
-}  // namespace sequence_manager
-}  // namespace base
-
-#endif  // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_TASK_RUNNER_H_
diff --git a/base/task/task_scheduler/scheduler_parallel_task_runner.h b/base/task/task_scheduler/scheduler_parallel_task_runner.h
index 5107be35..84552e2 100644
--- a/base/task/task_scheduler/scheduler_parallel_task_runner.h
+++ b/base/task/task_scheduler/scheduler_parallel_task_runner.h
@@ -12,6 +12,7 @@
 #include "base/task/task_scheduler/scheduler_lock.h"
 #include "base/task/task_traits.h"
 #include "base/task_runner.h"
+#include "base/thread_annotations.h"
 #include "base/time/time.h"
 
 namespace base {
@@ -44,13 +45,12 @@
   const TaskTraits traits_;
   SchedulerTaskRunnerDelegate* const scheduler_task_runner_delegate_;
 
-  // Synchronizes access to |sequences_|.
   SchedulerLock lock_;
 
   // List of alive Sequences instantiated by this SchedulerParallelTaskRunner.
   // Sequences are added when they are instantiated, and removed when they are
   // destroyed.
-  base::flat_set<Sequence*> sequences_;
+  base::flat_set<Sequence*> sequences_ GUARDED_BY(lock_);
 
   DISALLOW_COPY_AND_ASSIGN(SchedulerParallelTaskRunner);
 };
diff --git a/base/test/launcher/unit_test_launcher_ios.cc b/base/test/launcher/unit_test_launcher_ios.cc
index 0bb31f7..9e83877 100644
--- a/base/test/launcher/unit_test_launcher_ios.cc
+++ b/base/test/launcher/unit_test_launcher_ios.cc
@@ -17,6 +17,12 @@
 int LaunchUnitTests(int argc,
                     char** argv,
                     RunTestSuiteCallback run_test_suite) {
+  return LaunchUnitTestsSerially(argc, argv, std::move(run_test_suite));
+}
+
+int LaunchUnitTestsSerially(int argc,
+                            char** argv,
+                            RunTestSuiteCallback run_test_suite) {
   CHECK(CommandLine::InitializedForCurrentProcess() ||
         CommandLine::Init(argc, argv));
   const CommandLine* command_line = CommandLine::ForCurrentProcess();
diff --git a/base/threading/platform_thread_internal_posix.h b/base/threading/platform_thread_internal_posix.h
index 904a05bb..d248fa9 100644
--- a/base/threading/platform_thread_internal_posix.h
+++ b/base/threading/platform_thread_internal_posix.h
@@ -8,6 +8,7 @@
 #include "base/base_export.h"
 #include "base/optional.h"
 #include "base/threading/platform_thread.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -46,6 +47,14 @@
 // of CanIncreaseThreadPriority().
 Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform();
 
+#if defined(OS_LINUX)
+// Current thread id is cached in thread local storage for performance reasons.
+// In some rare cases it's important to clear that cache explicitly (e.g. after
+// going through clone() syscall which does not call pthread_atfork()
+// handlers).
+BASE_EXPORT void ClearTidCache();
+#endif  // defined(OS_LINUX)
+
 }  // namespace internal
 
 }  // namespace base
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index dff4525..a9f8d0a 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -148,19 +148,27 @@
 // CHECK/DCHECKs.
 thread_local pid_t g_thread_id = -1;
 
-void ClearTidCache() {
-  g_thread_id = -1;
-}
-
 class InitAtFork {
  public:
-  InitAtFork() { pthread_atfork(nullptr, nullptr, ClearTidCache); }
+  InitAtFork() { pthread_atfork(nullptr, nullptr, internal::ClearTidCache); }
 };
 
 #endif  // defined(OS_LINUX)
 
 }  // namespace
 
+#if defined(OS_LINUX)
+
+namespace internal {
+
+void ClearTidCache() {
+  g_thread_id = -1;
+}
+
+}  // namespace internal
+
+#endif  // defined(OS_LINUX)
+
 // static
 PlatformThreadId PlatformThread::CurrentId() {
   // Pthreads doesn't have the concept of a thread ID, so we have to reach down
diff --git a/base/threading/scoped_blocking_call.cc b/base/threading/scoped_blocking_call.cc
index 1734cbee..71cbfb2 100644
--- a/base/threading/scoped_blocking_call.cc
+++ b/base/threading/scoped_blocking_call.cc
@@ -15,17 +15,6 @@
 
 namespace {
 
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_64)
-// For a mysterious reason, enabling tracing in ScopedBlockingCall makes
-// telemetry_unittests's
-// telemetry.core.tracing_controller_unittest.
-//     StartupTracingTest.testCloseBrowserBeforeTracingIsStopped
-// timeout only on chromeos-amd64-generic-rel : https://crbug.com/902514.
-const bool kTraceScopedBlockingCall = false;
-#else
-const bool kTraceScopedBlockingCall = true;
-#endif
-
 LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
     tls_blocking_observer = LAZY_INSTANCE_INITIALIZER;
 
@@ -84,10 +73,8 @@
 #endif
 
   internal::AssertBlockingAllowed();
-  if (kTraceScopedBlockingCall) {
-    TRACE_EVENT_BEGIN1("base", "ScopedBlockingCall", "blocking_type",
-                       static_cast<int>(blocking_type));
-  }
+  TRACE_EVENT_BEGIN1("base", "ScopedBlockingCall", "blocking_type",
+                     static_cast<int>(blocking_type));
 
 #if DCHECK_IS_ON()
   tls_construction_in_progress.Get().Set(false);
@@ -95,8 +82,7 @@
 }
 
 ScopedBlockingCall::~ScopedBlockingCall() {
-  if (kTraceScopedBlockingCall)
-    TRACE_EVENT_END0("base", "ScopedBlockingCall");
+  TRACE_EVENT_END0("base", "ScopedBlockingCall");
 }
 
 namespace internal {
@@ -110,10 +96,8 @@
 #endif
 
   internal::AssertBaseSyncPrimitivesAllowed();
-  if (kTraceScopedBlockingCall) {
-    TRACE_EVENT_BEGIN1("base", "ScopedBlockingCallWithBaseSyncPrimitives",
-                       "blocking_type", static_cast<int>(blocking_type));
-  }
+  TRACE_EVENT_BEGIN1("base", "ScopedBlockingCallWithBaseSyncPrimitives",
+                     "blocking_type", static_cast<int>(blocking_type));
 
 #if DCHECK_IS_ON()
   tls_construction_in_progress.Get().Set(false);
@@ -122,8 +106,7 @@
 
 ScopedBlockingCallWithBaseSyncPrimitives::
     ~ScopedBlockingCallWithBaseSyncPrimitives() {
-  if (kTraceScopedBlockingCall)
-    TRACE_EVENT_END0("base", "ScopedBlockingCallWithBaseSyncPrimitives");
+  TRACE_EVENT_END0("base", "ScopedBlockingCallWithBaseSyncPrimitives");
 }
 
 void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) {
diff --git a/build/android/PRESUBMIT.py b/build/android/PRESUBMIT.py
index c444619..0ec045c 100644
--- a/build/android/PRESUBMIT.py
+++ b/build/android/PRESUBMIT.py
@@ -63,7 +63,6 @@
               J('gyp', 'util', 'build_utils_test.py'),
               J('gyp', 'util', 'md5_check_test.py'),
               J('gyp', 'util', 'resource_utils_test.py'),
-              J('play_services', 'update_test.py'),
               J('pylib', 'constants', 'host_paths_unittest.py'),
               J('pylib', 'gtest', 'gtest_test_instance_test.py'),
               J('pylib', 'instrumentation',
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index effc6a1..cba8c7f 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -37,10 +37,6 @@
   parser = optparse.OptionParser()
   build_utils.AddDepfileOption(parser)
 
-  parser.add_option('--classpath', help='Classpaths necessary for desugaring.')
-  parser.add_option(
-      '--sdk-jars',
-      help='Path(s) to android sdk jar, necessary for desugaring.')
   parser.add_option('--output-directory',
                     default=os.getcwd(),
                     help='Path to the output build directory.')
@@ -322,13 +318,7 @@
   if options.multi_dex:
     input_paths.append(options.main_dex_list_path)
 
-  dex_cmd = ['java', '-jar', options.d8_jar_path]
-  options.sdk_jars = build_utils.ParseGnList(options.sdk_jars)
-  options.classpath = build_utils.ParseGnList(options.classpath)
-  for path in options.classpath:
-    dex_cmd += ['--classpath', path]
-  for path in options.sdk_jars:
-    dex_cmd += ['--lib', path]
+  dex_cmd = ['java', '-jar', options.d8_jar_path, '--no-desugaring']
   if options.multi_dex:
     dex_cmd += ['--main-dex-list', options.main_dex_list_path]
   if options.release:
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index 65cb80f7..1377681 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -315,6 +315,10 @@
   # Don't bother enabling incremental compilation for non-chromium code.
   incremental = options.incremental and options.chromium_code
 
+  # Compiles with Error Prone take twice as long to run as pure javac. Thus GN
+  # rules run both in parallel, with Error Prone only used for checks.
+  save_outputs = not options.use_errorprone_path
+
   with build_utils.TempDir() as temp_dir:
     srcjars = options.java_srcjars
 
@@ -348,7 +352,11 @@
       # (by not extracting them).
       javac_cmd = _ConvertToJMakeArgs(javac_cmd, pdb_path)
 
-    generated_java_dir = options.generated_dir
+    if save_outputs:
+      generated_java_dir = options.generated_dir
+    else:
+      generated_java_dir = os.path.join(temp_dir, 'gen')
+
     # Incremental means not all files will be extracted, so don't bother
     # clearing out stale generated files.
     if not incremental:
@@ -436,22 +444,30 @@
         attempt_build()
       logging.info('Finished build command')
 
-    # Creating the jar file takes the longest, start it first on a separate
-    # process to unblock the rest of the post-processing steps.
-    jar_file_worker = multiprocessing.Process(
-        target=_CreateJarFile,
-        args=(options.jar_path, options.provider_configurations,
-              options.additional_jar_files, classes_dir))
-    jar_file_worker.start()
+    if options.incremental or save_outputs:
+      # Creating the jar file takes the longest, start it first on a separate
+      # process to unblock the rest of the post-processing steps.
+      jar_file_worker = multiprocessing.Process(
+          target=_CreateJarFile,
+          args=(options.jar_path, options.provider_configurations,
+                options.additional_jar_files, classes_dir))
+      jar_file_worker.start()
+    else:
+      jar_file_worker = None
+      build_utils.Touch(options.jar_path)
 
-    _CreateInfoFile(java_files, options.jar_path, options.chromium_code,
-                    srcjar_files, classes_dir, generated_java_dir)
+    if save_outputs:
+      _CreateInfoFile(java_files, options.jar_path, options.chromium_code,
+                      srcjar_files, classes_dir, generated_java_dir)
+    else:
+      build_utils.Touch(options.jar_path + '.info')
 
     if options.incremental and (not java_files or not incremental):
       # Make sure output exists.
       build_utils.Touch(pdb_path)
 
-    jar_file_worker.join()
+    if jar_file_worker:
+      jar_file_worker.join()
     logging.info('Completed all steps in _OnStaleMd5')
 
 
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 7627efb..087f266d 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -6,7 +6,6 @@
 
 import optparse
 import os
-import re
 import shutil
 import sys
 import tempfile
@@ -101,28 +100,21 @@
   return options
 
 
-def _NormalizeMergedConfig(merged_config_str):
-  stripped_config = re.sub(
-      r'(^\-(injars|libraryjars|print).*\n)|(#.*\n)',
-      '',
-      merged_config_str,
-      flags=re.MULTILINE)
-
-  config_groups = re.findall(
-      r'^(\-.*?(\n|(\{.*?\})))',
-      stripped_config,
-      flags=re.DOTALL | re.MULTILINE)
-
-  return '\n'.join(sorted(g[0].strip() for g in config_groups))
-
-
 def _VerifyExpectedConfigs(expected_path, actual_path, fail_on_exit):
-  diff = diff_utils.DiffFileContents(expected_path, actual_path,
-                                     'Proguard Flags')
+  diff = diff_utils.DiffFileContents(expected_path, actual_path)
   if not diff:
     return
 
-  print diff
+  print """
+{}
+
+Detected Proguard flags change. Please update by running:
+
+cp {} {}
+
+See https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/android/java/README.md#fixing-build-failures
+for more info.
+""".format(diff, os.path.abspath(actual_path), os.path.abspath(expected_path))
   if fail_on_exit:
     sys.exit(1)
 
@@ -147,9 +139,10 @@
 
 
 def _CreateR8Command(options, map_output_path, output_dir, tmp_config_path,
-                     tmp_printconfiguration_path, libraries):
+                     libraries):
   cmd = [
     'java', '-jar', options.r8_path,
+    '--no-desugaring',
     '--no-data-resources',
     '--output', output_dir,
     '--pg-map-output', map_output_path,
@@ -161,12 +154,9 @@
   for config_file in options.proguard_configs:
     cmd += ['--pg-conf', config_file]
 
-  if options.apply_mapping or options.output_config:
+  if options.apply_mapping:
     with open(tmp_config_path, 'w') as f:
-      if options.apply_mapping:
-        f.write('-applymapping ' + options.apply_mapping)
-      if options.output_config:
-        f.write('-printconfiguration ' + tmp_printconfiguration_path)
+      f.write('-applymapping ' + options.apply_mapping)
     cmd += ['--pg-conf', tmp_config_path]
 
   if options.min_api:
@@ -195,19 +185,16 @@
     with build_utils.TempDir() as tmp_dir:
       tmp_mapping_path = os.path.join(tmp_dir, 'mapping.txt')
       tmp_proguard_config_path = os.path.join(tmp_dir, 'proguard_config.txt')
-      tmp_merged_config_path = os.path.join(tmp_dir, 'merged_config.txt')
 
       if options.output_path.endswith('.dex'):
         with build_utils.TempDir() as tmp_dex_dir:
           cmd = _CreateR8Command(options, tmp_mapping_path, tmp_dex_dir,
-                                 tmp_proguard_config_path,
-                                 tmp_merged_config_path, libraries)
+                                 tmp_proguard_config_path, libraries)
           build_utils.CheckOutput(cmd)
           _MoveTempDexFile(tmp_dex_dir, options.output_path)
       else:
         cmd = _CreateR8Command(options, tmp_mapping_path, options.output_path,
-                               tmp_proguard_config_path, tmp_merged_config_path,
-                               libraries)
+                               tmp_proguard_config_path, libraries)
         build_utils.CheckOutput(cmd)
 
       # Copy output files to correct locations.
@@ -217,11 +204,9 @@
         with open(tmp_mapping_path) as tmp:
           mapping.writelines(l for l in tmp if not l.startswith("#"))
 
-      with build_utils.AtomicOutput(options.output_config) as merged_config:
-        with open(tmp_merged_config_path) as tmp:
-          # Sort flags alphabetically to make diffs more stable and easier to
-          # consume. Also strip out lines with build specific paths in them.
-          merged_config.write(_NormalizeMergedConfig(tmp.read()))
+    with build_utils.AtomicOutput(options.output_config) as merged_config:
+      proguard_util.WriteFlagsFile(
+          options.proguard_configs, merged_config, exclude_generated=True)
 
     if options.expected_configs_file:
       _VerifyExpectedConfigs(options.expected_configs_file,
diff --git a/build/android/gyp/util/diff_utils.py b/build/android/gyp/util/diff_utils.py
index 2f511b6..85acb681 100755
--- a/build/android/gyp/util/diff_utils.py
+++ b/build/android/gyp/util/diff_utils.py
@@ -7,7 +7,7 @@
 import difflib
 
 
-def DiffFileContents(expected_path, actual_path, description):
+def DiffFileContents(expected_path, actual_path):
   """Check file contents for equality and return the diff or None."""
   with open(expected_path) as f_expected, open(actual_path) as f_actual:
     expected_lines = f_expected.readlines()
@@ -23,16 +23,4 @@
       tofile=actual_path,
       n=0)
 
-  return """
-Detected change in {}.
-If change is expected, please update the expectations by running:
-
-    cd out/Release
-    cp {} {}
-
-If you have hit this error on a bot and the error is for a public target,
-build locally with enable_chrome_android_internal=false.
-
-Here is the diff:
-{}
-""".format(description, actual_path, expected_path, '\n'.join(diff))
+  return '\n'.join(diff)
diff --git a/build/android/gyp/util/proguard_util.py b/build/android/gyp/util/proguard_util.py
index bce124e..b9fecab 100644
--- a/build/android/gyp/util/proguard_util.py
+++ b/build/android/gyp/util/proguard_util.py
@@ -168,17 +168,7 @@
 
   def _WriteFlagsFile(self, cmd, out):
     # Quite useful for auditing proguard flags.
-    for config in sorted(self._configs):
-      out.write('#' * 80 + '\n')
-      out.write('# ' + config + '\n')
-      out.write('#' * 80 + '\n')
-      with open(config) as config_file:
-        contents = config_file.read().rstrip()
-      # Remove numbers from generated rule comments to make file more
-      # diff'able.
-      contents = re.sub(r' #generated:\d+', '', contents)
-      out.write(contents)
-      out.write('\n\n')
+    WriteFlagsFile(self._configs, out)
     out.write('#' * 80 + '\n')
     out.write('# Command-line\n')
     out.write('#' * 80 + '\n')
@@ -215,3 +205,20 @@
     open(self._outjar + '.seeds', 'a').close()
     open(self._outjar + '.usage', 'a').close()
     open(self._outjar + '.mapping', 'a').close()
+
+
+def WriteFlagsFile(configs, out, exclude_generated=False):
+  for config in sorted(configs):
+    if exclude_generated and config.endswith('.resources.proguard.txt'):
+      continue
+
+    out.write('#' * 80 + '\n')
+    out.write('# ' + config + '\n')
+    out.write('#' * 80 + '\n')
+    with open(config) as config_file:
+      contents = config_file.read().rstrip()
+    # Remove numbers from generated rule comments to make file more
+    # diff'able.
+    contents = re.sub(r' #generated:\d+', '', contents)
+    out.write(contents)
+    out.write('\n\n')
diff --git a/build/android/play_services/LICENSE.sha1 b/build/android/play_services/LICENSE.sha1
deleted file mode 100644
index c46edd84..0000000
--- a/build/android/play_services/LICENSE.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4d3bbf27839164e89417facf4906ab33222fa382
\ No newline at end of file
diff --git a/build/android/play_services/config.json b/build/android/play_services/config.json
deleted file mode 100644
index 85104a12..0000000
--- a/build/android/play_services/config.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "clients": [
-    "play-services-auth-api-phone",
-    "play-services-auth-base",
-    "play-services-auth",
-    "play-services-base",
-    "play-services-basement",
-    "play-services-cast",
-    "play-services-cast-framework",
-    "play-services-fido",
-    "play-services-gcm",
-    "play-services-iid",
-    "play-services-location",
-    "play-services-tasks",
-    "play-services-vision",
-    "play-services-vision-common"
-  ],
-  "version_number": "12.0.1",
-  "version_xml_path": "res/values/version.xml"
-}
diff --git a/build/android/play_services/google_play_services_library.zip.sha1 b/build/android/play_services/google_play_services_library.zip.sha1
deleted file mode 100644
index cdeaa09..0000000
--- a/build/android/play_services/google_play_services_library.zip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-914b84f58cf521f911d97a524562d3117855df77
\ No newline at end of file
diff --git a/build/android/play_services/update.py b/build/android/play_services/update.py
deleted file mode 100755
index 118279c..0000000
--- a/build/android/play_services/update.py
+++ /dev/null
@@ -1,524 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-'''
-Script to help uploading and downloading the Google Play services library to
-and from a Google Cloud storage.
-'''
-
-import argparse
-import logging
-import os
-import re
-import shutil
-import sys
-import zipfile
-
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
-import devil_chromium
-from devil.utils import cmd_helper
-from play_services import utils
-from py_utils import tempfile_ext
-from pylib import constants
-from pylib.constants import host_paths
-from pylib.utils import logging_utils
-from pylib.utils import maven_downloader
-
-sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build'))
-import find_depot_tools  # pylint: disable=import-error,unused-import
-import breakpad
-import download_from_google_storage
-import upload_to_google_storage
-
-
-# Directory where the SHA1 files for the zip and the license are stored
-# It should be managed by git to provided information about new versions.
-SHA1_DIRECTORY = os.path.join(host_paths.DIR_SOURCE_ROOT, 'build', 'android',
-                              'play_services')
-
-# Default bucket used for storing the files.
-GMS_CLOUD_STORAGE = 'chromium-android-tools/play-services'
-
-# Path to the default configuration file. It exposes the currently installed
-# version of the library in a human readable way.
-CONFIG_DEFAULT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'build',
-                                   'android', 'play_services', 'config.json')
-
-LICENSE_FILE_NAME = 'LICENSE'
-ZIP_FILE_NAME = 'google_play_services_library.zip'
-
-LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE)
-
-COM_GOOGLE_ANDROID_GMS = os.path.join('com', 'google', 'android', 'gms')
-EXTRAS_GOOGLE_M2REPOSITORY = os.path.join('extras', 'google', 'm2repository')
-
-def main(raw_args):
-  parser = argparse.ArgumentParser(
-      description=__doc__ + 'Please see the subcommand help for more details.',
-      formatter_class=utils.DefaultsRawHelpFormatter)
-  subparsers = parser.add_subparsers(title='commands')
-
-  # Download arguments
-  parser_download = subparsers.add_parser(
-      'download',
-      help='download the library from the cloud storage',
-      description=Download.__doc__,
-      formatter_class=utils.DefaultsRawHelpFormatter)
-  parser_download.set_defaults(func=Download)
-  AddBasicArguments(parser_download)
-  AddBucketArguments(parser_download)
-
-  # SDK Update arguments
-  parser_sdk = subparsers.add_parser(
-      'sdk',
-      help='get the latest Google Play services SDK using Maven',
-      description=UpdateSdk.__doc__,
-      formatter_class=utils.DefaultsRawHelpFormatter)
-  parser_sdk.set_defaults(func=UpdateSdk)
-  AddBasicArguments(parser_sdk)
-
-  # Upload arguments
-  parser_upload = subparsers.add_parser(
-      'upload',
-      help='upload the library to the cloud storage',
-      description=Upload.__doc__,
-      formatter_class=utils.DefaultsRawHelpFormatter)
-
-  parser_upload.set_defaults(func=Upload)
-  AddBasicArguments(parser_upload)
-  AddBucketArguments(parser_upload)
-
-  args = parser.parse_args(raw_args)
-  if args.verbose:
-    logging.basicConfig(level=logging.DEBUG)
-  logging_utils.ColorStreamHandler.MakeDefault(not _IsBotEnvironment())
-  devil_chromium.Initialize()
-  return args.func(args)
-
-
-def AddBasicArguments(parser):
-  '''
-  Defines the common arguments on subparser rather than the main one. This
-  allows to put arguments after the command: `foo.py upload --debug --force`
-  instead of `foo.py --debug upload --force`
-  '''
-
-  parser.add_argument('--config',
-                      help='JSON Configuration file',
-                      default=CONFIG_DEFAULT_PATH)
-
-  parser.add_argument('--sdk-root',
-                      help='base path to the Android SDK tools root',
-                      default=constants.ANDROID_SDK_ROOT)
-
-  parser.add_argument('-v', '--verbose',
-                      action='store_true',
-                      help='print debug information')
-
-
-def AddBucketArguments(parser):
-  parser.add_argument('--bucket',
-                      help='name of the bucket where the files are stored',
-                      default=GMS_CLOUD_STORAGE)
-
-  parser.add_argument('--dry-run',
-                      action='store_true',
-                      help=('run the script in dry run mode. Files will be '
-                            'copied to a local directory instead of the '
-                            'cloud storage. The bucket name will be as path '
-                            'to that directory relative to the repository '
-                            'root.'))
-
-  parser.add_argument('-f', '--force',
-                      action='store_true',
-                      help='run even if the library is already up to date')
-
-
-def Download(args):
-  '''
-  Downloads the Google Play services library from a Google Cloud Storage bucket
-  and installs it to
-  //third_party/android_tools/sdk/extras/google/m2repository.
-
-  A license check will be made, and the user might have to accept the license
-  if that has not been done before.
-  '''
-
-  if not os.path.isdir(args.sdk_root):
-    logging.debug('Did not find the Android SDK root directory at "%s".',
-                  args.sdk_root)
-    if not args.force:
-      logging.info('Skipping, not on an android checkout.')
-      return 0
-
-  config = utils.ConfigParser(args.config)
-  paths = PlayServicesPaths(args.sdk_root, config.version_number,
-                            config.clients)
-
-  if os.path.isdir(paths.package) and not os.access(paths.package, os.W_OK):
-    logging.error('Failed updating the Google Play Services library. '
-                  'The location is not writable. Please remove the '
-                  'directory (%s) and try again.', paths.package)
-    return -2
-
-  new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, ZIP_FILE_NAME + '.sha1')
-
-  logging.debug('Comparing zip hashes: %s and %s', new_lib_zip_sha1,
-                paths.lib_zip_sha1)
-  if utils.FileEquals(new_lib_zip_sha1, paths.lib_zip_sha1) and not args.force:
-    logging.info('Skipping, the Google Play services library is up to date.')
-    return 0
-
-  bucket_path = _VerifyBucketPathFormat(args.bucket,
-                                        config.version_number,
-                                        args.dry_run)
-
-  with tempfile_ext.NamedTemporaryDirectory() as tmp_root:
-    # setup the destination directory
-    _MakeDirIfAbsent(paths.package)
-
-    # download license file from bucket/{version_number}/license.sha1
-    new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
-
-    license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1')
-    _DownloadFromBucket(bucket_path, license_sha1, new_license,
-                        args.verbose, args.dry_run)
-
-    if (not _IsBotEnvironment() and
-        not _CheckLicenseAgreement(new_license, paths.license,
-                                   config.version_number)):
-        logging.warning('Your version of the Google Play services library is '
-                        'not up to date. You might run into issues building '
-                        'or running the app. Please run `%s download` to '
-                        'retry downloading it.', __file__)
-        return 0
-
-    new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
-    _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip,
-                        args.verbose, args.dry_run)
-
-    try:
-      # Remove the deprecated sdk directory.
-      deprecated_package_path = os.path.join(args.sdk_root, 'extras', 'google',
-                                             'google_play_services')
-      if os.path.exists(deprecated_package_path):
-        shutil.rmtree(deprecated_package_path)
-
-      # We remove the current version of the Google Play services SDK.
-      if os.path.exists(paths.package):
-        shutil.rmtree(paths.package)
-      os.makedirs(paths.package)
-
-      logging.debug('Extracting the library to %s', paths.package)
-      with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file:
-        new_lib_zip_file.extractall(paths.package)
-
-      logging.debug('Copying %s to %s', new_license, paths.license)
-      shutil.copy(new_license, paths.license)
-
-      logging.debug('Copying %s to %s', new_lib_zip_sha1, paths.lib_zip_sha1)
-      shutil.copy(new_lib_zip_sha1, paths.lib_zip_sha1)
-
-      logging.info('Update complete.')
-
-    except Exception as e:  # pylint: disable=broad-except
-      logging.error('Failed updating the Google Play Services library. '
-                    'An error occurred while installing the new version in '
-                    'the SDK directory: %s ', e)
-      return -3
-
-  return 0
-
-
-def _MakeDirIfAbsent(path):
-  try:
-    os.makedirs(path)
-  except OSError as e:
-    if e.errno != os.errno.EEXIST:
-      raise
-
-
-def UpdateSdk(args):
-  '''
-  Uses Maven to download the latest Google Play Services SDK. Its installation
-  path is //third_party/android_tools/sdk/extras/google/m2repository.
-  '''
-
-  # This should function should not run on bots and could fail for many user
-  # and setup related reasons. Also, exceptions here are not caught, so we
-  # disable breakpad to avoid spamming the logs.
-  breakpad.IS_ENABLED = False
-
-  config = utils.ConfigParser(args.config)
-  target_repo = os.path.join(args.sdk_root, EXTRAS_GOOGLE_M2REPOSITORY)
-
-  # Remove the old SDK.
-  # TODO(https://crbug.com/778650) not everything should be deleted.
-  shutil.rmtree(target_repo, ignore_errors=True)
-
-  downloader = maven_downloader.MavenDownloader()
-  artifact_list = [
-      'com.google.android.gms:{}:{}:aar'.format(client, config.version_number)
-      for client in config.clients]
-  downloader.Install(target_repo, artifact_list)
-  return 0
-
-
-def Upload(args):
-  '''
-  Uploads the library from the local Google Play services SDK to a Google Cloud
-  storage bucket. The version of the library and the list of clients to be
-  uploaded will be taken from the configuration file. (see --config parameter)
-
-  By default, a local commit will be made at the end of the operation.
-  '''
-
-  # This should function should not run on bots and could fail for many user
-  # and setup related reasons. Also, exceptions here are not caught, so we
-  # disable breakpad to avoid spamming the logs.
-  breakpad.IS_ENABLED = False
-
-  config = utils.ConfigParser(args.config)
-  paths = PlayServicesPaths(args.sdk_root, config.version_number,
-                            config.clients)
-  logging.debug('-- Loaded paths --\n%s\n------------------', paths)
-
-  with tempfile_ext.NamedTemporaryDirectory() as tmp_root:
-    new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
-    new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
-
-    _ZipLibrary(new_lib_zip, paths.client_paths, paths.package)
-    _ExtractLicenseFile(new_license, paths.source_prop)
-
-    bucket_path = _VerifyBucketPathFormat(args.bucket, config.version_number,
-                                          args.dry_run)
-    files_to_upload = [new_lib_zip, new_license]
-    logging.debug('Uploading %s to %s', files_to_upload, bucket_path)
-    _UploadToBucket(bucket_path, files_to_upload, args.dry_run)
-
-    new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY,
-                                    ZIP_FILE_NAME + '.sha1')
-    new_license_sha1 = os.path.join(SHA1_DIRECTORY,
-                                    LICENSE_FILE_NAME + '.sha1')
-    shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1)
-    shutil.copy(new_license + '.sha1', new_license_sha1)
-
-  logging.info('Update to version %s complete', config.version_number)
-  return 0
-
-
-def _DownloadFromBucket(bucket_path, sha1_file, destination, verbose,
-                        is_dry_run):
-  '''Downloads the file designated by the provided sha1 from a cloud bucket.'''
-
-  download_from_google_storage.download_from_google_storage(
-      input_filename=sha1_file,
-      base_url=bucket_path,
-      gsutil=_InitGsutil(is_dry_run),
-      num_threads=1,
-      directory=None,
-      recursive=False,
-      force=False,
-      output=destination,
-      ignore_errors=False,
-      sha1_file=sha1_file,
-      verbose=verbose,
-      auto_platform=True,
-      extract=False)
-
-
-def _UploadToBucket(bucket_path, files_to_upload, is_dry_run):
-  '''Uploads the files designated by the provided paths to a cloud bucket. '''
-
-  upload_to_google_storage.upload_to_google_storage(
-      input_filenames=files_to_upload,
-      base_url=bucket_path,
-      gsutil=_InitGsutil(is_dry_run),
-      force=False,
-      use_md5=False,
-      num_threads=1,
-      skip_hashing=False,
-      gzip=None)
-
-
-def _InitGsutil(is_dry_run):
-  '''Initialize the Gsutil object as regular or dummy version for dry runs. '''
-
-  if is_dry_run:
-    return DummyGsutil()
-  else:
-    return download_from_google_storage.Gsutil(
-        download_from_google_storage.GSUTIL_DEFAULT_PATH)
-
-
-def _ExtractLicenseFile(license_path, prop_file_path):
-  with open(prop_file_path, 'r') as prop_file:
-    prop_file_content = prop_file.read()
-
-  match = LICENSE_PATTERN.search(prop_file_content)
-  if not match:
-    raise AttributeError('The license was not found in ' +
-                         os.path.abspath(prop_file_path))
-
-  with open(license_path, 'w') as license_file:
-    license_file.write(match.group('text'))
-
-
-def _CheckLicenseAgreement(expected_license_path, actual_license_path,
-                           version_number):
-  '''
-  Checks that the new license is the one already accepted by the user. If it
-  isn't, it prompts the user to accept it. Returns whether the expected license
-  has been accepted.
-  '''
-
-  if utils.FileEquals(expected_license_path, actual_license_path):
-    return True
-
-  with open(expected_license_path) as license_file:
-    # Uses plain print rather than logging to make sure this is not formatted
-    # by the logger.
-    print ('Updating the Google Play services SDK to '
-           'version %s.' % version_number)
-
-    # The output is buffered when running as part of gclient hooks. We split
-    # the text here and flush is explicitly to avoid having part of it dropped
-    # out.
-    # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'.
-    for license_part in license_file.read().split('\\n'):
-      print license_part
-      sys.stdout.flush()
-
-  # Need to put the prompt on a separate line otherwise the gclient hook buffer
-  # only prints it after we received an input.
-  print ('Do you accept the license for version %s of the Google Play services '
-         'client library? [y/n]: ' % version_number)
-  sys.stdout.flush()
-  return raw_input('> ') in ('Y', 'y')
-
-
-def _IsBotEnvironment():
-  return bool(os.environ.get('CHROME_HEADLESS'))
-
-
-def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run):
-  '''
-  Formats and checks the download/upload path depending on whether we are
-  running in dry run mode or not. Returns a supposedly safe path to use with
-  Gsutil.
-  '''
-
-  if is_dry_run:
-    bucket_path = os.path.abspath(os.path.join(bucket_name,
-                                               str(version_number)))
-    if not os.path.isdir(bucket_path):
-      os.makedirs(bucket_path)
-  else:
-    if bucket_name.startswith('gs://'):
-      # We enforce the syntax without gs:// for consistency with the standalone
-      # download/upload scripts and to make dry run transition easier.
-      raise AttributeError('Please provide the bucket name without the gs:// '
-                           'prefix (e.g. %s)' % GMS_CLOUD_STORAGE)
-    bucket_path = 'gs://%s/%s' % (bucket_name, version_number)
-
-  return bucket_path
-
-def _ZipLibrary(zip_name, files, zip_root):
-  with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf:
-    for file_name in files:
-      zipf.write(file_name, os.path.relpath(file_name, zip_root))
-
-
-class PlayServicesPaths(object):
-  '''
-  Describes the different paths to be used in the update process.
-
-         Filesystem hierarchy                        | Exposed property / notes
-  ---------------------------------------------------|-------------------------
-  [sdk_root]                                         | sdk_root / (1)
-   +- extras                                         |
-      +- google                                      |
-         +- m2repository                             | package / (2)
-            +- source.properties                     | source_prop / (3)
-            +- LICENSE                               | license / (4)
-            +- google_play_services_library.zip.sha1 | lib_zip_sha1 / (5)
-            +- com/google/android/gms/               |
-               +- [play-services-foo]                |
-                  +- [X.Y.Z]                         |
-                     +- play-services-foo-X.Y.Z.aar  | client_paths / (6)
-
-  Notes:
-
-   1. sdk_root: Path provided as a parameter to the script (--sdk_root)
-   2. package: This directory contains the Google Play services SDK itself.
-      When downloaded via the Android SDK manager, it will be a complete maven,
-      repository with the different versions of the library. When the update
-      script downloads the library from our cloud storage, it is cleared.
-   3. source_prop: File created by the Android SDK manager that contains
-      the package information, such as the version info and the license.
-   4. license: File created by the update script. Contains the license accepted
-      by the user.
-   5. lib_zip_sha1: sha1 of the library that has been installed by the
-      update script. It is compared with the one required by the config file to
-      check if an update is necessary.
-   6. client_paths: The client library jars we care about. They are zipped
-      zipped together and uploaded to the cloud storage.
-
-  '''
-
-  def __init__(self, sdk_root, version_number, client_names):
-    '''
-    sdk_root: path to the root of the sdk directory
-    version_number: version of the library supposed to be installed locally.
-    client_names: names of client libraries to be uploaded. See
-        utils.ConfigParser for more info.
-    '''
-    self.sdk_root = sdk_root
-    self.version_number = version_number
-
-    self.package = os.path.join(sdk_root, EXTRAS_GOOGLE_M2REPOSITORY)
-    self.lib_zip_sha1 = os.path.join(self.package, ZIP_FILE_NAME + '.sha1')
-    self.license = os.path.join(self.package, LICENSE_FILE_NAME)
-    self.source_prop = os.path.join(self.package, 'source.properties')
-
-    self.client_paths = []
-    for client in client_names:
-      self.client_paths.append(os.path.join(
-          self.package, COM_GOOGLE_ANDROID_GMS, client, version_number,
-          '{}-{}.aar'.format(client, version_number)))
-
-  def __repr__(self):
-    return ("\nsdk_root: " + self.sdk_root +
-            "\nversion_number: " + self.version_number +
-            "\npackage: " + self.package +
-            "\nlib_zip_sha1: " + self.lib_zip_sha1 +
-            "\nlicense: " + self.license +
-            "\nsource_prop: " + self.source_prop +
-            "\nclient_paths: \n - " + '\n - '.join(self.client_paths))
-
-
-class DummyGsutil(download_from_google_storage.Gsutil):
-  '''
-  Class that replaces Gsutil to use a local directory instead of an online
-  bucket. It relies on the fact that Gsutil commands are very similar to shell
-  ones, so for the ones used here (ls, cp), it works to just use them with a
-  local directory.
-  '''
-
-  def __init__(self):
-    super(DummyGsutil, self).__init__(
-        download_from_google_storage.GSUTIL_DEFAULT_PATH)
-
-  def call(self, *args):
-    logging.debug('Calling command "%s"', str(args))
-    return cmd_helper.GetCmdStatusOutputAndError(args)
-
-  def check_call(self, *args):
-    logging.debug('Calling command "%s"', str(args))
-    return cmd_helper.GetCmdStatusOutputAndError(args)
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/play_services/update_test.py b/build/android/play_services/update_test.py
deleted file mode 100755
index bb76f819..0000000
--- a/build/android/play_services/update_test.py
+++ /dev/null
@@ -1,391 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-'''Unittests for update.py.
-
-They set up a temporary directory that is used to mock a bucket, the directory
-containing the configuration files and the android sdk directory.
-
-Tests run the script with various inputs and check the status of the filesystem
-'''
-
-import contextlib
-import logging
-import os
-import shutil
-import sys
-import tempfile
-import unittest
-import zipfile
-
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
-from play_services import update
-import devil_chromium  # pylint: disable=import-error,unused-import
-from devil.utils import cmd_helper
-
-
-class TestFunctions(unittest.TestCase):
-  DEFAULT_CONFIG_VERSION = '1.2.3'
-  DEFAULT_LICENSE = 'Default License'
-  DEFAULT_ZIP_SHA1 = 'zip0and0filling0to0forty0chars0000000000'
-
-  def __init__(self, *args, **kwargs):
-    super(TestFunctions, self).__init__(*args, **kwargs)
-    self.paths = None  # Initialized in SetUpWorkdir
-    self.workdir = None  # Initialized in setUp
-
-    # Uncomment for debug logs.
-    # logging.basicConfig(level=logging.DEBUG)
-
-  #override
-  def setUp(self):
-    self.workdir = tempfile.mkdtemp()
-
-  #override
-  def tearDown(self):
-    shutil.rmtree(self.workdir)
-    self.workdir = None
-
-  def testUpload(self):
-    version = '2.3.4'
-    self.SetUpWorkdir(
-        gms_lib=True,
-        config_version=version,
-        source_prop=True)
-
-    status = update.main([
-        'upload',
-        '--dry-run',
-        '--bucket', self.paths.bucket,
-        '--config', self.paths.config_file,
-        '--sdk-root', self.paths.gms.sdk_root
-    ])
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # bucket should contain license, name = content from LICENSE.sha1
-    self.assertTrue(os.path.isfile(self.paths.config_license_sha1))
-    license_sha1 = _GetFileContent(self.paths.config_license_sha1)
-    bucket_license = os.path.join(self.paths.bucket, version, license_sha1)
-    self.assertTrue(os.path.isfile(bucket_license))
-    self.assertEqual(_GetFileContent(bucket_license), self.DEFAULT_LICENSE)
-
-    # bucket should contain zip, name = content from zip.sha1
-    self.assertTrue(os.path.isfile(self.paths.config_zip_sha1))
-    bucket_zip = os.path.join(self.paths.bucket, str(version),
-                              _GetFileContent(self.paths.config_zip_sha1))
-    self.assertTrue(os.path.isfile(bucket_zip))
-
-    # unzip, should contain expected files
-    with zipfile.ZipFile(bucket_zip, "r") as bucket_zip_file:
-      self.assertEqual(bucket_zip_file.namelist(),
-                       ['com/google/android/gms/client/2.3.4/client-2.3.4.aar'])
-
-  def testDownload(self):
-    self.SetUpWorkdir(populate_bucket=True)
-
-    with _MockedInput('y'):
-      status = update.main([
-          'download',
-          '--dry-run',
-          '--bucket', self.paths.bucket,
-          '--config', self.paths.config_file,
-          '--sdk-root', self.paths.gms.sdk_root,
-      ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # sdk_root should contain zip contents, zip sha1, license
-    self.assertTrue(os.path.isfile(self.paths.gms.client_paths[0]))
-    self.assertTrue(os.path.isfile(self.paths.gms.lib_zip_sha1))
-    self.assertTrue(os.path.isfile(self.paths.gms.license))
-    self.assertEquals(_GetFileContent(self.paths.gms.license),
-                      self.DEFAULT_LICENSE)
-
-  def testDownloadBot(self):
-    self.SetUpWorkdir(populate_bucket=True, bot_env=True)
-
-    # No need to type 'y' on bots
-    status = update.main([
-        'download',
-        '--dry-run',
-        '--bucket', self.paths.bucket,
-        '--config', self.paths.config_file,
-        '--sdk-root', self.paths.gms.sdk_root,
-    ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # sdk_root should contain zip contents, zip sha1, license
-    self.assertTrue(os.path.isfile(self.paths.gms.client_paths[0]))
-    self.assertTrue(os.path.isfile(self.paths.gms.lib_zip_sha1))
-    self.assertTrue(os.path.isfile(self.paths.gms.license))
-    self.assertEquals(_GetFileContent(self.paths.gms.license),
-                      self.DEFAULT_LICENSE)
-
-  def testDownloadAlreadyUpToDate(self):
-    self.SetUpWorkdir(
-        populate_bucket=True,
-        existing_zip_sha1=self.DEFAULT_ZIP_SHA1)
-
-    status = update.main([
-        'download',
-        '--dry-run',
-        '--bucket', self.paths.bucket,
-        '--config', self.paths.config_file,
-        '--sdk-root', self.paths.gms.sdk_root,
-    ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # there should not be new files downloaded to sdk_root
-    self.assertFalse(os.path.isfile(os.path.join(self.paths.gms.client_paths[0],
-                                                 'dummy_file')))
-    self.assertFalse(os.path.isfile(self.paths.gms.license))
-
-  def testDownloadAcceptedLicense(self):
-    self.SetUpWorkdir(
-        populate_bucket=True,
-        existing_license=self.DEFAULT_LICENSE)
-
-    # License already accepted, no need to type
-    status = update.main([
-        'download',
-        '--dry-run',
-        '--bucket', self.paths.bucket,
-        '--config', self.paths.config_file,
-        '--sdk-root', self.paths.gms.sdk_root,
-    ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # sdk_root should contain zip contents, zip sha1, license
-    self.assertTrue(os.path.isfile(self.paths.gms.client_paths[0]))
-    self.assertTrue(os.path.isfile(self.paths.gms.lib_zip_sha1))
-    self.assertTrue(os.path.isfile(self.paths.gms.license))
-    self.assertEquals(_GetFileContent(self.paths.gms.license),
-                      self.DEFAULT_LICENSE)
-
-  def testDownloadNewLicense(self):
-    self.SetUpWorkdir(
-        populate_bucket=True,
-        existing_license='Old license')
-
-    with _MockedInput('y'):
-      status = update.main([
-          'download',
-          '--dry-run',
-          '--bucket', self.paths.bucket,
-          '--config', self.paths.config_file,
-          '--sdk-root', self.paths.gms.sdk_root,
-      ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # sdk_root should contain zip contents, zip sha1, NEW license
-    self.assertTrue(os.path.isfile(self.paths.gms.client_paths[0]))
-    self.assertTrue(os.path.isfile(self.paths.gms.lib_zip_sha1))
-    self.assertTrue(os.path.isfile(self.paths.gms.license))
-    self.assertEquals(_GetFileContent(self.paths.gms.license),
-                      self.DEFAULT_LICENSE)
-
-  def testDownloadRefusedLicense(self):
-    self.SetUpWorkdir(
-        populate_bucket=True,
-        existing_license='Old license')
-
-    with _MockedInput('n'):
-      status = update.main([
-          'download',
-          '--dry-run',
-          '--bucket', self.paths.bucket,
-          '--config', self.paths.config_file,
-          '--sdk-root', self.paths.gms.sdk_root,
-      ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-
-    # there should not be new files downloaded to sdk_root
-    self.assertFalse(os.path.isfile(os.path.join(self.paths.gms.client_paths[0],
-                                                 'dummy_file')))
-    self.assertEquals(_GetFileContent(self.paths.gms.license),
-                      'Old license')
-
-  def testDownloadNoAndroidSDK(self):
-    self.SetUpWorkdir(
-        populate_bucket=True,
-        existing_license='Old license')
-
-    non_existing_sdk_root = os.path.join(self.workdir, 'non_existing_sdk_root')
-    # Should not run, no typing needed
-    status = update.main([
-        'download',
-        '--dry-run',
-        '--bucket', self.paths.bucket,
-        '--config', self.paths.config_file,
-        '--sdk-root', non_existing_sdk_root,
-    ])
-
-    self.assertEqual(status, 0, 'the command should have succeeded.')
-    self.assertFalse(os.path.isdir(non_existing_sdk_root))
-
-  def SetUpWorkdir(self,
-                   bot_env=False,
-                   config_version=DEFAULT_CONFIG_VERSION,
-                   existing_license=None,
-                   existing_zip_sha1=None,
-                   gms_lib=False,
-                   populate_bucket=False,
-                   source_prop=None):
-    '''Prepares workdir by putting it in the specified state
-
-    Args:
-      - general
-        bot_env: sets or unsets CHROME_HEADLESS
-
-      - bucket
-        populate_bucket: boolean. Populate the bucket with a zip and license
-                         file. The sha1s will be copied to the config directory
-
-      - config
-        config_version: number. Version of the current SDK. Defaults to
-                        `self.DEFAULT_CONFIG_VERSION`
-
-      - sdk_root
-        existing_license: string. Create a LICENSE file setting the specified
-                          text as content of the currently accepted license.
-        existing_zip_sha1: string. Create a sha1 file setting the specified
-                           hash as hash of the SDK supposed to be installed
-        gms_lib: boolean. Create a dummy file in the location of the play
-                 services SDK.
-        source_prop: boolean. Create a source.properties file that contains
-                     the license to upload.
-    '''
-    client_name = 'client'
-    self.paths = Paths(self.workdir, config_version, [client_name])
-
-    # Create the main directories
-    _MakeDirs(self.paths.gms.sdk_root)
-    _MakeDirs(self.paths.config_dir)
-    _MakeDirs(self.paths.bucket)
-
-    # is not configured via argument.
-    update.SHA1_DIRECTORY = self.paths.config_dir
-
-    os.environ['CHROME_HEADLESS'] = '1' if bot_env else ''
-
-    if config_version:
-      _MakeDirs(os.path.dirname(self.paths.config_file))
-      with open(self.paths.config_file, 'w') as stream:
-        stream.write(('{"clients": ["%s"],'
-                      '"version_number": "%s"}'
-                      '\n') % (client_name, config_version))
-
-    if existing_license:
-      _MakeDirs(self.paths.gms.package)
-      with open(self.paths.gms.license, 'w') as stream:
-        stream.write(existing_license)
-
-    if existing_zip_sha1:
-      _MakeDirs(self.paths.gms.package)
-      with open(self.paths.gms.lib_zip_sha1, 'w') as stream:
-        stream.write(existing_zip_sha1)
-
-    if gms_lib:
-      _MakeDirs(os.path.dirname(self.paths.gms.client_paths[0]))
-      with open(self.paths.gms.client_paths[0], 'w') as stream:
-        stream.write('foo\n')
-
-    if source_prop:
-      _MakeDirs(os.path.dirname(self.paths.gms.source_prop))
-      with open(self.paths.gms.source_prop, 'w') as stream:
-        stream.write('Foo=Bar\n'
-                     'Pkg.License=%s\n'
-                     'Baz=Fizz\n' % self.DEFAULT_LICENSE)
-
-    if populate_bucket:
-      _MakeDirs(self.paths.config_dir)
-      bucket_dir = os.path.join(self.paths.bucket, str(config_version))
-      _MakeDirs(bucket_dir)
-
-      # TODO(dgn) should we use real sha1s? comparison with the real sha1 is
-      # done but does not do anything other than displaying a message.
-      config_license_sha1 = 'license0and0filling0to0forty0chars000000'
-      with open(self.paths.config_license_sha1, 'w') as stream:
-        stream.write(config_license_sha1)
-
-      with open(os.path.join(bucket_dir, config_license_sha1), 'w') as stream:
-        stream.write(self.DEFAULT_LICENSE)
-
-      config_zip_sha1 = self.DEFAULT_ZIP_SHA1
-      with open(self.paths.config_zip_sha1, 'w') as stream:
-        stream.write(config_zip_sha1)
-
-      pre_zip_client = os.path.join(
-          self.workdir,
-          'pre_zip_lib',
-          os.path.relpath(self.paths.gms.client_paths[0],
-                          self.paths.gms.package))
-      pre_zip_lib = os.path.dirname(pre_zip_client)
-      post_zip_lib = os.path.join(bucket_dir, config_zip_sha1)
-      print(pre_zip_lib, post_zip_lib)
-      _MakeDirs(pre_zip_lib)
-      with open(pre_zip_client, 'w') as stream:
-        stream.write('foo\n')
-
-      # pylint: disable=protected-access
-      update._ZipLibrary(post_zip_lib, [pre_zip_client], os.path.join(
-          self.workdir, 'pre_zip_lib'))
-
-    if logging.getLogger().isEnabledFor(logging.DEBUG):
-      cmd_helper.Call(['tree', self.workdir])
-
-
-class Paths(object):
-  '''Declaration of the paths commonly manipulated in the tests.'''
-
-  def __init__(self, workdir, version, clients):
-    self.bucket = os.path.join(workdir, 'bucket')
-
-    self.config_dir = os.path.join(workdir, 'config')
-    self.config_file = os.path.join(self.config_dir, 'config.json')
-    self.config_license_sha1 = os.path.join(self.config_dir, 'LICENSE.sha1')
-    self.config_zip_sha1 = os.path.join(
-        self.config_dir,
-        'google_play_services_library.zip.sha1')
-    self.gms = update.PlayServicesPaths(os.path.join(workdir, 'sdk_root'),
-                                        version, clients)
-
-
-def _GetFileContent(file_path):
-  with open(file_path, 'r') as stream:
-    return stream.read()
-
-
-def _MakeDirs(path):
-  '''Avoids having to do the error handling everywhere.'''
-  if not os.path.exists(path):
-    os.makedirs(path)
-
-
-@contextlib.contextmanager
-def _MockedInput(typed_string):
-  '''Makes raw_input return |typed_string| while inside the context.'''
-  try:
-    if isinstance(__builtins__, dict):
-      original_raw_input = __builtins__['raw_input']
-      __builtins__['raw_input'] = lambda _: typed_string
-    else:
-      original_raw_input = __builtins__.raw_input
-      __builtins__.raw_input = lambda _: typed_string
-    yield
-  finally:
-    if isinstance(__builtins__, dict):
-      __builtins__['raw_input'] = original_raw_input
-    else:
-      __builtins__.raw_input = original_raw_input
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index c998033..fdaaca3 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1284,31 +1284,22 @@
                                ])
         script = "//build/android/gyp/dex.py"
         depfile = "$target_gen_dir/$target_name.d"
-        inputs = [
-          invoker.build_config,
-        ]
+        inputs = []
         outputs = [
           invoker.output,
         ]
 
         _rebased_output = rebase_path(invoker.output, root_build_dir)
-        _rebased_build_config =
-            rebase_path(invoker.build_config, root_build_dir)
+
         args = [
           "--depfile",
           rebase_path(depfile, root_build_dir),
           "--dex-path",
           _rebased_output,
-          "--sdk-jars=@FileArg($_rebased_build_config:android:sdk_jars)",
         ]
 
-        # Some targets use java_runtime_classpath and others use
-        # javac_full_interface_classpath. Either of them will work for desugar.
         if (_proguard_enabled) {
           deps += [ ":${_proguard_target_name}" ]
-          args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:java_runtime_classpath)" ]
-        } else {
-          args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)" ]
         }
 
         if (_enable_multidex) {
@@ -1475,7 +1466,7 @@
     # Turned off because of existing code which fails the assertion
     _enable_thread_annotations = false
 
-    _desugar = defined(invoker.desugar) && invoker.desugar
+    _desugar = defined(invoker.supports_android) && invoker.supports_android
     _emma_instrument = invoker.emma_instrument
     _enable_bytecode_rewriter =
         _enable_assert || _enable_custom_resources || _enable_thread_annotations
@@ -2716,12 +2707,6 @@
     _build_config = invoker.build_config
     _chromium_code = invoker.chromium_code
 
-    if (defined(invoker.enable_errorprone)) {
-      _enable_errorprone = invoker.enable_errorprone
-    } else {
-      _enable_errorprone = use_errorprone_java_compiler && _chromium_code
-    }
-
     _provider_configurations = []
     if (defined(invoker.provider_configurations)) {
       _provider_configurations = invoker.provider_configurations
@@ -2761,12 +2746,6 @@
     if (defined(invoker.javac_args)) {
       _javac_args = invoker.javac_args
     }
-    if (defined(invoker.errorprone_args)) {
-      not_needed(invoker, [ "errorprone_args" ])
-      if (_enable_errorprone) {
-        _javac_args += invoker.errorprone_args
-      }
-    }
 
     action_with_pydeps(target_name) {
       script = "//build/android/gyp/javac.py"
@@ -2819,7 +2798,7 @@
       if (_chromium_code) {
         args += [ "--chromium-code=1" ]
       }
-      if (_enable_errorprone) {
+      if (invoker.enable_errorprone) {
         deps += [ "//third_party/errorprone:errorprone($default_toolchain)" ]
         deps += [ "//tools/android/errorprone_plugin:errorprone_plugin_java($default_toolchain)" ]
         _rebased_errorprone_processorpath = [
@@ -3296,45 +3275,77 @@
     # TODO(agrieve): Enable lint for _has_sources rather than just _java_files.
     _lint_enabled = _java_files != [] && _supports_android && _chromium_code &&
                     !disable_android_lint
+    if (defined(invoker.enable_errorprone)) {
+      _enable_errorprone = invoker.enable_errorprone
+    } else {
+      _enable_errorprone =
+          _java_files != [] && _chromium_code && use_errorprone_java_compiler
+    }
 
     if (_has_sources) {
-      _compile_java_target = "${_main_target_name}__compile_java"
-      compile_java(_compile_java_target) {
-        forward_variables_from(invoker,
-                               [
-                                 "additional_jar_files",
-                                 "apk_name",
-                                 "enable_errorprone",
-                                 "enable_incremental_javac_override",
-                                 "errorprone_args",
-                                 "processor_args_javac",
-                                 "provider_configurations",
-                                 "javac_args",
-                               ])
-        main_target_name = _main_target_name
-        build_config = _build_config
-        java_files = _java_files
-        if (_java_files != []) {
-          java_sources_file = _java_sources_file
-        }
-        srcjar_deps = _srcjar_deps
-        chromium_code = _chromium_code
-        requires_android = _requires_android
-        deps = _accumulated_deps + _accumulated_public_deps
-        javac_jar_path = _javac_jar_path
+      _type = invoker.type
+      template("compile_java_helper") {
+        compile_java(target_name) {
+          forward_variables_from(invoker, "*")
+          enable_errorprone = invoker.enable_errorprone
+          javac_jar_path = invoker.javac_jar_path
 
-        # android_apk and junit_binary pass R.java srcjars via srcjar_deps.
-        if (invoker.type == "java_library" && _requires_android) {
-          _rebased_build_config = rebase_path(_build_config, root_build_dir)
-          srcjar_filearg = "@FileArg($_rebased_build_config:deps_info:owned_resource_srcjars)"
+          main_target_name = _main_target_name
+          build_config = _build_config
+          java_files = _java_files
+          if (_java_files != []) {
+            java_sources_file = _java_sources_file
+          }
+          srcjar_deps = _srcjar_deps
+          chromium_code = _chromium_code
+          requires_android = _requires_android
+          deps = _accumulated_deps + _accumulated_public_deps
+
+          # android_apk and junit_binary pass R.java srcjars via srcjar_deps.
+          if (_type == "java_library" && _requires_android) {
+            _rebased_build_config = rebase_path(_build_config, root_build_dir)
+            srcjar_filearg = "@FileArg($_rebased_build_config:deps_info:owned_resource_srcjars)"
+          }
         }
       }
+      _analysis_public_deps = []
+      _compile_java_target = "${_main_target_name}__compile_java"
+      _compile_java_forward_variables = [
+        "additional_jar_files",
+        "apk_name",
+        "enable_incremental_javac_override",
+        "processor_args_javac",
+        "provider_configurations",
+        "javac_args",
+      ]
+      compile_java_helper(_compile_java_target) {
+        forward_variables_from(invoker, _compile_java_forward_variables)
+        enable_errorprone = false
+        javac_jar_path = _javac_jar_path
+      }
+      if (_enable_errorprone) {
+        _compile_java_errorprone_target =
+            "${_main_target_name}__compile_java_errorprone"
+        compile_java_helper(_compile_java_errorprone_target) {
+          forward_variables_from(invoker, _compile_java_forward_variables)
+          enable_errorprone = true
+          if (defined(invoker.errorprone_args)) {
+            if (!defined(javac_args)) {
+              javac_args = []
+            }
+            javac_args += invoker.errorprone_args
+          }
+          javac_jar_path = _javac_jar_path + ".errorprone.jar"
+        }
+        _analysis_public_deps += [ ":$_compile_java_errorprone_target" ]
+      }
       if (defined(invoker.android_manifest_for_lint)) {
         _android_manifest_for_lint = invoker.android_manifest_for_lint
         assert(_android_manifest_for_lint != "")  # Mark as used.
       }
       if (_lint_enabled) {
-        android_lint("${_main_target_name}__lint") {
+        _android_lint_target = "${_main_target_name}__lint"
+        android_lint(_android_lint_target) {
           if (invoker.type == "android_apk" ||
               invoker.type == "android_app_bundle_module") {
             forward_variables_from(invoker, [ "android_manifest" ])
@@ -3353,14 +3364,15 @@
             lint_suppressions_file = invoker.lint_suppressions_file
           }
         }
+        _analysis_public_deps += [ ":$_android_lint_target" ]
+      }
 
+      if (_analysis_public_deps != []) {
         # Use an intermediate group() rather as the data_deps target in order to
-        # avoid lint artifacts showing up as runtime_deps (while still having lint
-        # run in parallel to other targets).
+        # avoid errorprone or lint artifacts showing up as runtime_deps (while
+        # still having them run in parallel to other targets).
         group("${_main_target_name}__analysis") {
-          public_deps = [
-            ":${_main_target_name}__lint",
-          ]
+          public_deps = _analysis_public_deps
         }
       }
 
@@ -3386,11 +3398,6 @@
         _accumulated_public_deps += [ ":$_copy_system_library_target_name" ]
       } else {
         _process_prebuilt_target_name = "${target_name}__process_prebuilt"
-
-        # Skip desugaring for debug builds using r8 to increase incremental
-        # build speed. Release builds still need desugar.jar for binary size.
-        _using_r8 = !defined(invoker.proguard_jar_path) || use_r8
-        _desugar = !(_using_r8 && is_java_debug) && _supports_android
         process_java_prebuilt(_process_prebuilt_target_name) {
           forward_variables_from(invoker,
                                  [
@@ -3400,7 +3407,7 @@
                                    "jar_included_patterns",
                                  ])
           is_prebuilt = _is_prebuilt
-          desugar = _desugar
+          supports_android = _supports_android
           enable_build_hooks = _enable_build_hooks
           enable_build_hooks_android = _enable_build_hooks_android
           build_config = _build_config
@@ -3423,9 +3430,7 @@
           dex("${target_name}__dex") {
             input_jars = [ _final_jar_path ]
             output = _dex_path
-            build_config = _build_config
             deps = [
-              ":$_build_config_target_name",
               ":$_process_prebuilt_target_name",
             ]
           }
@@ -3457,7 +3462,7 @@
           # BuildConfig, NativeLibraries, etc.
           input_jar = _unprocessed_jar_path
           output_jar = _final_ijar_path
-          if (_lint_enabled) {
+          if (_lint_enabled || _enable_errorprone) {
             if (!defined(data_deps)) {
               data_deps = []
             }
@@ -3514,7 +3519,7 @@
                                  "visibility",
                                ])
         public_deps = _accumulated_public_deps
-        if (_lint_enabled) {
+        if (_lint_enabled || _enable_errorprone) {
           if (!defined(data_deps)) {
             data_deps = []
           }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index d617db9..9c8e867 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4100,8 +4100,12 @@
           invoker.command_line_flags_file,
         ]
       }
-
-      # TODO(digit): Add --proguard-mapping-path argument.
+      if (_proguard_enabled) {
+        args += [
+          "--proguard-mapping-path",
+          rebase_path(_proguard_mapping_path, root_build_dir),
+        ]
+      }
     }
 
     group(target_name) {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 4cd2180..54ce77b6 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -736,8 +736,10 @@
   }
 
   # Pass flag to LLD to work around issue in Valgrind related to
-  # location of debug symbols.
-  if (use_lld && ro_segment_workaround_for_valgrind) {
+  # location of debug symbols. This is also enabled for Android
+  # builds to allow debuggerd to properly symbolize stack crashes
+  # on this platform (http://crbug.com/919499).
+  if (use_lld && (ro_segment_workaround_for_valgrind || is_android)) {
     ldflags += [ "-Wl,--no-rosegment" ]
   }
 
diff --git a/build/config/fuchsia/build_manifest.py b/build/config/fuchsia/build_manifest.py
index f5129d48..72dd8ac 100644
--- a/build/config/fuchsia/build_manifest.py
+++ b/build/config/fuchsia/build_manifest.py
@@ -179,13 +179,13 @@
     # Compute the set of dynamic libraries used by the application or its
     # transitive dependencies (dist libs and components), and merge the result
     # with |expanded_files| so that they are included in the manifest.
-
-    # TODO(https://crbug.com/861931): Temporarily just include all |dist_libs|.
-    #expanded_files = expanded_files.union(
-    #    ComputeTransitiveLibDeps(
-    #        app_filename,
-    #        {os.path.basename(f): f for f in expanded_files.union(dist_libs)}))
-    expanded_files = expanded_files.union(dist_libs)
+    #
+    # TODO(crbug.com/861931): Make sure that deps of the files in data_deps
+    # (binaries and libraries) are included as well.
+    expanded_files = expanded_files.union(
+       ComputeTransitiveLibDeps(
+           app_filename,
+           {os.path.basename(f): f for f in expanded_files.union(dist_libs)}))
 
     # Format and write out the manifest contents.
     gen_dir = os.path.join(out_dir, "gen")
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index f7b93d6..5ac1704 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-5032bdbecfd633c2c78645577809ef1369ea5a49
\ No newline at end of file
+a3228e6b05854fe8b95d9ce56af04f668f430296
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index fd396b1..d45e5e57f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7a36022208110134fdb1e784c71af6c5a50e5dab
\ No newline at end of file
+502be66881872fefb134364ed70520a7ed122f30
\ No newline at end of file
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc
index d54f4e0..9834eef 100644
--- a/build/sanitizers/lsan_suppressions.cc
+++ b/build/sanitizers/lsan_suppressions.cc
@@ -81,9 +81,6 @@
     // http://crbug.com/355641
     "leak:TrayAccessibilityTest\n"
 
-    // http://crbug.com/354644
-    "leak:CertificateViewerUITest::ShowModalCertificateViewer\n"
-
     // http://crbug.com/356306
     "leak:service_manager::SetProcessTitleFromCommandLine\n"
 
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 83efcd4..3389ee0 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -5,8 +5,8 @@
 # Toolchain-related configuration that may be needed outside the context of the
 # toolchain() rules themselves.
 
-import("//build_overrides/build.gni")
 import("//build/config/chrome_build.gni")
+import("//build_overrides/build.gni")
 
 declare_args() {
   # If this is set to true, or if LLVM_FORCE_HEAD_REVISION is set to 1
@@ -46,7 +46,12 @@
 declare_args() {
   if (is_clang) {
     # Clang compiler version. Clang files are placed at version-dependent paths.
-    clang_version = "8.0.0"
+    if (llvm_force_head_revision) {
+      clang_version = "9.0.0"
+    } else {
+      # TODO(hans): Trunk version was updated; remove after the next roll.
+      clang_version = "8.0.0"
+    }
   }
 }
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index f234c7d..a03bd1f 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -150,6 +150,8 @@
     "raster/gpu_raster_buffer_provider.h",
     "raster/one_copy_raster_buffer_provider.cc",
     "raster/one_copy_raster_buffer_provider.h",
+    "raster/paint_worklet_image_provider.cc",
+    "raster/paint_worklet_image_provider.h",
     "raster/playback_image_provider.cc",
     "raster/playback_image_provider.h",
     "raster/raster_buffer.cc",
diff --git a/cc/OWNERS b/cc/OWNERS
index 18066cf..e12309d 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -8,6 +8,7 @@
 # layers
 enne@chromium.org
 danakj@chromium.org
+pdr@chromium.org
 
 # mac-specific
 ccameron@chromium.org
@@ -28,6 +29,7 @@
 # property trees
 chrishtr@chromium.org
 weiliangc@chromium.org
+pdr@chromium.org
 
 # animation
 flackr@chromium.org
diff --git a/cc/animation/keyframe_model.cc b/cc/animation/keyframe_model.cc
index 06c9a54..bdfacbbe 100644
--- a/cc/animation/keyframe_model.cc
+++ b/cc/animation/keyframe_model.cc
@@ -165,10 +165,17 @@
              (ConvertMonotonicTimeToLocalTime(monotonic_time) + time_offset_);
 }
 
+KeyframeModel::Phase KeyframeModel::CalculatePhaseForTesting(
+    base::TimeDelta local_time) const {
+  return CalculatePhase(local_time);
+}
+
 KeyframeModel::Phase KeyframeModel::CalculatePhase(
     base::TimeDelta local_time) const {
   base::TimeDelta before_active_boundary_time =
-      std::max(-time_offset_, base::TimeDelta());
+      (time_offset_ == base::TimeDelta::Min()
+           ? base::TimeDelta::Max()
+           : std::max(-time_offset_, base::TimeDelta()));
   if (local_time < before_active_boundary_time ||
       (local_time == before_active_boundary_time && playback_rate_ < 0)) {
     return KeyframeModel::Phase::BEFORE;
diff --git a/cc/animation/keyframe_model.h b/cc/animation/keyframe_model.h
index b970ca6e..cdbb2d5 100644
--- a/cc/animation/keyframe_model.h
+++ b/cc/animation/keyframe_model.h
@@ -172,6 +172,9 @@
   }
   bool affects_pending_elements() const { return affects_pending_elements_; }
 
+  KeyframeModel::Phase CalculatePhaseForTesting(
+      base::TimeDelta local_time) const;
+
  private:
   KeyframeModel(std::unique_ptr<AnimationCurve> curve,
                 int keyframe_model_id,
diff --git a/cc/animation/keyframe_model_unittest.cc b/cc/animation/keyframe_model_unittest.cc
index a04fe6d..5c853eb 100644
--- a/cc/animation/keyframe_model_unittest.cc
+++ b/cc/animation/keyframe_model_unittest.cc
@@ -1358,6 +1358,21 @@
   EXPECT_FALSE(keyframe_model->InEffect(TicksFromSecondsF(2.0)));
 }
 
+// CalculatePhase uses -time_offset_ which may cause integer overflow when
+// time_offset_ is set to min(). This test makes sure that the code handles it
+// correctly. See https://crbug.com/921454.
+TEST(KeyframeModelTest, CalculatePhaseWithMinTimeOffset) {
+  std::unique_ptr<KeyframeModel> keyframe_model(CreateKeyframeModel(1));
+  keyframe_model->set_time_offset(
+      TimeDelta::FromMilliseconds(std::numeric_limits<int64_t>::min()));
+
+  // Setting the time_offset_ to min implies that the effect has a max start
+  // delay and any local time will fall into the BEFORE phase.
+  EXPECT_EQ(
+      keyframe_model->CalculatePhaseForTesting(TimeDelta::FromSecondsD(1.0)),
+      KeyframeModel::Phase::BEFORE);
+}
+
 TEST(KeyframeModelTest, ToString) {
   std::unique_ptr<KeyframeModel> keyframe_model =
       KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(15), 42,
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index b83b72c7..6365a91 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -123,7 +123,7 @@
 }
 
 TEST_F(WorkletAnimationTest,
-       DISABLED_CurrentTimeFromDocumentTimelineIsOffsetByStartTime) {
+       CurrentTimeFromRegularTimelineIsOffsetByStartTime) {
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
       worklet_animation_id_, "test_name", nullptr, nullptr);
 
diff --git a/cc/benchmarks/rasterize_and_record_benchmark.cc b/cc/benchmarks/rasterize_and_record_benchmark.cc
index 5d1afd1..a8c606a 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark.cc
@@ -131,8 +131,8 @@
     scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) {
   return base::WrapUnique(new RasterizeAndRecordBenchmarkImpl(
       origin_task_runner, settings_.get(),
-      base::Bind(&RasterizeAndRecordBenchmark::RecordRasterResults,
-                 weak_ptr_factory_.GetWeakPtr())));
+      base::BindOnce(&RasterizeAndRecordBenchmark::RecordRasterResults,
+                     weak_ptr_factory_.GetWeakPtr())));
 }
 
 void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) {
diff --git a/cc/benchmarks/unittest_only_benchmark.cc b/cc/benchmarks/unittest_only_benchmark.cc
index 10724d4..4e32942 100644
--- a/cc/benchmarks/unittest_only_benchmark.cc
+++ b/cc/benchmarks/unittest_only_benchmark.cc
@@ -61,8 +61,8 @@
 
   return base::WrapUnique(new UnittestOnlyBenchmarkImpl(
       origin_task_runner, nullptr,
-      base::Bind(&UnittestOnlyBenchmark::RecordImplResults,
-                 weak_ptr_factory_.GetWeakPtr())));
+      base::BindOnce(&UnittestOnlyBenchmark::RecordImplResults,
+                     weak_ptr_factory_.GetWeakPtr())));
 }
 
 }  // namespace cc
diff --git a/cc/input/layer_selection_bound.cc b/cc/input/layer_selection_bound.cc
index 521cd653..55f2e3a 100644
--- a/cc/input/layer_selection_bound.cc
+++ b/cc/input/layer_selection_bound.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "cc/input/layer_selection_bound.h"
 
 namespace cc {
@@ -22,4 +23,10 @@
   return !(*this == other);
 }
 
+std::string LayerSelectionBound::ToString() const {
+  return base::StringPrintf("LayerSelectionBound(%s, %s, %d)",
+                            edge_top.ToString().c_str(),
+                            edge_bottom.ToString().c_str(), hidden);
+}
+
 }  // namespace cc
diff --git a/cc/input/layer_selection_bound.h b/cc/input/layer_selection_bound.h
index 5174341..a2a2583 100644
--- a/cc/input/layer_selection_bound.h
+++ b/cc/input/layer_selection_bound.h
@@ -26,6 +26,8 @@
   // content of the layer (as opposed to being outside of the layer's bounds).
   bool hidden;
 
+  std::string ToString() const;
+
   bool operator==(const LayerSelectionBound& other) const;
   bool operator!=(const LayerSelectionBound& other) const;
 };
diff --git a/cc/input/scrollbar_animation_controller.cc b/cc/input/scrollbar_animation_controller.cc
index 55f97f09..0e57be6 100644
--- a/cc/input/scrollbar_animation_controller.cc
+++ b/cc/input/scrollbar_animation_controller.cc
@@ -124,8 +124,8 @@
   animation_change_ = animation_change;
   delayed_scrollbar_animation_.Cancel();
   delayed_scrollbar_animation_.Reset(
-      base::Bind(&ScrollbarAnimationController::StartAnimation,
-                 weak_factory_.GetWeakPtr()));
+      base::BindOnce(&ScrollbarAnimationController::StartAnimation,
+                     weak_factory_.GetWeakPtr()));
   client_->PostDelayedScrollbarAnimationTask(
       delayed_scrollbar_animation_.callback(), fade_delay_);
 }
diff --git a/cc/input/scrollbar_animation_controller.h b/cc/input/scrollbar_animation_controller.h
index 9473b41..f7a26c5 100644
--- a/cc/input/scrollbar_animation_controller.h
+++ b/cc/input/scrollbar_animation_controller.h
@@ -18,7 +18,7 @@
 
 class CC_EXPORT ScrollbarAnimationControllerClient {
  public:
-  virtual void PostDelayedScrollbarAnimationTask(const base::Closure& task,
+  virtual void PostDelayedScrollbarAnimationTask(base::OnceClosure task,
                                                  base::TimeDelta delay) = 0;
   virtual void SetNeedsRedrawForScrollbarAnimation() = 0;
   virtual void SetNeedsAnimateForScrollbarAnimation() = 0;
@@ -153,7 +153,7 @@
   bool currently_scrolling_;
   bool show_in_fast_scroll_;
 
-  base::CancelableClosure delayed_scrollbar_animation_;
+  base::CancelableOnceClosure delayed_scrollbar_animation_;
 
   float opacity_;
 
diff --git a/cc/input/scrollbar_animation_controller_unittest.cc b/cc/input/scrollbar_animation_controller_unittest.cc
index 8a521e62..0836aeb 100644
--- a/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/cc/input/scrollbar_animation_controller_unittest.cc
@@ -37,9 +37,9 @@
       : host_impl_(host_impl) {}
   ~MockScrollbarAnimationControllerClient() override = default;
 
-  void PostDelayedScrollbarAnimationTask(const base::Closure& start_fade,
+  void PostDelayedScrollbarAnimationTask(base::OnceClosure start_fade,
                                          base::TimeDelta delay) override {
-    start_fade_ = start_fade;
+    start_fade_ = std::move(start_fade);
     delay_ = delay;
   }
   void SetNeedsRedrawForScrollbarAnimation() override {}
@@ -49,11 +49,11 @@
   }
   MOCK_METHOD0(DidChangeScrollbarVisibility, void());
 
-  base::Closure& start_fade() { return start_fade_; }
+  base::OnceClosure& start_fade() { return start_fade_; }
   base::TimeDelta& delay() { return delay_; }
 
  private:
-  base::Closure start_fade_;
+  base::OnceClosure start_fade_;
   base::TimeDelta delay_;
   LayerTreeHostImpl* host_impl_;
 };
@@ -261,7 +261,7 @@
   // An fade out animation should have been enqueued.
   EXPECT_EQ(kFadeDelay, client_.delay());
   EXPECT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
 
   // Scrollbar should fade out over kFadeDuration.
   scrollbar_controller_->Animate(time);
@@ -290,7 +290,7 @@
   // An fade out animation should have been enqueued.
   EXPECT_EQ(kFadeDelay, client_.delay());
   EXPECT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
 
   // Scrollbar should fade out over kFadeDuration.
   scrollbar_controller_->Animate(time);
@@ -651,7 +651,7 @@
   // A fade out animation should have been enqueued. Start it.
   EXPECT_EQ(kFadeDelay, client_.delay());
   EXPECT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
 
   scrollbar_controller_->Animate(time);
   ExpectScrollbarsOpacity(1);
@@ -691,7 +691,7 @@
   EXPECT_EQ(kFadeDelay, client_.delay());
   EXPECT_FALSE(client_.start_fade().is_null());
   EXPECT_FALSE(client_.start_fade().IsCancelled());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
   scrollbar_controller_->Animate(time);
   ExpectScrollbarsOpacity(1);
 
@@ -736,8 +736,7 @@
   EXPECT_EQ(kFadeDelay, client_.delay());
 
   // Play the delay animation.
-  client_.start_fade().Run();
-  EXPECT_TRUE(client_.start_fade().IsCancelled());
+  std::move(client_.start_fade()).Run();
 
   scrollbar_controller_->Animate(time);
   time += kFadeDuration;
@@ -831,7 +830,7 @@
   // An fade out animation should have been enqueued.
   EXPECT_EQ(kFadeDelay, client_.delay());
   EXPECT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
 
   // Test that at half the fade duration time, the opacity is at half.
   scrollbar_controller_->Animate(time);
@@ -865,7 +864,7 @@
   // to) notify during the animation that the scrollbars are still visible.
   EXPECT_CALL(client_, DidChangeScrollbarVisibility()).Times(0);
   ASSERT_FALSE(client_.start_fade().is_null());
-  client_.start_fade().Run();
+  std::move(client_.start_fade()).Run();
   scrollbar_controller_->Animate(time);
   time += kFadeDuration / 4;
   EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
@@ -1123,8 +1122,7 @@
   EXPECT_EQ(kFadeDelay, client_.delay());
 
   // Play the delay animation.
-  client_.start_fade().Run();
-  EXPECT_TRUE(client_.start_fade().IsCancelled());
+  std::move(client_.start_fade()).Run();
 
   scrollbar_controller_->Animate(time);
   time += kFadeDuration / 2;
@@ -1156,13 +1154,13 @@
   EXPECT_FALSE(client_.start_fade().IsCancelled());
   EXPECT_EQ(kFadeDelay, client_.delay());
 
-  base::Closure& fade = client_.start_fade();
+  client_.start_fade().Reset();
   // Move mouse still hover the fade in region of scrollbar should not
   // post a new fade in.
   scrollbar_controller_->DidMouseMove(
       NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 2, 0));
 
-  EXPECT_TRUE(fade.Equals(client_.start_fade()));
+  EXPECT_TRUE(client_.start_fade().is_null());
 }
 
 // Scrollbars should cancel delay fade in when mouse hover hidden scrollbar then
@@ -1220,8 +1218,7 @@
   EXPECT_EQ(kFadeDelay, client_.delay());
 
   // Play the delay animation.
-  client_.start_fade().Run();
-  EXPECT_TRUE(client_.start_fade().IsCancelled());
+  std::move(client_.start_fade()).Run();
 
   scrollbar_controller_->Animate(time);
   time += kFadeDuration;
@@ -1269,8 +1266,7 @@
   EXPECT_EQ(kFadeDelay, client_.delay());
 
   // Play the delay animation.
-  client_.start_fade().Run();
-  EXPECT_TRUE(client_.start_fade().IsCancelled());
+  std::move(client_.start_fade()).Run();
 
   scrollbar_controller_->Animate(time);
   time += kFadeDuration;
@@ -1375,9 +1371,9 @@
         did_request_redraw_(false),
         did_request_animate_(false) {}
 
-  void PostDelayedScrollbarAnimationTask(const base::Closure& start_fade,
+  void PostDelayedScrollbarAnimationTask(base::OnceClosure start_fade,
                                          base::TimeDelta delay) override {
-    start_fade_ = start_fade;
+    start_fade_ = std::move(start_fade);
     delay_ = delay;
   }
   void SetNeedsRedrawForScrollbarAnimation() override {
@@ -1438,7 +1434,7 @@
   std::unique_ptr<ScrollbarAnimationController> scrollbar_controller_;
   SolidColorScrollbarLayerImpl* scrollbar_layer_;
 
-  base::Closure start_fade_;
+  base::OnceClosure start_fade_;
   base::TimeDelta delay_;
   bool did_request_redraw_;
   bool did_request_animate_;
@@ -1635,7 +1631,7 @@
   EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
   scrollbar_controller_->DidScrollEnd();
   EXPECT_FALSE(did_request_animate_);
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
 
@@ -1663,7 +1659,7 @@
   scrollbar_controller_->DidScrollUpdate();
   scrollbar_controller_->DidScrollEnd();
 
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
 
@@ -1697,7 +1693,7 @@
   scrollbar_controller_->DidScrollUpdate();
   EXPECT_FALSE(did_request_animate_);
 
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
@@ -1713,7 +1709,7 @@
   scrollbar_controller_->DidScrollUpdate();
   EXPECT_FALSE(did_request_animate_);
 
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
   time += base::TimeDelta::FromSeconds(2);
@@ -1736,7 +1732,7 @@
 
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate();
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->Animate(time);
   EXPECT_TRUE(did_request_animate_);
@@ -1766,7 +1762,7 @@
   base::TimeTicks time;
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate();
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
@@ -1806,7 +1802,7 @@
   time += base::TimeDelta::FromSeconds(1);
   scrollbar_controller_->DidScrollUpdate();
   EXPECT_FALSE(did_request_animate_);
-  start_fade_.Run();
+  std::move(start_fade_).Run();
   EXPECT_TRUE(did_request_animate_);
   did_request_animate_ = false;
   scrollbar_controller_->Animate(time);
diff --git a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
index c1c0723..a08e6aa8 100644
--- a/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc
@@ -42,12 +42,10 @@
   }
 
   MOCK_METHOD2(PostDelayedScrollbarAnimationTask,
-               void(const base::Closure& start_fade, base::TimeDelta delay));
+               void(base::OnceClosure start_fade, base::TimeDelta delay));
   MOCK_METHOD0(SetNeedsRedrawForScrollbarAnimation, void());
   MOCK_METHOD0(SetNeedsAnimateForScrollbarAnimation, void());
   MOCK_METHOD0(DidChangeScrollbarVisibility, void());
-  MOCK_METHOD0(start_fade, base::Closure());
-  MOCK_METHOD0(delay, base::TimeDelta());
 
  private:
   LayerTreeHostImpl* host_impl_;
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 35ab85c..5c1e3156 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -32,7 +32,6 @@
 #include "cc/trees/layer_tree_impl.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index c60b729..18dc2f3 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -77,6 +77,7 @@
 
 Layer::Layer()
     : ignore_set_needs_commit_(false),
+      paint_count_(0),
       parent_(nullptr),
       layer_tree_host_(nullptr),
       // Layer IDs start from 1.
@@ -97,7 +98,9 @@
       needs_show_scrollbars_(false),
       has_transform_node_(false),
       subtree_has_copy_request_(false),
-      safe_opaque_background_color_(0) {}
+      safe_opaque_background_color_(0),
+      compositing_reasons_(0),
+      owner_node_id_(0) {}
 
 Layer::~Layer() {
   // Our parent should be holding a reference to us so there should be no
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 165b2919d..286a72e 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -772,6 +772,25 @@
   // in PropertyTreeManager when handling scroll offsets.
   void SetNeedsCommit();
 
+  // The following data are for profiling and debugging. They will be displayed
+  // e.g. in the Layers panel of DevTools.
+
+  // The compositing reasons of the layer. The values are defined in
+  // third_party/blink/renderer/platform/graphics/compositing_reasons.h.
+  void set_compositing_reasons(uint64_t compositing_reasons) {
+    compositing_reasons_ = compositing_reasons;
+  }
+  uint64_t compositing_reasons() const { return compositing_reasons_; }
+
+  // The id of the DOM node that owns this layer.
+  void set_owner_node_id(int node_id) { owner_node_id_ = node_id; }
+  int owner_node_id() const { return owner_node_id_; }
+
+  // How many times this layer has been repainted.
+  int paint_count() const { return paint_count_; }
+
+  // End of data for profiling and debugging.
+
  protected:
   friend class LayerImpl;
   friend class TreeSynchronizer;
@@ -814,6 +833,8 @@
   // will be handled implicitly after the update completes.
   bool ignore_set_needs_commit_;
 
+  int paint_count_;
+
  private:
   friend class base::RefCounted<Layer>;
   friend class LayerTreeHostCommon;
@@ -990,6 +1011,8 @@
   // This value is valid only when LayerTreeHost::has_copy_request() is true
   bool subtree_has_copy_request_ : 1;
   SkColor safe_opaque_background_color_;
+  uint64_t compositing_reasons_;
+  int owner_node_id_;
 
   std::unique_ptr<std::set<Layer*>> clip_children_;
 
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 50a0b87..fa1d6ae 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -133,6 +133,7 @@
         layer_tree_host()->recording_scale_factor());
 
     SetNeedsPushProperties();
+    paint_count_++;
   } else {
     // If this invalidation did not affect the recording source, then it can be
     // cleared as an optimization.
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index 0b2784b..e97162e7 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -339,8 +339,8 @@
   DCHECK_GT(internal_references_, 0);
   InternalAddRef();
   return viz::SingleReleaseCallback::Create(
-      base::Bind(&TransferableResourceHolder::ReturnAndReleaseOnImplThread,
-                 this, std::move(main_thread_task_runner)));
+      base::BindOnce(&TransferableResourceHolder::ReturnAndReleaseOnImplThread,
+                     this, std::move(main_thread_task_runner)));
 }
 
 void TextureLayer::TransferableResourceHolder::InternalAddRef() {
@@ -375,7 +375,7 @@
 #endif
   main_thread_task_runner->PostTask(
       FROM_HERE,
-      base::Bind(&TransferableResourceHolder::InternalRelease, this));
+      base::BindOnce(&TransferableResourceHolder::InternalRelease, this));
 }
 
 }  // namespace cc
diff --git a/cc/layers/texture_layer_impl_unittest.cc b/cc/layers/texture_layer_impl_unittest.cc
index 7022e57..93a6189 100644
--- a/cc/layers/texture_layer_impl_unittest.cc
+++ b/cc/layers/texture_layer_impl_unittest.cc
@@ -63,7 +63,7 @@
   texture_layer_impl->SetDrawsContent(true);
   texture_layer_impl->SetTransferableResource(
       resource,
-      viz::SingleReleaseCallback::Create(base::Bind(&IgnoreCallback)));
+      viz::SingleReleaseCallback::Create(base::BindOnce(&IgnoreCallback)));
 
   impl.CalcDrawProps(viewport_size);
 
@@ -122,7 +122,7 @@
   texture_layer_impl->SetBounds(layer_size);
   texture_layer_impl->SetDrawsContent(true);
   texture_layer_impl->SetTransferableResource(
-      resource, viz::SingleReleaseCallback::Create(base::Bind(
+      resource, viz::SingleReleaseCallback::Create(base::BindOnce(
                     [](bool* released, const gpu::SyncToken& sync_token,
                        bool lost) { *released = true; },
                     base::Unretained(&released))));
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index de62de0..c85b89e9 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -123,11 +123,11 @@
                      gpu::CommandBufferId::FromUnsafeValue(0x234),
                      2) {
     release_callback1_ =
-        base::Bind(&MockReleaseCallback::Release,
-                   base::Unretained(&mock_callback_), mailbox_name1_);
+        base::BindRepeating(&MockReleaseCallback::Release,
+                            base::Unretained(&mock_callback_), mailbox_name1_);
     release_callback2_ =
-        base::Bind(&MockReleaseCallback::Release,
-                   base::Unretained(&mock_callback_), mailbox_name2_);
+        base::BindRepeating(&MockReleaseCallback::Release,
+                            base::Unretained(&mock_callback_), mailbox_name2_);
     const uint32_t arbitrary_target1 = GL_TEXTURE_2D;
     const uint32_t arbitrary_target2 = GL_TEXTURE_EXTERNAL_OES;
     resource1_ = viz::TransferableResource::MakeGL(
@@ -136,9 +136,9 @@
         mailbox_name2_, GL_LINEAR, arbitrary_target2, sync_token2_);
     gfx::Size size(128, 128);
     shared_bitmap_id_ = viz::SharedBitmap::GenerateId();
-    sw_release_callback_ =
-        base::Bind(&MockReleaseCallback::Release2,
-                   base::Unretained(&mock_callback_), shared_bitmap_id_);
+    sw_release_callback_ = base::BindRepeating(
+        &MockReleaseCallback::Release2, base::Unretained(&mock_callback_),
+        shared_bitmap_id_);
     sw_resource_ = viz::TransferableResource::MakeSoftware(
         shared_bitmap_id_, size, viz::RGBA_8888);
   }
@@ -683,7 +683,7 @@
   void SetMailbox(char mailbox_char) {
     EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     std::unique_ptr<viz::SingleReleaseCallback> callback =
-        viz::SingleReleaseCallback::Create(base::Bind(
+        viz::SingleReleaseCallback::Create(base::BindOnce(
             &TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback,
             base::Unretained(this), mailbox_char));
 
@@ -761,7 +761,7 @@
     const gpu::SyncToken sync_token =
         SyncTokenFromUInt(static_cast<uint32_t>(mailbox_char));
     std::unique_ptr<viz::SingleReleaseCallback> callback =
-        viz::SingleReleaseCallback::Create(base::Bind(
+        viz::SingleReleaseCallback::Create(base::BindOnce(
             &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback,
             base::Unretained(this), sync_token));
     auto resource = viz::TransferableResource::MakeGL(
@@ -1027,9 +1027,9 @@
     *resource = viz::TransferableResource::MakeGL(MailboxFromChar('1'),
                                                   GL_LINEAR, GL_TEXTURE_2D,
                                                   SyncTokenFromUInt(0x123));
-    *release_callback = viz::SingleReleaseCallback::Create(
-        base::Bind(&TextureLayerNoExtraCommitForMailboxTest::ResourceReleased,
-                   base::Unretained(this)));
+    *release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
+        &TextureLayerNoExtraCommitForMailboxTest::ResourceReleased,
+        base::Unretained(this)));
     return true;
   }
 
@@ -1101,9 +1101,9 @@
     if (!resource_changed_)
       return false;
     *resource = resource_;
-    *release_callback = viz::SingleReleaseCallback::Create(
-        base::Bind(&TextureLayerChangeInvisibleMailboxTest::ResourceReleased,
-                   base::Unretained(this)));
+    *release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
+        &TextureLayerChangeInvisibleMailboxTest::ResourceReleased,
+        base::Unretained(this)));
     return true;
   }
 
@@ -1220,8 +1220,8 @@
     *resource = viz::TransferableResource::MakeGL(
         MailboxFromChar('1'), GL_LINEAR, GL_TEXTURE_2D, SyncTokenFromUInt(1));
     *release_callback = viz::SingleReleaseCallback::Create(
-        base::Bind(&TextureLayerReleaseResourcesBase::ResourceReleased,
-                   base::Unretained(this)));
+        base::BindOnce(&TextureLayerReleaseResourcesBase::ResourceReleased,
+                       base::Unretained(this)));
     return true;
   }
 
@@ -1291,7 +1291,7 @@
   void SetMailbox(char mailbox_char) {
     EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     std::unique_ptr<viz::SingleReleaseCallback> callback =
-        viz::SingleReleaseCallback::Create(base::Bind(
+        viz::SingleReleaseCallback::Create(base::BindOnce(
             &TextureLayerWithResourceMainThreadDeleted::ReleaseCallback,
             base::Unretained(this)));
     auto resource = viz::TransferableResource::MakeGL(
@@ -1361,7 +1361,7 @@
   void SetMailbox(char mailbox_char) {
     EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     std::unique_ptr<viz::SingleReleaseCallback> callback =
-        viz::SingleReleaseCallback::Create(base::Bind(
+        viz::SingleReleaseCallback::Create(base::BindOnce(
             &TextureLayerWithResourceImplThreadDeleted::ReleaseCallback,
             base::Unretained(this)));
     auto resource = viz::TransferableResource::MakeGL(
diff --git a/cc/layers/video_layer_impl_unittest.cc b/cc/layers/video_layer_impl_unittest.cc
index f70e8e22..f7b2f20 100644
--- a/cc/layers/video_layer_impl_unittest.cc
+++ b/cc/layers/video_layer_impl_unittest.cc
@@ -299,8 +299,6 @@
   EXPECT_EQ(gfx::Point3F(0, 0, 0), p2);
 }
 
-void EmptyCallback(const gpu::SyncToken& sync_token) {}
-
 TEST(VideoLayerImplTest, SoftwareVideoFrameGeneratesYUVQuad) {
   gfx::Size layer_size(1000, 1000);
 
@@ -388,7 +386,7 @@
 
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::WrapNativeTextures(
-          media::PIXEL_FORMAT_I420, mailbox_holders, base::Bind(EmptyCallback),
+          media::PIXEL_FORMAT_I420, mailbox_holders, base::DoNothing(),
           gfx::Size(10, 10), gfx::Rect(10, 10), gfx::Size(10, 10),
           base::TimeDelta());
   ASSERT_TRUE(video_frame);
@@ -432,7 +430,7 @@
   gfx::Size resource_size = gfx::Size(10, 10);
   scoped_refptr<media::VideoFrame> video_frame =
       media::VideoFrame::WrapNativeTextures(
-          media::PIXEL_FORMAT_ARGB, mailbox_holders, base::Bind(EmptyCallback),
+          media::PIXEL_FORMAT_ARGB, mailbox_holders, base::DoNothing(),
           resource_size, gfx::Rect(10, 10), resource_size, base::TimeDelta());
   ASSERT_TRUE(video_frame);
   video_frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY,
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc
index 51bbd660..dcdb3c3 100644
--- a/cc/paint/paint_flags.cc
+++ b/cc/paint/paint_flags.cc
@@ -136,7 +136,8 @@
   paint.setStrokeWidth(width_);
   paint.setStrokeMiter(miter_limit_);
   paint.setBlendMode(getBlendMode());
-  paint.setFlags(bitfields_.flags_);
+  paint.setAntiAlias(bitfields_.antialias_);
+  paint.setDither(bitfields_.dither_);
   paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap()));
   paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin()));
   paint.setStyle(static_cast<SkPaint::Style>(getStyle()));
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index 1ae12d9..64acb78 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -53,18 +53,10 @@
   ALWAYS_INLINE SkBlendMode getBlendMode() const {
     return static_cast<SkBlendMode>(blend_mode_);
   }
-  ALWAYS_INLINE bool isAntiAlias() const {
-    return !!(bitfields_.flags_ & SkPaint::kAntiAlias_Flag);
-  }
-  ALWAYS_INLINE void setAntiAlias(bool aa) {
-    SetInternalFlag(aa, SkPaint::kAntiAlias_Flag);
-  }
-  ALWAYS_INLINE bool isDither() const {
-    return !!(bitfields_.flags_ & SkPaint::kDither_Flag);
-  }
-  ALWAYS_INLINE void setDither(bool dither) {
-    SetInternalFlag(dither, SkPaint::kDither_Flag);
-  }
+  ALWAYS_INLINE bool isAntiAlias() const { return bitfields_.antialias_; }
+  ALWAYS_INLINE void setAntiAlias(bool aa) { bitfields_.antialias_ = aa; }
+  ALWAYS_INLINE bool isDither() const { return bitfields_.dither_; }
+  ALWAYS_INLINE void setDither(bool dither) { bitfields_.dither_ = dither; }
   ALWAYS_INLINE void setFilterQuality(SkFilterQuality quality) {
     bitfields_.filter_quality_ = quality;
   }
@@ -168,13 +160,6 @@
   friend class PaintOpReader;
   friend class PaintOpWriter;
 
-  ALWAYS_INLINE void SetInternalFlag(bool value, uint32_t mask) {
-    if (value)
-      bitfields_.flags_ |= mask;
-    else
-      bitfields_.flags_ &= ~mask;
-  }
-
   sk_sp<SkPathEffect> path_effect_;
   sk_sp<PaintShader> shader_;
   sk_sp<SkMaskFilter> mask_filter_;
@@ -190,7 +175,8 @@
   uint32_t blend_mode_ = static_cast<uint32_t>(SkBlendMode::kSrcOver);
 
   struct PaintFlagsBitfields {
-    uint32_t flags_ : 16;  // TODO(fmalita): only 3 bits used - squeeze?
+    uint32_t antialias_ : 1;
+    uint32_t dither_ : 1;
     uint32_t cap_type_ : 2;
     uint32_t join_type_ : 2;
     uint32_t style_ : 2;
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index fb36429a..2053c56d 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -24,7 +24,6 @@
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
diff --git a/cc/raster/paint_worklet_image_provider.cc b/cc/raster/paint_worklet_image_provider.cc
new file mode 100644
index 0000000..38feceb
--- /dev/null
+++ b/cc/raster/paint_worklet_image_provider.cc
@@ -0,0 +1,25 @@
+// 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 "cc/raster/paint_worklet_image_provider.h"
+
+#include "cc/tiles/paint_worklet_image_cache.h"
+
+namespace cc {
+
+PaintWorkletImageProvider::PaintWorkletImageProvider(
+    PaintWorkletImageCache* cache)
+    : cache_(cache) {
+  DCHECK(cache_);
+}
+
+PaintWorkletImageProvider::~PaintWorkletImageProvider() = default;
+
+PaintWorkletImageProvider::PaintWorkletImageProvider(
+    PaintWorkletImageProvider&& other) = default;
+
+PaintWorkletImageProvider& PaintWorkletImageProvider::operator=(
+    PaintWorkletImageProvider&& other) = default;
+
+}  // namespace cc
diff --git a/cc/raster/paint_worklet_image_provider.h b/cc/raster/paint_worklet_image_provider.h
new file mode 100644
index 0000000..0712b12b
--- /dev/null
+++ b/cc/raster/paint_worklet_image_provider.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CC_RASTER_PAINT_WORKLET_IMAGE_PROVIDER_H_
+#define CC_RASTER_PAINT_WORKLET_IMAGE_PROVIDER_H_
+
+#include "cc/cc_export.h"
+#include "cc/paint/image_provider.h"
+
+namespace cc {
+class PaintWorkletImageCache;
+
+// PaintWorkletImageProvider is a bridge between PaintWorkletImageCache and its
+// rasterization.
+class CC_EXPORT PaintWorkletImageProvider {
+ public:
+  explicit PaintWorkletImageProvider(PaintWorkletImageCache* cache);
+  ~PaintWorkletImageProvider();
+
+  PaintWorkletImageProvider(PaintWorkletImageProvider&& other);
+  PaintWorkletImageProvider& operator=(PaintWorkletImageProvider&& other);
+
+ private:
+  PaintWorkletImageCache* cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaintWorkletImageProvider);
+};
+
+}  // namespace cc
+
+#endif  // CC_RASTER_PAINT_WORKLET_IMAGE_PROVIDER_H_
diff --git a/cc/raster/raster_buffer_provider_unittest.cc b/cc/raster/raster_buffer_provider_unittest.cc
index e290484..3f62b72 100644
--- a/cc/raster/raster_buffer_provider_unittest.cc
+++ b/cc/raster/raster_buffer_provider_unittest.cc
@@ -322,7 +322,6 @@
   std::unique_ptr<RasterBufferProvider> raster_buffer_provider_;
   viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
   SynchronousTaskGraphRunner task_graph_runner_;
-  base::CancelableClosure timeout_;
   UniqueNotifier all_tile_tasks_finished_;
   int timeout_seconds_;
   bool timed_out_;
diff --git a/cc/raster/raster_source.h b/cc/raster/raster_source.h
index 0fa0f864..c86dee2 100644
--- a/cc/raster/raster_source.h
+++ b/cc/raster/raster_source.h
@@ -27,6 +27,7 @@
 class DisplayItemList;
 class DrawImage;
 class ImageProvider;
+class PaintWorkletImageProvider;
 
 class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
  public:
@@ -42,6 +43,10 @@
     // The ImageProvider used to replace images during playback.
     ImageProvider* image_provider = nullptr;
 
+    // The PaintWorkletImageProvider is a bridge connecting the playback and the
+    // paint worklet image cache.
+    PaintWorkletImageProvider* paint_worklet_image_provider = nullptr;
+
     RasterColorSpace raster_color_space;
   };
 
diff --git a/cc/raster/staging_buffer_pool.cc b/cc/raster/staging_buffer_pool.cc
index cf3740d4..40ea936 100644
--- a/cc/raster/staging_buffer_pool.cc
+++ b/cc/raster/staging_buffer_pool.cc
@@ -137,7 +137,7 @@
       base::BindRepeating(&StagingBufferPool::OnMemoryPressure,
                           weak_ptr_factory_.GetWeakPtr())));
 
-  reduce_memory_usage_callback_ = base::Bind(
+  reduce_memory_usage_callback_ = base::BindRepeating(
       &StagingBufferPool::ReduceMemoryUsage, weak_ptr_factory_.GetWeakPtr());
 }
 
diff --git a/cc/raster/staging_buffer_pool.h b/cc/raster/staging_buffer_pool.h
index cfd0f3a..d3331323 100644
--- a/cc/raster/staging_buffer_pool.h
+++ b/cc/raster/staging_buffer_pool.h
@@ -136,7 +136,7 @@
   int free_staging_buffer_usage_in_bytes_;
   const base::TimeDelta staging_buffer_expiration_delay_;
   bool reduce_memory_usage_pending_;
-  base::Closure reduce_memory_usage_callback_;
+  base::RepeatingClosure reduce_memory_usage_callback_;
 
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
 
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 2aeb815..76ea211 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -856,7 +856,8 @@
   }
 
   if (timeout_seconds_) {
-    timeout_.Reset(base::Bind(&LayerTreeTest::Timeout, base::Unretained(this)));
+    timeout_.Reset(
+        base::BindOnce(&LayerTreeTest::Timeout, base::Unretained(this)));
     main_task_runner_->PostDelayedTask(
         FROM_HERE, timeout_.callback(),
         base::TimeDelta::FromSeconds(timeout_seconds_));
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 4bb4314..a7251166 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -235,7 +235,7 @@
   std::unique_ptr<base::Thread> image_worker_;
   std::unique_ptr<viz::TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
   std::unique_ptr<TestTaskGraphRunner> task_graph_runner_;
-  base::CancelableClosure timeout_;
+  base::CancelableOnceClosure timeout_;
   scoped_refptr<viz::TestContextProvider> compositor_contexts_;
   base::WeakPtr<LayerTreeTest> main_thread_weak_ptr_;
   base::WeakPtrFactory<LayerTreeTest> weak_factory_;
diff --git a/cc/tiles/decoded_image_tracker.cc b/cc/tiles/decoded_image_tracker.cc
index 0af369ff..197b749 100644
--- a/cc/tiles/decoded_image_tracker.cc
+++ b/cc/tiles/decoded_image_tracker.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "cc/tiles/decoded_image_tracker.h"
+#include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
 
 namespace cc {
@@ -27,7 +28,7 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : image_controller_(controller),
       task_runner_(std::move(task_runner)),
-      now_fn_(base::Bind(&base::TimeTicks::Now)),
+      tick_clock_(base::DefaultTickClock::GetInstance()),
       weak_ptr_factory_(this) {
   DCHECK(image_controller_);
 }
@@ -78,7 +79,8 @@
     // decode.
     locked_images_.erase(image_id);
     locked_images_.emplace(
-        image_id, std::make_unique<ImageLock>(this, request_id, now_fn_.Run()));
+        image_id,
+        std::make_unique<ImageLock>(this, request_id, tick_clock_->NowTicks()));
     EnqueueTimeout();
   }
   bool decode_succeeded =
@@ -92,7 +94,7 @@
   if (locked_images_.size() == 0)
     return;
 
-  auto now = now_fn_.Run();
+  auto now = tick_clock_->NowTicks();
   auto timeout = base::TimeDelta::FromMilliseconds(kTimeoutDurationMs);
   for (auto it = locked_images_.begin(); it != locked_images_.end();) {
     auto& image = it->second;
diff --git a/cc/tiles/decoded_image_tracker.h b/cc/tiles/decoded_image_tracker.h
index 45312af..c88bd2b 100644
--- a/cc/tiles/decoded_image_tracker.h
+++ b/cc/tiles/decoded_image_tracker.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "cc/cc_export.h"
 #include "cc/tiles/image_controller.h"
@@ -44,8 +45,9 @@
   // unlock them.
   void OnImagesUsedInDraw(const std::vector<DrawImage>& draw_images);
 
-  using NowFn = base::Callback<base::TimeTicks()>;
-  void SetNowFunctionForTesting(NowFn now_fn) { now_fn_ = now_fn; }
+  void SetTickClockForTesting(const base::TickClock* tick_clock) {
+    tick_clock_ = tick_clock;
+  }
 
   // Test only functions:
   size_t NumLockedImagesForTesting() const { return locked_images_.size(); }
@@ -83,7 +85,7 @@
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   // Defaults to base::TimeTicks::Now(), but overrideable for testing.
-  NowFn now_fn_;
+  const base::TickClock* tick_clock_;
 
   base::WeakPtrFactory<DecodedImageTracker> weak_ptr_factory_;
 
diff --git a/cc/tiles/decoded_image_tracker_unittest.cc b/cc/tiles/decoded_image_tracker_unittest.cc
index d2f489b..e481894 100644
--- a/cc/tiles/decoded_image_tracker_unittest.cc
+++ b/cc/tiles/decoded_image_tracker_unittest.cc
@@ -67,8 +67,8 @@
   DecodedImageTrackerTest()
       : task_runner_(new base::TestMockTimeTaskRunner()),
         decoded_image_tracker_(&image_controller_, task_runner_) {
-    decoded_image_tracker_.SetNowFunctionForTesting(
-        base::Bind(&base::TestMockTimeTaskRunner::NowTicks, task_runner_));
+    decoded_image_tracker_.SetTickClockForTesting(
+        task_runner_->GetMockTickClock());
   }
 
   TestImageController* image_controller() { return &image_controller_; }
diff --git a/cc/tiles/image_controller.h b/cc/tiles/image_controller.h
index 5a47c98d..d554010 100644
--- a/cc/tiles/image_controller.h
+++ b/cc/tiles/image_controller.h
@@ -88,6 +88,9 @@
   }
 
   ImageDecodeCache* cache() const { return cache_; }
+  PaintWorkletImageCache* paint_worklet_image_cache() {
+    return &paint_worklet_image_cache_;
+  }
 
  protected:
   scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index e45088dc..6970e507 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -24,6 +24,7 @@
 #include "cc/base/devtools_instrumentation.h"
 #include "cc/base/histograms.h"
 #include "cc/layers/picture_layer_impl.h"
+#include "cc/raster/paint_worklet_image_provider.h"
 #include "cc/raster/playback_image_provider.h"
 #include "cc/raster/raster_buffer.h"
 #include "cc/raster/task_category.h"
@@ -85,6 +86,7 @@
                  TileTask::Vector* dependencies,
                  bool is_gpu_rasterization,
                  PlaybackImageProvider image_provider,
+                 PaintWorkletImageProvider paint_worklet_image_provider,
                  GURL url)
       : TileTask(!is_gpu_rasterization, dependencies),
         tile_manager_(tile_manager),
@@ -104,9 +106,12 @@
         is_gpu_rasterization_(is_gpu_rasterization),
         raster_buffer_(std::move(raster_buffer)),
         image_provider_(std::move(image_provider)),
+        paint_worklet_image_provider_(std::move(paint_worklet_image_provider)),
         url_(std::move(url)) {
     DCHECK(origin_thread_checker_.CalledOnValidThread());
     playback_settings_.image_provider = &image_provider_;
+    playback_settings_.paint_worklet_image_provider =
+        &paint_worklet_image_provider_;
   }
 
   // Overridden from Task:
@@ -173,6 +178,7 @@
   bool is_gpu_rasterization_;
   std::unique_ptr<RasterBuffer> raster_buffer_;
   PlaybackImageProvider image_provider_;
+  PaintWorkletImageProvider paint_worklet_image_provider_;
   GURL url_;
 
   DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl);
@@ -298,10 +304,11 @@
  public:
   explicit TaskSetFinishedTaskImpl(
       base::SequencedTaskRunner* task_runner,
-      const base::Closure& on_task_set_finished_callback)
+      base::RepeatingClosure on_task_set_finished_callback)
       : TileTask(true),
         task_runner_(task_runner),
-        on_task_set_finished_callback_(on_task_set_finished_callback) {}
+        on_task_set_finished_callback_(
+            std::move(on_task_set_finished_callback)) {}
 
   // Overridden from Task:
   void RunOnWorkerThread() override {
@@ -321,7 +328,7 @@
 
  private:
   base::SequencedTaskRunner* task_runner_;
-  const base::Closure on_task_set_finished_callback_;
+  const base::RepeatingClosure on_task_set_finished_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(TaskSetFinishedTaskImpl);
 };
@@ -1237,12 +1244,16 @@
                                        std::move(settings));
 
   playback_settings.raster_color_space = raster_color_space;
+
+  PaintWorkletImageProvider paint_worklet_image_provider(
+      image_controller_.paint_worklet_image_cache());
+
   return base::MakeRefCounted<RasterTaskImpl>(
       this, tile, std::move(resource), prioritized_tile.raster_source(),
       playback_settings, prioritized_tile.priority().resolution,
       invalidated_rect, prepare_tiles_count_, std::move(raster_buffer),
       &decode_tasks, use_gpu_rasterization_, std::move(image_provider),
-      active_url_);
+      std::move(paint_worklet_image_provider), active_url_);
 }
 
 void TileManager::ResetSignalsForTesting() {
@@ -1388,7 +1399,7 @@
   if (!check_pending_tile_queries_callback_.IsCancelled())
     return;
 
-  check_pending_tile_queries_callback_.Reset(base::Bind(
+  check_pending_tile_queries_callback_.Reset(base::BindOnce(
       &TileManager::CheckRasterFinishedQueries, base::Unretained(this)));
   task_runner_->PostDelayedTask(FROM_HERE,
                                 check_pending_tile_queries_callback_.callback(),
@@ -1687,7 +1698,8 @@
     void (TileManager::*callback)()) {
   return base::MakeRefCounted<TaskSetFinishedTaskImpl>(
       task_runner_,
-      base::Bind(callback, task_set_finished_weak_ptr_factory_.GetWeakPtr()));
+      base::BindRepeating(callback,
+                          task_set_finished_weak_ptr_factory_.GetWeakPtr()));
 }
 
 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index e211e64..244eea4 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -462,7 +462,7 @@
   // The callback scheduled to poll whether the GPU side work for pending tiles
   // has completed.
   bool has_pending_queries_ = false;
-  base::CancelableClosure check_pending_tile_queries_callback_;
+  base::CancelableOnceClosure check_pending_tile_queries_callback_;
 
   // We need two WeakPtrFactory objects as the invalidation pattern of each is
   // different. The |task_set_finished_weak_ptr_factory_| is invalidated any
diff --git a/cc/trees/image_animation_controller.cc b/cc/trees/image_animation_controller.cc
index a781c08f..62900fb0 100644
--- a/cc/trees/image_animation_controller.cc
+++ b/cc/trees/image_animation_controller.cc
@@ -426,7 +426,7 @@
   pending_notification_time_.emplace(notification_time);
   task_runner_->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&DelayedNotifier::Notify, weak_factory_.GetWeakPtr()),
+      base::BindOnce(&DelayedNotifier::Notify, weak_factory_.GetWeakPtr()),
       notification_time - now);
 }
 
diff --git a/cc/trees/image_animation_controller_unittest.cc b/cc/trees/image_animation_controller_unittest.cc
index ea59f849..f8abed7 100644
--- a/cc/trees/image_animation_controller_unittest.cc
+++ b/cc/trees/image_animation_controller_unittest.cc
@@ -74,11 +74,11 @@
   void SetUp() override {
     task_runner_ =
         new DelayTrackingTaskRunner(base::ThreadTaskRunnerHandle::Get().get());
-    base::Closure invalidation_callback =
-        base::Bind(&ImageAnimationControllerTest::RequestInvalidation,
-                   base::Unretained(this));
+    auto invalidation_callback =
+        base::BindRepeating(&ImageAnimationControllerTest::RequestInvalidation,
+                            base::Unretained(this));
     controller_ = std::make_unique<ImageAnimationController>(
-        task_runner_.get(), invalidation_callback,
+        task_runner_.get(), std::move(invalidation_callback),
         GetEnableImageAnimationResync());
     now_ += base::TimeDelta::FromSeconds(10);
   }
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 5413674..bc110bf 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -87,7 +87,6 @@
 #include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/compositor_frame_metadata.h"
@@ -4981,9 +4980,9 @@
 }
 
 void LayerTreeHostImpl::PostDelayedScrollbarAnimationTask(
-    const base::Closure& task,
+    base::OnceClosure task,
     base::TimeDelta delay) {
-  client_->PostDelayedAnimationTaskOnImplThread(task, delay);
+  client_->PostDelayedAnimationTaskOnImplThread(std::move(task), delay);
 }
 
 // TODO(danakj): Make this a return value from the Animate() call instead of an
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 3c5bc81..9297f60 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -418,7 +418,7 @@
                                WhichTree tree) const override;
 
   // ScrollbarAnimationControllerClient implementation.
-  void PostDelayedScrollbarAnimationTask(const base::Closure& task,
+  void PostDelayedScrollbarAnimationTask(base::OnceClosure task,
                                          base::TimeDelta delay) override;
   void SetNeedsAnimateForScrollbarAnimation() override;
   void SetNeedsRedrawForScrollbarAnimation() override;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 5ce8950..26b7843 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -9411,10 +9411,11 @@
 };
 
 TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
-  auto gl_owned = std::make_unique<viz::TestGLES2Interface>();
-  viz::TestGLES2Interface* gl = gl_owned.get();
+  scoped_refptr<viz::TestContextProvider> context_provider =
+      viz::TestContextProvider::Create();
+  viz::TestSharedImageInterface* sii = context_provider->SharedImageInterface();
   std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink(
-      FakeLayerTreeFrameSink::Create3d(std::move(gl_owned)));
+      FakeLayerTreeFrameSink::Create3d(context_provider));
   CreateHostImpl(DefaultSettings(), std::move(layer_tree_frame_sink));
 
   std::unique_ptr<LayerImpl> root_layer =
@@ -9435,19 +9436,19 @@
   host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_layer));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
-  EXPECT_EQ(0u, gl->NumTextures());
+  EXPECT_EQ(0u, sii->shared_image_count());
 
   TestFrameData frame;
   EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  EXPECT_GT(gl->NumTextures(), 0u);
+  EXPECT_GT(sii->shared_image_count(), 0u);
 
   // Kill the layer tree.
   host_impl_->active_tree()->DetachLayers();
   // There should be no textures left in use after.
-  EXPECT_EQ(0u, gl->NumTextures());
+  EXPECT_EQ(0u, sii->shared_image_count());
 }
 
 
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc
index 9c800d9..954455ffa5 100644
--- a/cc/trees/layer_tree_host_perftest.cc
+++ b/cc/trees/layer_tree_host_perftest.cc
@@ -319,7 +319,7 @@
     gpu_mailbox.SetName(
         reinterpret_cast<const int8_t*>(name_stream.str().c_str()));
     std::unique_ptr<viz::SingleReleaseCallback> callback =
-        viz::SingleReleaseCallback::Create(base::Bind(
+        viz::SingleReleaseCallback::Create(base::BindOnce(
             &BrowserCompositorInvalidateLayerTreePerfTest::ReleaseMailbox,
             base::Unretained(this)));
 
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index 5f6068be..aa7781a 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -936,9 +936,9 @@
     auto resource = viz::TransferableResource::MakeGL(
         mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token);
     texture->SetTransferableResource(
-        resource, viz::SingleReleaseCallback::Create(
-                      base::Bind(&LayerTreeHostContextTestDontUseLostResources::
-                                     EmptyReleaseCallback)));
+        resource, viz::SingleReleaseCallback::Create(base::BindOnce(
+                      &LayerTreeHostContextTestDontUseLostResources::
+                          EmptyReleaseCallback)));
     root->AddChild(texture);
 
     scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client_);
diff --git a/cc/trees/layer_tree_host_unittest_proxy.cc b/cc/trees/layer_tree_host_unittest_proxy.cc
index b918f70..9fa460e 100644
--- a/cc/trees/layer_tree_host_unittest_proxy.cc
+++ b/cc/trees/layer_tree_host_unittest_proxy.cc
@@ -255,11 +255,11 @@
     switch (impl->sync_tree()->source_frame_number()) {
       case 0: {
         // This is for case 1 in DidCommit.
-        auto unblock = base::Bind(
+        auto unblock = base::BindOnce(
             &LayerTreeHostProxyTestCommitWaitsForActivation::UnblockActivation,
             base::Unretained(this), impl);
         ImplThreadTaskRunner()->PostDelayedTask(
-            FROM_HERE, unblock,
+            FROM_HERE, std::move(unblock),
             // Use a delay to allow the main frame to start if it would. This
             // should cause failures (or flakiness) if we fail to wait for the
             // activation before starting the main frame.
@@ -348,13 +348,13 @@
         // This is the main frame with SetNextCommitWaitsForActivation().
         // Activation is currently blocked for the previous main frame (from the
         // case above). We unblock activate to allow this main frame to commit.
-        auto unblock =
-            base::Bind(&LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting,
-                       base::Unretained(impl), false);
+        auto unblock = base::BindOnce(
+            &LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting,
+            base::Unretained(impl), false);
         // Post the unblock instead of doing it immediately so that the main
         // frame is fully processed by the compositor thread, and it has a full
         // opportunity to wrongly unblock the main thread.
-        ImplThreadTaskRunner()->PostTask(FROM_HERE, unblock);
+        ImplThreadTaskRunner()->PostTask(FROM_HERE, std::move(unblock));
         // Once activation completes, we'll begin the commit for frame 1.
         break;
       }
@@ -376,11 +376,12 @@
       // failures (or flakiness) if we fail to wait for the activation before
       // starting the main frame.
       auto unblock =
-          base::Bind(&LayerTreeHostProxyTestCommitWaitsForActivationMFBA::
-                         UnblockActivation,
-                     base::Unretained(this), impl);
+          base::BindOnce(&LayerTreeHostProxyTestCommitWaitsForActivationMFBA::
+                             UnblockActivation,
+                         base::Unretained(this), impl);
       ImplThreadTaskRunner()->PostDelayedTask(
-          FROM_HERE, unblock, base::TimeDelta::FromMilliseconds(16 * 4));
+          FROM_HERE, std::move(unblock),
+          base::TimeDelta::FromMilliseconds(16 * 4));
     }
   }
 
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 5ec2bfc..f2d96d8 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -100,8 +100,9 @@
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestScrollSimple::DidScrollOuterViewport,
-                   base::Unretained(this)));
+        base::BindRepeating(
+            &LayerTreeHostScrollTestScrollSimple::DidScrollOuterViewport,
+            base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
   }
 
@@ -171,7 +172,7 @@
   void BeginTest() override {
     scroll_layer_ = layer_tree_host()->outer_viewport_scroll_layer();
     scroll_layer_->SetScrollOffset(initial_scroll_);
-    scroll_layer_->set_did_scroll_callback(base::Bind(
+    scroll_layer_->set_did_scroll_callback(base::BindRepeating(
         &LayerTreeHostScrollTestScrollMultipleRedraw::DidScrollOuterViewport,
         base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
@@ -259,7 +260,7 @@
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(
+        base::BindRepeating(
             &LayerTreeHostScrollTestScrollAbortedCommit::DidScrollOuterViewport,
             base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
@@ -579,8 +580,8 @@
 
     child_layer_ = FakePictureLayer::Create(&fake_content_layer_client_);
     child_layer_->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestCaseWithChild::DidScroll,
-                   base::Unretained(this)));
+        base::BindRepeating(&LayerTreeHostScrollTestCaseWithChild::DidScroll,
+                            base::Unretained(this)));
     child_layer_->SetElementId(
         LayerIdToElementIdForTesting(child_layer_->id()));
     child_layer_->SetBounds(gfx::Size(110, 110));
@@ -617,7 +618,7 @@
     fake_content_layer_client_.set_bounds(root_layer->bounds());
 
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(
+        base::BindRepeating(
             &LayerTreeHostScrollTestCaseWithChild::DidScrollOuterViewport,
             base::Unretained(this)));
   }
@@ -840,8 +841,9 @@
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestSimple::DidScrollOuterViewport,
-                   base::Unretained(this)));
+        base::BindRepeating(
+            &LayerTreeHostScrollTestSimple::DidScrollOuterViewport,
+            base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
   }
 
@@ -1412,7 +1414,7 @@
         LayerIdToElementIdForTesting(scroll_layer->id()));
     scroll_layer->SetBounds(gfx::Size(parent->bounds().width() + 100,
                                       parent->bounds().height() + 100));
-    scroll_layer->set_did_scroll_callback(base::Bind(
+    scroll_layer->set_did_scroll_callback(base::BindRepeating(
         &FakeLayerScrollClient::DidScroll, base::Unretained(client)));
     client->owner_ = this;
     client->layer_ = scroll_layer.get();
@@ -1471,8 +1473,9 @@
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestScrollMFBA::DidScrollOuterViewport,
-                   base::Unretained(this)));
+        base::BindRepeating(
+            &LayerTreeHostScrollTestScrollMFBA::DidScrollOuterViewport,
+            base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
   }
 
@@ -1601,9 +1604,9 @@
     layer_tree_host()->outer_viewport_scroll_layer()->SetScrollOffset(
         initial_scroll_);
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestScrollAbortedCommitMFBA::
-                       DidScrollOuterViewport,
-                   base::Unretained(this)));
+        base::BindRepeating(&LayerTreeHostScrollTestScrollAbortedCommitMFBA::
+                                DidScrollOuterViewport,
+                            base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
   }
 
@@ -2050,9 +2053,9 @@
     : public LayerTreeHostScrollTest {
   void BeginTest() override {
     layer_tree_host()->outer_viewport_scroll_layer()->set_did_scroll_callback(
-        base::Bind(&LayerTreeHostScrollTestImplSideInvalidation::
-                       DidScrollOuterViewport,
-                   base::Unretained(this)));
+        base::BindRepeating(&LayerTreeHostScrollTestImplSideInvalidation::
+                                DidScrollOuterViewport,
+                            base::Unretained(this)));
     PostSetNeedsCommitToMainThread();
   }
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index aca94b3..92e1d84 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -595,8 +595,8 @@
   if (layer_tree_frame_sink_creation_callback_.IsCancelled() &&
       !layer_tree_frame_sink_creation_requested_) {
     layer_tree_frame_sink_creation_callback_.Reset(
-        base::Bind(&SingleThreadProxy::RequestNewLayerTreeFrameSink,
-                   weak_factory_.GetWeakPtr()));
+        base::BindOnce(&SingleThreadProxy::RequestNewLayerTreeFrameSink,
+                       weak_factory_.GetWeakPtr()));
     task_runner_provider_->MainThreadTaskRunner()->PostTask(
         FROM_HERE, layer_tree_frame_sink_creation_callback_.callback());
   }
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index f741050..7247fce1 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -190,7 +190,7 @@
   bool layer_tree_frame_sink_lost_;
 
   // This is the callback for the scheduled RequestNewLayerTreeFrameSink.
-  base::CancelableClosure layer_tree_frame_sink_creation_callback_;
+  base::CancelableOnceClosure layer_tree_frame_sink_creation_callback_;
 
   base::WeakPtr<SingleThreadProxy> frame_sink_bound_weak_ptr_;
 
diff --git a/chrome/VERSION b/chrome/VERSION
index fbe3004..ff407fe 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=73
 MINOR=0
-BUILD=3673
+BUILD=3675
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 9d34703..3dfe2add 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1434,7 +1434,6 @@
                              "jni_registration_header",
                              "is_modern",
                              "module_name",
-                             "proguard_expectations_file",
                              "target_type",
                            ])
 
@@ -1486,26 +1485,12 @@
 chrome_public_apk_or_module_tmpl("chrome_public_apk") {
   target_type = "android_apk"
   apk_name = "ChromePublic"
-
-  # Having //clank present causes different flags because of how play services
-  # is wired up.
-  if (!is_java_debug && !enable_chrome_android_internal) {
-    proguard_expectations_file =
-        "//chrome/android/java/chrome_public_apk.proguard_flags.expected"
-  }
 }
 
 chrome_public_apk_or_module_tmpl("chrome_modern_public_apk") {
   target_type = "android_apk"
   apk_name = "ChromeModernPublic"
   is_modern = true
-
-  # Having //clank present causes different flags because of how play services
-  # is wired up.
-  if (!is_java_debug && !enable_chrome_android_internal) {
-    proguard_expectations_file =
-        "//chrome/android/java/chrome_modern_public_apk.proguard_flags.expected"
-  }
 }
 
 chrome_public_apk_or_module_tmpl("chrome_public_base_bundle_module") {
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 094d9ee5..26bde52b 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -176,7 +176,7 @@
              the module installation will not automatically bring in DFM's
              manifest entries. -->
         <meta-data android:name="com.google.ar.core.min_apk_version"
-            android:value="180815000"/>
+            android:value="181012000"/>
         {% endif %}
 
         <!-- Cast support -->
diff --git a/chrome/android/java/README.md b/chrome/android/java/README.md
new file mode 100644
index 0000000..4af18a2
--- /dev/null
+++ b/chrome/android/java/README.md
@@ -0,0 +1,90 @@
+## //chrome/android/java/*proguard_flags.expected files
+
+### Proguard flags
+
+[Proguard](https://www.guardsquare.com/en/products/proguard) is used in the
+build to obfuscate and minify Java code.
+
+Proguard flags (also known as configs or rules) are used to specify which parts
+of Java code should not be optimized/obfuscated/modified by Proguard.
+
+For example, the following rule specifies that all public classes with a
+`public static void main(java.lang.String[])` method should not be modifed.
+
+```
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+```
+
+### What are `*.proguard_flags.expected` files?
+
+[monochrome_public_apk.proguard_flags.expected](monochrome_public_apk.proguard_flags.expected)
+contains all proguard configs used when building MonochromePublic.apk, and is
+generated by the `proguard()` build step.
+
+### Why do we care about proguard flag discrepancies?
+
+Some configs are explicitly added ([ex](proguard.flags)) while others are pulled
+in implicitly by GN deps (ex. `aar_prebuilt()` deps). In the later case, these
+config changes aren't reviewed and can sometimes result in crashes and/or size
+regressions. Example of where this has happened before:
+
+  * Updating a prebuilt that supplies a Proguard config with a rule that is
+    too broad (i.e. a rule that matches more classes than the intended ones)
+
+Having checked-in versions of the Proguard configs used allows us to identify
+and address these issues earlier.
+
+### What is the build error telling me?
+
+The build error is indicating that your CL has caused proguard rules to be
+added/removed/changed and that the expected file needs to be updated.
+
+### Fixing build failures
+
+1. Ensure your args.gn contains these args:
+```
+enable_chrome_android_internal = false
+is_java_debug = false
+```
+
+2. Run:
+
+```
+autoninja -C $CHROMIUM_OUTPUT_DIR monochrome_public_apk
+```
+
+3. Run the command suggested in the error message to copy the contents of the
+   generated proguard config file to the expected config file.
+
+4. Add the updated `.expected` file to your CL
+
+5. Afterwards, you can revert the args.gn changes suggested above and build
+   normally
+
+### Trybot failures
+
+On the [android-binary-size](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-binary-size)
+trybot we set `check_android_configuration=true` which causes any differences
+between the expected and generated Proguard configs to fail the build.
+
+Without this argument the error message is shown but doesn't fail the build.
+
+### Troubleshooting
+
+Trybots fail but you can't reproduce locally
+
+* If a public target is failing, double check to make sure you've set
+  `enable_chrome_android_internal=false`
+
+Can't find the file suggested by the error message
+
+* Make sure `is_java_debug=false`
+
+Updating the file doesn't fix the error
+
+* Make sure you're building `monochrome_public_apk`
+
+Otherwise, please file a bug at [crbug.com/new](https://crbug.com/new) and/or
+message estevenson@.
diff --git a/chrome/android/java/chrome_modern_public_apk.proguard_flags.expected b/chrome/android/java/chrome_modern_public_apk.proguard_flags.expected
deleted file mode 100644
index d70cd99..0000000
--- a/chrome/android/java/chrome_modern_public_apk.proguard_flags.expected
+++ /dev/null
@@ -1,571 +0,0 @@
--allowaccessmodification
--assumenosideeffects class ** {
-  @org.chromium.base.annotations.RemovableInRelease <methods>;
-}
--assumenosideeffects class android.util.Log {
-  static *** d(...);
-  static *** v(...);
-  static *** isLoggable(...);
-}
--dontpreverify
--dontwarn android.app.Notification
--dontwarn android.nfc.NfcAdapter
--dontwarn android.security.NetworkSecurityPolicy
--dontwarn android.support.**
--dontwarn com.google.android.apps.common.proguard.UsedBy*
--dontwarn com.google.common.logging.nano.Vr$**
--dontwarn com.google.protobuf.nano.NanoEnumValue
--dontwarn com.google.vr.**
--dontwarn javax.annotation.**
--dontwarn libcore.io.Memory
--dontwarn org.checkerframework.**
--dontwarn org.chromium.base.library_loader.NativeLibraries
--dontwarn sun.misc.Unsafe
--keep @android.support.annotation.Keep class *
--keep @com.google.android.gms.common.util.DynamiteApi public class * {
-  public <fields>;
-  public <methods>;
-}
--keep @com.google.vr.cardboard.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByReflection class *
--keep @interface android.support.annotation.Keep
--keep @interface com.google.android.gms.common.annotation.KeepName
--keep @interface com.google.android.gms.common.util.DynamiteApi
--keep @interface org.chromium.base.annotations.AccessedByNative
--keep @interface org.chromium.base.annotations.CalledByNative
--keep @interface org.chromium.base.annotations.CalledByNativeUnchecked
--keep @interface org.chromium.base.annotations.DoNotInline
--keep @interface org.chromium.base.annotations.RemovableInRelease
--keep @interface org.chromium.base.annotations.UsedByReflection
--keep @org.chromium.base.annotations.UsedByReflection class * {}
--keep class * implements android.arch.lifecycle.GenericLifecycleObserver {
-    <init>(...);
-}
--keep class * implements android.arch.lifecycle.LifecycleObserver {
-}
--keep class * implements org.chromium.base.test.params.ParameterProvider
--keep class android.support.customtabs.PostMessageService { <init>(); }
--keep class android.support.customtabs.browseractions.BrowserActionsFallbackMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.customtabs.browseractions.BrowserServiceFileProvider { <init>(); }
--keep class android.support.design.internal.BaselineLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.SnackbarContentLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.AppBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CheckableImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CoordinatorLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.Snackbar$SnackbarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.media.** implements android.os.Parcelable {
-    public static final android.os.Parcelable$Creator *;
-}
--keep class android.support.v4.view.ViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.NestedScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.Space { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.AlertController$RecycleListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteExpandCollapseButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteVolumeSlider { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.OverlayListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ActionMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ExpandedMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ListMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarOverlayLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActivityChooserView$InnerLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AlertDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatSpinner { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ButtonBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ContentFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.DialogTitle { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.GridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.RecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SearchView {
-  public <init>(...);
-}
--keep class android.support.v7.widget.SearchView$SearchAutoComplete { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SwitchCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.Toolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ViewStubCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.widget.RadioButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider { <init>(); }
--keep class com.google.android.gms.cast.framework.ReconnectionService { <init>(); }
--keep class com.google.android.gms.cast.framework.internal.featurehighlight.HelpTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.google.android.gms.cast.framework.media.MediaIntentReceiver { <init>(); }
--keep class com.google.android.gms.cast.framework.media.MediaNotificationService { <init>(); }
--keep class com.google.android.gms.common.api.GoogleApiActivity { <init>(); }
--keep class com.google.android.gms.common.internal.ReflectedParcelable
--keep class com.google.android.gms.gcm.GcmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.external.client.contrib.AndroidListener$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.TiclService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidInstanceIDListenerService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService { <init>(); }
--keep class com.google.vr.cardboard.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByReflection
--keep class org.chromium.build.BuildHooksAndroidImpl
--keep class org.chromium.chrome.browser.BrowserRestartActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeApplication { <init>(); }
--keep class org.chromium.chrome.browser.ChromeBackgroundService { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.LauncherShortcutActivity { <init>(); }
--keep class org.chromium.chrome.browser.NoTouchActivity { <init>(); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuItemIcon { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModernView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryTabLayoutView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.TouchEventFilter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActionBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkEditActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkItemRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy { <init>(); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetService { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionActivity { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionsService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataDialogActivity { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ManageTrustedWebActivityDataActivity { <init>(); }
--keep class org.chromium.chrome.browser.compositor.CompositorViewHolder { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactsPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.TopView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextual_suggestions.ToolbarView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.coordinator.CoordinatorLayoutForPointer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService { <init>(); }
--keep class org.chromium.chrome.browser.crash.MinidumpUploadService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabsConnectionService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.PaymentHandlerActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.ChromeLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.DocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.IncognitoDocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.dom_distiller.DistilledPagePrefsView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.DownloadActivity { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastManager { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadForegroundService { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadLocationCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AsyncImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.CircularProgressView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.ForegroundRoundedCornerImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.toolbar.DownloadHomeToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.view.SelectionView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.OfflineGroupHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunChooserView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.TabbedModeFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.history.HistoryManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.incognito.IncognitoDisclosureActivity { <init>(); }
--keep class org.chromium.chrome.browser.incognito.IncognitoNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.infobar.InfoBarMessageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.TextViewEllipsizerSafe { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabContent { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService { <init>(); }
--keep class org.chromium.chrome.browser.jsdialog.JavascriptDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaCaptureNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.ExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.FullscreenMediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.modaldialog.ModalDialogView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationJobService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$TimeoutReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageViewMD { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.LogoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NativePageRootFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout$SearchBoxContainerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsExpandableListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsGroupView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.cards.ProgressIndicatorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.snippets.SectionHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$SettingsReceiver { <init>(); }
--keep class org.chromium.chrome.browser.omaha.OmahaClient { <init>(); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.UrlBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.status.StatusView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.page_info.PageInfoView$ElidedUrlTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.password_manager.PasswordGenerationDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestHeader { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestUiErrorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.DecoderService { <init>(); }
--keep class org.chromium.chrome.browser.photo_picker.PhotoPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.PickerBitmapView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.AboutChromePreferenceOSVersion { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBasePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeSwitchPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ExpandablePreferenceGroup { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.HyperlinkPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.LearnMorePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.Preferences { <init>(); }
--keep class org.chromium.chrome.browser.preferences.SeekBarLinkedCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SignInPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SpinnerPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncErrorCardPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncedAccountPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextAndButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextMessagePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextScalePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionMainMenuItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionSiteBreakdownView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionStatsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.download.DownloadLocationPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.languages.LanguageListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ClearWebsiteStorage { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ManageSpaceActivity { <init>(); }
--keep class org.chromium.chrome.browser.preferences.website.SiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.TriStateSiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.prerender.ChromePrerenderService { <init>(); }
--keep class org.chromium.chrome.browser.printing.PrintShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.provider.ChromeBrowserProvider { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivity { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivityLocationBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.searchwidget.SearchWidgetProvider { <init>(); }
--keep class org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.services.AccountsChangedReceiver { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninChooseView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninConfirmationView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.PersonalizedSigninPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.SigninScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SyncPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.BottomContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.TemplatePreservingTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.TileGridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.sync.ui.PassphraseActivity { <init>(); }
--keep class org.chromium.chrome.browser.tab.SadTabView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.HomeButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.MenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.NewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.TabSwitcherButtonView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.SearchAccelerator { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ShareButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar$InterceptTouchLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.TabSwitcherModeTTPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer$ToolbarViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.tracing.TracingNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeActivity { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeIntentService { <init>(); }
--keep class org.chromium.chrome.browser.util.ChromeFileProvider { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrCancelAnimationActivity { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.SameTaskWebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.widget.AlertDialogEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.BoundedLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.CompatibilityTextInputLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.DualControlLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.EmptyAlertEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingEdgeScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingShadowView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.ListMenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.LoadingView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.MaterialProgressBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.NumberRollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.PromoDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonWithDescription { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelWrapper { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.TouchRestrictingFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbar$FindQuery { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.incognitotoggle.IncognitoToggleButtonTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.EditorDialogToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.ExpandableGridView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.selection.SelectableListLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskGcmTaskService { <init>(); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskJobService { <init>(); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerAdvanced { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerMoreButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerSimple { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.content.app.PrivilegedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.PrivilegedProcessService0 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService1 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService2 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService3 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.SandboxedProcessService0 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService1 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService10 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService11 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService12 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService13 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService14 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService15 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService16 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService17 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService18 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService19 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService2 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService20 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService21 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService22 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService23 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService24 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService25 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService26 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService27 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService28 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService29 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService3 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService30 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService31 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService32 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService33 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService34 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService35 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService36 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService37 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService38 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService39 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService5 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService6 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService7 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService8 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService9 { <init>(); }
--keep class org.chromium.third_party.android.datausagechart.ChartDataUsageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.datausagechart.ChartNetworkSeriesView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.media.MediaController { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.AsyncViewStub { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ButtonCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.CheckableImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChipView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.OptimizedFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithClickableSpans { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithLeading { <init>(android.content.Context, android.util.AttributeSet); }
--keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
-    public <init>(android.content.Context, android.util.AttributeSet);
-    public <init>();
-}
--keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
-    public <init>(...);
-}
--keep public class * implements com.google.android.gms.cast.framework.OptionsProvider
--keep public class android.support.transition.FragmentTransitionSupport {
-}
--keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
-  public <init>();
-}
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes AnnotationDefault
--keepattributes RuntimeVisible*Annotations
--keepattributes SourceFile,LineNumberTable
--keepclasseswithmembernames,allowoptimization class com.google.common.logging.nano.Vr$VREvent$SdkConfigurationParams** {
-    *;
-}
--keepclasseswithmembernames,includedescriptorclasses class com.google.vr.** {
-    native <methods>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <fields>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <methods>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.AccessedByNative <fields>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <fields>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  @org.chromium.base.annotations.CalledByNative <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  @org.chromium.base.annotations.CalledByNativeUnchecked <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  native <methods>;
-}
--keepclassmembernames class * {
-  @com.google.android.gms.common.annotation.KeepName *;
-}
--keepclassmembernames,allowobfuscation class * {
-  @org.chromium.base.annotations.DoNotInline <methods>;
-}
--keepclassmembers class * extends com.google.android.gms.internal.clearcut.zzcg {
-  <fields>;
-}
--keepclassmembers class * implements android.os.Parcelable {
-  public static *** CREATOR;
-}
--keepclassmembers class * implements android.os.Parcelable {
-  public static final *** CREATOR;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.UsedByNative *;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByNative *;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByReflection *;
-}
--keepclassmembers class ** {
-    @android.arch.lifecycle.OnLifecycleEvent *;
-}
--keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
-    boolean mShiftingMode;
-}
--keepclassmembers class android.support.graphics.drawable.VectorDrawableCompat$* {
-   void set*(***);
-   *** get*();
-}
--keepclassmembers class android.support.transition.ChangeBounds$* extends android.animation.AnimatorListenerAdapter {
-  android.support.transition.ChangeBounds$ViewBounds mViewBounds;
-}
--keepclassmembers class com.google.android.gms.common.api.internal.BasePendingResult {
-  com.google.android.gms.common.api.internal.BasePendingResult$ReleasableResultGuardian mResultGuardian;
-}
--keepclassmembers class com.google.android.gms.gcm.GcmListenerService {
-    public void handleIntent(android.content.Intent);
-}
--keepclassmembers class org.chromium.** implements android.os.Parcelable {
-  public static *** CREATOR;
-}
--keepclassmembers enum * {
-    public static **[] values();
-}
--keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
-    <fields>;
-}
--keepclassmembers enum org.chromium.** {
-    public static **[] values();
-}
--keepclassmembers public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
-    public static final *** NULL;
-}
--keepnames @com.google.android.gms.common.annotation.KeepName class *
--keepnames class * implements android.os.Parcelable
--keepnames class * implements com.google.android.gms.common.internal.ReflectedParcelable
--keepnames class * implements org.chromium.components.background_task_scheduler.BackgroundTask {
-  public <init>();
-}
--keepnames class com.google.vr.ndk.** { *; }
--keepnames class com.google.vr.sdk.** { *; }
--keepnames class org.chromium.** implements android.os.Parcelable
--keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * {
-  *;
-}
--optimizationpasses 3
--optimizations !class/merging/horizontal
--renamesourcefileattribute PG
--repackageclasses ''
\ No newline at end of file
diff --git a/chrome/android/java/chrome_public_apk.proguard_flags.expected b/chrome/android/java/chrome_public_apk.proguard_flags.expected
deleted file mode 100644
index b14b0b54..0000000
--- a/chrome/android/java/chrome_public_apk.proguard_flags.expected
+++ /dev/null
@@ -1,581 +0,0 @@
--allowaccessmodification
--assumenosideeffects class ** {
-  @org.chromium.base.annotations.RemovableInRelease <methods>;
-}
--assumenosideeffects class android.util.Log {
-  static *** d(...);
-  static *** v(...);
-  static *** isLoggable(...);
-}
--dontpreverify
--dontwarn android.app.Notification
--dontwarn android.nfc.NfcAdapter
--dontwarn android.security.NetworkSecurityPolicy
--dontwarn android.support.**
--dontwarn com.google.android.apps.common.proguard.UsedBy*
--dontwarn com.google.common.logging.nano.Vr$**
--dontwarn com.google.protobuf.nano.NanoEnumValue
--dontwarn com.google.vr.**
--dontwarn javax.annotation.**
--dontwarn libcore.io.Memory
--dontwarn org.checkerframework.**
--dontwarn org.chromium.base.library_loader.NativeLibraries
--dontwarn sun.misc.Unsafe
--keep @android.support.annotation.Keep class *
--keep @com.google.android.gms.common.util.DynamiteApi public class * {
-  public <fields>;
-  public <methods>;
-}
--keep @com.google.vr.cardboard.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByReflection class *
--keep @interface android.support.annotation.Keep
--keep @interface com.google.android.gms.common.annotation.KeepName
--keep @interface com.google.android.gms.common.util.DynamiteApi
--keep @interface org.chromium.base.annotations.AccessedByNative
--keep @interface org.chromium.base.annotations.CalledByNative
--keep @interface org.chromium.base.annotations.CalledByNativeUnchecked
--keep @interface org.chromium.base.annotations.DoNotInline
--keep @interface org.chromium.base.annotations.RemovableInRelease
--keep @interface org.chromium.base.annotations.UsedByReflection
--keep @org.chromium.base.annotations.UsedByReflection class * {}
--keep class * implements android.arch.lifecycle.GenericLifecycleObserver {
-    <init>(...);
-}
--keep class * implements android.arch.lifecycle.LifecycleObserver {
-}
--keep class * implements org.chromium.base.test.params.ParameterProvider
--keep class android.support.customtabs.PostMessageService { <init>(); }
--keep class android.support.customtabs.browseractions.BrowserActionsFallbackMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.customtabs.browseractions.BrowserServiceFileProvider { <init>(); }
--keep class android.support.design.internal.BaselineLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.SnackbarContentLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.AppBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CheckableImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CoordinatorLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.Snackbar$SnackbarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.media.** implements android.os.Parcelable {
-    public static final android.os.Parcelable$Creator *;
-}
--keep class android.support.v4.view.ViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.NestedScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.Space { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.AlertController$RecycleListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteExpandCollapseButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteVolumeSlider { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.OverlayListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ActionMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ExpandedMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ListMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarOverlayLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActivityChooserView$InnerLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AlertDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatSpinner { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ButtonBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ContentFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.DialogTitle { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.GridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.RecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SearchView {
-  public <init>(...);
-}
--keep class android.support.v7.widget.SearchView$SearchAutoComplete { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SwitchCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.Toolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ViewStubCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.widget.RadioButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider { <init>(); }
--keep class com.google.android.gms.cast.framework.ReconnectionService { <init>(); }
--keep class com.google.android.gms.cast.framework.internal.featurehighlight.HelpTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.google.android.gms.cast.framework.media.MediaIntentReceiver { <init>(); }
--keep class com.google.android.gms.cast.framework.media.MediaNotificationService { <init>(); }
--keep class com.google.android.gms.common.api.GoogleApiActivity { <init>(); }
--keep class com.google.android.gms.common.internal.ReflectedParcelable
--keep class com.google.android.gms.gcm.GcmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.external.client.contrib.AndroidListener$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.TiclService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidInstanceIDListenerService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService { <init>(); }
--keep class com.google.vr.cardboard.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByReflection
--keep class org.chromium.build.BuildHooksAndroidImpl
--keep class org.chromium.chrome.browser.BrowserRestartActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeApplication { <init>(); }
--keep class org.chromium.chrome.browser.ChromeBackgroundService { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.LauncherShortcutActivity { <init>(); }
--keep class org.chromium.chrome.browser.NoTouchActivity { <init>(); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuItemIcon { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModernView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryTabLayoutView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.TouchEventFilter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActionBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkEditActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkItemRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy { <init>(); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetService { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionActivity { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionsService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataDialogActivity { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ManageTrustedWebActivityDataActivity { <init>(); }
--keep class org.chromium.chrome.browser.compositor.CompositorViewHolder { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactsPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.TopView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextual_suggestions.ToolbarView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.coordinator.CoordinatorLayoutForPointer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService { <init>(); }
--keep class org.chromium.chrome.browser.crash.MinidumpUploadService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabsConnectionService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.PaymentHandlerActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.document.ChromeLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.DocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.IncognitoDocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.dom_distiller.DistilledPagePrefsView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.DownloadActivity { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastManager { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadForegroundService { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadLocationCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AsyncImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.CircularProgressView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.ForegroundRoundedCornerImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.toolbar.DownloadHomeToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.view.SelectionView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.OfflineGroupHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunChooserView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.TabbedModeFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.history.HistoryManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.incognito.IncognitoDisclosureActivity { <init>(); }
--keep class org.chromium.chrome.browser.incognito.IncognitoNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.infobar.InfoBarMessageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.TextViewEllipsizerSafe { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabContent { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService { <init>(); }
--keep class org.chromium.chrome.browser.jsdialog.JavascriptDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaCaptureNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.ExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.FullscreenMediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.modaldialog.ModalDialogView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationJobService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$TimeoutReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageViewMD { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.LogoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NativePageRootFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout$SearchBoxContainerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsExpandableListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsGroupView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.cards.ProgressIndicatorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.snippets.SectionHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$SettingsReceiver { <init>(); }
--keep class org.chromium.chrome.browser.omaha.OmahaClient { <init>(); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.UrlBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.status.StatusView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.page_info.PageInfoView$ElidedUrlTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.password_manager.PasswordGenerationDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestHeader { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestUiErrorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.DecoderService { <init>(); }
--keep class org.chromium.chrome.browser.photo_picker.PhotoPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.PickerBitmapView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.AboutChromePreferenceOSVersion { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBasePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeSwitchPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ExpandablePreferenceGroup { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.HyperlinkPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.LearnMorePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.Preferences { <init>(); }
--keep class org.chromium.chrome.browser.preferences.SeekBarLinkedCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SignInPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SpinnerPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncErrorCardPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncedAccountPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextAndButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextMessagePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextScalePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionMainMenuItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionSiteBreakdownView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionStatsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.download.DownloadLocationPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.languages.LanguageListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ClearWebsiteStorage { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ManageSpaceActivity { <init>(); }
--keep class org.chromium.chrome.browser.preferences.website.SiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.TriStateSiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.prerender.ChromePrerenderService { <init>(); }
--keep class org.chromium.chrome.browser.printing.PrintShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.provider.ChromeBrowserProvider { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivity { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivityLocationBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.searchwidget.SearchWidgetProvider { <init>(); }
--keep class org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.services.AccountsChangedReceiver { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninChooseView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninConfirmationView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.PersonalizedSigninPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.SigninScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SyncPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.BottomContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.TemplatePreservingTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.TileGridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.sync.ui.PassphraseActivity { <init>(); }
--keep class org.chromium.chrome.browser.tab.SadTabView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.HomeButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.MenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.NewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.TabSwitcherButtonView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.SearchAccelerator { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ShareButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar$InterceptTouchLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.TabSwitcherModeTTPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer$ToolbarViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.tracing.TracingNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeActivity { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeIntentService { <init>(); }
--keep class org.chromium.chrome.browser.util.ChromeFileProvider { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrCancelAnimationActivity { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.SameTaskWebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.widget.AlertDialogEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.BoundedLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.CompatibilityTextInputLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.DualControlLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.EmptyAlertEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingEdgeScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingShadowView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.ListMenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.LoadingView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.MaterialProgressBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.NumberRollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.PromoDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonWithDescription { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelWrapper { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.TouchRestrictingFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbar$FindQuery { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.incognitotoggle.IncognitoToggleButtonTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.EditorDialogToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.ExpandableGridView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.selection.SelectableListLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskGcmTaskService { <init>(); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskJobService { <init>(); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerAdvanced { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerMoreButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerSimple { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.content.app.PrivilegedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.PrivilegedProcessService0 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService1 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService2 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService3 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.SandboxedProcessService0 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService1 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService10 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService11 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService12 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService13 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService14 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService15 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService16 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService17 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService18 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService19 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService2 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService20 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService21 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService22 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService23 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService24 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService25 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService26 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService27 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService28 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService29 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService3 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService30 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService31 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService32 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService33 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService34 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService35 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService36 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService37 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService38 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService39 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService5 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService6 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService7 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService8 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService9 { <init>(); }
--keep class org.chromium.third_party.android.datausagechart.ChartDataUsageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.datausagechart.ChartNetworkSeriesView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.media.MediaController { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.AsyncViewStub { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ButtonCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.CheckableImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChipView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.OptimizedFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithClickableSpans { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithLeading { <init>(android.content.Context, android.util.AttributeSet); }
--keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
-    public <init>(android.content.Context, android.util.AttributeSet);
-    public <init>();
-}
--keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
-    public <init>(...);
-}
--keep public class * implements com.google.android.gms.cast.framework.OptionsProvider
--keep public class android.support.transition.FragmentTransitionSupport {
-}
--keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
-  public <init>();
-}
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes AnnotationDefault
--keepattributes RuntimeVisible*Annotations
--keepattributes SourceFile,LineNumberTable
--keepclasseswithmembernames,allowoptimization class com.google.common.logging.nano.Vr$VREvent$SdkConfigurationParams** {
-    *;
-}
--keepclasseswithmembernames,includedescriptorclasses class com.google.vr.** {
-    native <methods>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <fields>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <methods>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.AccessedByNative <fields>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <fields>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  @org.chromium.base.annotations.CalledByNative <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  @org.chromium.base.annotations.CalledByNativeUnchecked <methods>;
-}
--keepclasseswithmembers,includedescriptorclasses class * {
-  native <methods>;
-}
--keepclassmembernames class * {
-  @com.google.android.gms.common.annotation.KeepName *;
-}
--keepclassmembernames,allowobfuscation class * {
-  @org.chromium.base.annotations.DoNotInline <methods>;
-}
--keepclassmembers class * extends com.google.android.gms.internal.clearcut.zzcg {
-  <fields>;
-}
--keepclassmembers class * implements android.os.Parcelable {
-  public static *** CREATOR;
-}
--keepclassmembers class * implements android.os.Parcelable {
-  public static final *** CREATOR;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.UsedByNative *;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByNative *;
-}
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByReflection *;
-}
--keepclassmembers class ** {
-    @android.arch.lifecycle.OnLifecycleEvent *;
-}
--keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
-    boolean mShiftingMode;
-}
--keepclassmembers class android.support.graphics.drawable.VectorDrawableCompat$* {
-   void set*(***);
-   *** get*();
-}
--keepclassmembers class android.support.transition.ChangeBounds$* extends android.animation.AnimatorListenerAdapter {
-  android.support.transition.ChangeBounds$ViewBounds mViewBounds;
-}
--keepclassmembers class com.google.android.gms.common.api.internal.BasePendingResult {
-  com.google.android.gms.common.api.internal.BasePendingResult$ReleasableResultGuardian mResultGuardian;
-}
--keepclassmembers class com.google.android.gms.gcm.GcmListenerService {
-    public void handleIntent(android.content.Intent);
-}
--keepclassmembers class org.chromium.** implements android.os.Parcelable {
-  public static *** CREATOR;
-}
--keepclassmembers enum * {
-    public static **[] values();
-}
--keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
-    <fields>;
-}
--keepclassmembers enum org.chromium.** {
-    public static **[] values();
-}
--keepclassmembers public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
-    public static final *** NULL;
-}
--keepnames @com.google.android.gms.common.annotation.KeepName class *
--keepnames class * implements android.os.Parcelable
--keepnames class * implements com.google.android.gms.common.internal.ReflectedParcelable
--keepnames class * implements org.chromium.components.background_task_scheduler.BackgroundTask {
-  public <init>();
-}
--keepnames class com.google.vr.ndk.** { *; }
--keepnames class com.google.vr.sdk.** { *; }
--keepnames class org.chromium.** implements android.os.Parcelable
--keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * {
-  *;
-}
--optimizationpasses 3
--optimizations !class/merging/horizontal
--renamesourcefileattribute PG
--repackageclasses ''
\ No newline at end of file
diff --git a/chrome/android/java/monochrome_public_apk.proguard_flags.expected b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
index 13c5d7a..43076576 100644
--- a/chrome/android/java/monochrome_public_apk.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
@@ -1,650 +1,672 @@
--allowaccessmodification
--assumenosideeffects class ** {
-  @org.chromium.base.annotations.RemovableInRelease <methods>;
+################################################################################
+# ../../android_webview/apk/java/proguard.flags
+################################################################################
+# 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.
+
+-keepclassmembers class org.chromium.android_webview.AwPdfExporter {
+    android.view.ViewGroup mContainerView;
 }
+
+# Keep the factory and its public members; it's the main entry point used by the
+# framework.
+-keep class com.android.webview.chromium.WebViewChromiumFactoryProvider {
+    public *;
+}
+
+-keep class * implements android.webkit.WebViewFactoryProvider$Statics {
+    *;
+}
+
+-keep class com.android.webview.chromium.ContentSettingsAdapter {
+    public *;
+}
+
+-keep class com.android.webview.chromium.WebViewChromiumFactoryProviderFor* {
+    public *;
+}
+
+-keep class com.android.webview.chromium.WebViewDatabaseAdapter {
+  public *;
+}
+
+# This is the main entry point for APIs. It is kept to make developing with
+# unreleased Android easier.
+-keep class com.android.webview.chromium.WebViewChromium {
+  public *;
+}
+
+# Functor classes with native methods implemented in Android.
+-keep class com.android.webview.chromium.DrawFunctor
+-keep class com.android.webview.chromium.DrawGLFunctor
+-keep class com.android.webview.chromium.GraphicsUtils
+
+# Linker dynamically casts to $TestRunner when running tests. We don't run these
+# tests in WebView.
+-dontnote org.chromium.base.library_loader.Linker$TestRunner
+
+# Don't note about the API 21 compatibility code which references various
+# hidden APIs via reflection.
+-dontnote com.android.webview.chromium.WebViewDelegateFactory$Api21CompatibilityDelegate
+
+# DefaultAndroidKeyStore uses reflection to access internal OpenSSL state.
+-dontnote org.chromium.net.DefaultAndroidKeyStore
+
+# MediaPlayerBridge uses reflection to access internal metadata.
+-dontnote org.chromium.media.MediaPlayerBridge
+
+# ProxyChangeListener$ProxyReceiver uses reflection to access internal
+# android.net.ProxyProperties.
+-dontnote org.chromium.net.ProxyChangeListener$ProxyReceiver
+
+# Silence warnings about reflection used to check for onShow/HideCustomView.
+# This class is not really kept since it's in a library jar.
+-keep class android.webkit.WebChromeClient {
+  void onShowCustomView(...);
+  void onHideCustomView();
+}
+
+-keep class org.chromium.android_webview.AwBrowserProcess {
+    java.nio.channels.FileLock sExclusiveFileLock;
+}
+
+# Accessed via reflection but not present in all builds
+-keep class com.android.webview.chromium.AwSafeBrowsingApiHandler {
+  AwSafeBrowsingApiHandler(...);
+}
+-dontnote com.android.webview.chromium.AwSafeBrowsingApiHandler
+
+# We strip some unused resources when preprocessing the GMS client libs.
+-dontwarn com.google.android.gms.R**
+
+# Trichrome builds don't include a native library list in the main APK; it's
+# picked up from the library APK at runtime.
+-dontwarn org.chromium.base.library_loader.NativeLibraries
+
+################################################################################
+# ../../android_webview/support_library/boundary_interfaces/proguard.flags
+################################################################################
+# Copyright 2018 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.
+
+# We need to avoid obfuscating the support library boundary interface because
+# this API is shared with the Android Support Library.
+# Note that we only 'keep' the package org.chromium.support_lib_boundary itself,
+# any sub-packages of that package can still be obfuscated.
+-keep public class org.chromium.support_lib_boundary.* { public *; }
+
+################################################################################
+# ../../base/android/proguard/chromium_apk.flags
+################################################################################
+# 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.
+
+# Contains flags that we'd like all Chromium .apks to use.
+
+# Not needed for Android and saves a bit of processing time.
+-dontpreverify
+
+# Keep line number information, useful for stack traces.
+-keepattributes SourceFile,LineNumberTable
+
+# Keep all CREATOR fields within Parcelable that are kept.
+-keepclassmembers class * implements android.os.Parcelable {
+  public static *** CREATOR;
+}
+
+# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
+# If we annotated all Parcelables that get put into Bundles other than
+# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
+# names of just those ones. For now, we'll just keep them all.
+-keepnames class * implements android.os.Parcelable
+
+# Keep all enum values and valueOf methods. See
+# http://proguard.sourceforge.net/index.html#manual/examples.html
+# for the reason for this. Also, see http://crbug.com/248037.
+-keepclassmembers enum * {
+    public static **[] values();
+}
+
+# Keep classes implementing ParameterProvider -- these will be instantiated
+# via reflection.
+-keep class * implements org.chromium.base.test.params.ParameterProvider
+
+# Allows Proguard freedom in removing these log related calls. We ask for debug
+# and verbose logs to be stripped out in base.Log, so we are just ensuring we
+# get rid of all other debug/verbose logs.
 -assumenosideeffects class android.util.Log {
   static *** d(...);
   static *** v(...);
   static *** isLoggable(...);
 }
--dontnote com.android.webview.chromium.AwSafeBrowsingApiHandler
--dontnote com.android.webview.chromium.WebViewDelegateFactory$Api21CompatibilityDelegate
--dontnote org.chromium.base.library_loader.Linker$TestRunner
--dontnote org.chromium.media.MediaPlayerBridge
--dontnote org.chromium.net.DefaultAndroidKeyStore
--dontnote org.chromium.net.ProxyChangeListener$ProxyReceiver
--dontpreverify
--dontwarn android.app.Notification
--dontwarn android.nfc.NfcAdapter
--dontwarn android.security.NetworkSecurityPolicy
+
+# The following chart was created on July 20, 2016, to decide on 3 optimization
+# passes for Chrome.
+# optimization passes | time | .dex size | dirty memory per process
+# -----------------------------------------------------------------
+#          1          | 0:48 |  5805676  |         488972
+#          2          | 1:07 |  5777376  |         487092
+#          3          | 1:24 |  5772192  |         486596
+#          4          | 1:42 |  5771124  |         486484
+#          5          | 1:56 |  5770504  |         486432
+-optimizationpasses 3
+
+# Horizontal class merging marginally increases dex size (as of Mar 2018).
+-optimizations !class/merging/horizontal
+
+# Allowing Proguard to change modifiers. This change shrinks the .dex size by
+# ~1%, and reduces the method count by ~4%.
+-allowaccessmodification
+
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version.  We know about them, and they are safe.
 -dontwarn android.support.**
--dontwarn com.google.android.apps.common.proguard.UsedBy*
--dontwarn com.google.android.gms.R**
--dontwarn com.google.common.logging.nano.Vr$**
--dontwarn com.google.protobuf.nano.NanoEnumValue
--dontwarn com.google.vr.**
--dontwarn javax.annotation.**
--dontwarn libcore.io.Memory
--dontwarn org.checkerframework.**
--dontwarn org.chromium.base.library_loader.NativeLibraries
--dontwarn org.chromium.base.library_loader.NativeLibraries
--dontwarn sun.misc.Unsafe
--keep @android.support.annotation.Keep class *
--keep @com.google.android.gms.common.util.DynamiteApi public class * {
-  public <fields>;
-  public <methods>;
-}
--keep @com.google.ar.core.annotations.UsedByNative class *
--keep @com.google.ar.core.annotations.UsedByReflection class *
--keep @com.google.vr.cardboard.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByNative class *
--keep @com.google.vr.cardboard.annotations.UsedByReflection class *
--keep @com.google.vr.dynamite.client.UsedByNative class *
--keep @com.google.vr.dynamite.client.UsedByReflection class *
--keep @interface android.support.annotation.Keep
--keep @interface com.google.android.gms.common.annotation.KeepName
--keep @interface com.google.android.gms.common.util.DynamiteApi
+
+################################################################################
+# ../../base/android/proguard/chromium_code.flags
+################################################################################
+# 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.
+
+# Contains flags that can be safely shared with Cronet, and thus would be
+# appropriate for third-party apps to include.
+
+# Keep all annotation related attributes that can affect runtime
+-keepattributes RuntimeVisible*Annotations
+-keepattributes AnnotationDefault
+
+# Keep the annotations, because if we don't, the ProGuard rules that use them
+# will not be respected. These classes then show up in our final dex, which we
+# do not want - see crbug.com/628226.
 -keep @interface org.chromium.base.annotations.AccessedByNative
 -keep @interface org.chromium.base.annotations.CalledByNative
 -keep @interface org.chromium.base.annotations.CalledByNativeUnchecked
 -keep @interface org.chromium.base.annotations.DoNotInline
 -keep @interface org.chromium.base.annotations.RemovableInRelease
 -keep @interface org.chromium.base.annotations.UsedByReflection
+
+# Keeps for class level annotations.
 -keep @org.chromium.base.annotations.UsedByReflection class * {}
--keep class * implements android.arch.lifecycle.GenericLifecycleObserver {
-    <init>(...);
-}
--keep class * implements android.arch.lifecycle.LifecycleObserver {
-}
--keep class * implements android.webkit.WebViewFactoryProvider$Statics {
-    *;
-}
--keep class * implements org.chromium.base.test.params.ParameterProvider
--keep class android.support.customtabs.PostMessageService { <init>(); }
--keep class android.support.customtabs.browseractions.BrowserActionsFallbackMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.customtabs.browseractions.BrowserServiceFileProvider { <init>(); }
--keep class android.support.design.internal.BaselineLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.NavigationMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.internal.SnackbarContentLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.AppBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CheckableImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.CoordinatorLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.Snackbar$SnackbarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.design.widget.TabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.media.** implements android.os.Parcelable {
-    public static final android.os.Parcelable$Creator *;
-}
--keep class android.support.v4.view.ViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.NestedScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v4.widget.Space { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.AlertController$RecycleListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteExpandCollapseButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.MediaRouteVolumeSlider { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.app.OverlayListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ActionMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ExpandedMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.view.menu.ListMenuItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarContextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionBarOverlayLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActionMenuView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ActivityChooserView$InnerLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AlertDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.AppCompatSpinner { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ButtonBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ContentFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.DialogTitle { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.FitWindowsLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.GridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.RecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SearchView {
-  public <init>(...);
-}
--keep class android.support.v7.widget.SearchView$SearchAutoComplete { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.SwitchCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.Toolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.support.v7.widget.ViewStubCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.webkit.WebChromeClient {
-  void onShowCustomView(...);
-  void onHideCustomView();
-}
--keep class android.widget.LinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class android.widget.RadioButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.android.webview.chromium.AwSafeBrowsingApiHandler {
-  AwSafeBrowsingApiHandler(...);
-}
--keep class com.android.webview.chromium.ContentSettingsAdapter {
-    public *;
-}
--keep class com.android.webview.chromium.DrawFunctor
--keep class com.android.webview.chromium.DrawGLFunctor
--keep class com.android.webview.chromium.GraphicsUtils
--keep class com.android.webview.chromium.LicenseActivity { <init>(); }
--keep class com.android.webview.chromium.LicenseContentProvider { <init>(); }
--keep class com.android.webview.chromium.WebViewChromium {
-  public *;
-}
--keep class com.android.webview.chromium.WebViewChromiumFactoryProvider {
-    public *;
-}
--keep class com.android.webview.chromium.WebViewChromiumFactoryProviderFor* {
-    public *;
-}
--keep class com.android.webview.chromium.WebViewDatabaseAdapter {
-  public *;
-}
--keep class com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider { <init>(); }
--keep class com.google.android.gms.cast.framework.ReconnectionService { <init>(); }
--keep class com.google.android.gms.cast.framework.internal.featurehighlight.HelpTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class com.google.android.gms.cast.framework.media.MediaIntentReceiver { <init>(); }
--keep class com.google.android.gms.cast.framework.media.MediaNotificationService { <init>(); }
--keep class com.google.android.gms.common.api.GoogleApiActivity { <init>(); }
--keep class com.google.android.gms.common.internal.ReflectedParcelable
--keep class com.google.android.gms.gcm.GcmReceiver { <init>(); }
--keep class com.google.ar.core.InstallActivity { <init>(); }
--keep class com.google.ar.core.annotations.UsedByNative
--keep class com.google.ar.core.annotations.UsedByReflection
--keep class com.google.ipc.invalidation.external.client.contrib.AndroidListener$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.TiclService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidInstanceIDListenerService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService { <init>(); }
--keep class com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService { <init>(); }
--keep class com.google.vr.cardboard.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByNative
--keep class com.google.vr.cardboard.annotations.UsedByReflection
--keep class com.google.vr.dynamite.client.ILoadedInstanceCreator { *; }
--keep class com.google.vr.dynamite.client.INativeLibraryLoader { *; }
--keep class com.google.vr.dynamite.client.IObjectWrapper { *; }
--keep class com.google.vr.dynamite.client.UsedByNative
--keep class com.google.vr.dynamite.client.UsedByReflection
--keep class org.chromium.android_webview.AwBrowserProcess {
-    java.nio.channels.FileLock sExclusiveFileLock;
-}
--keep class org.chromium.android_webview.services.AwMinidumpUploadJobService { <init>(); }
--keep class org.chromium.android_webview.services.AwVariationsSeedFetcher { <init>(); }
--keep class org.chromium.android_webview.services.CrashReceiverService { <init>(); }
--keep class org.chromium.android_webview.services.VariationsSeedServer { <init>(); }
--keep class org.chromium.build.BuildHooksAndroidImpl
--keep class org.chromium.chrome.browser.BrowserRestartActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeBackgroundService { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.ChromeTabbedActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.LauncherShortcutActivity { <init>(); }
--keep class org.chromium.chrome.browser.MonochromeApplication { <init>(); }
--keep class org.chromium.chrome.browser.NoTouchActivity { <init>(); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.appmenu.AppMenuItemIcon { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModernView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryTabLayoutView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.autofill_assistant.ui.TouchEventFilter { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActionBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkEditActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity { <init>(); }
--keep class org.chromium.chrome.browser.bookmarks.BookmarkItemRow { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy { <init>(); }
--keep class org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetService { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionActivity { <init>(); }
--keep class org.chromium.chrome.browser.browseractions.BrowserActionsService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataDialogActivity { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClearDataService { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.browserservices.ManageTrustedWebActivityDataActivity { <init>(); }
--keep class org.chromium.chrome.browser.compositor.CompositorViewHolder { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.ContactsPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contacts_picker.TopView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.contextual_suggestions.ToolbarView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.coordinator.CoordinatorLayoutForPointer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService { <init>(); }
--keep class org.chromium.chrome.browser.crash.MinidumpUploadService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.CustomTabsConnectionService { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.PaymentHandlerActivity { <init>(); }
--keep class org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.ChromeLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.DocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.document.IncognitoDocumentActivity { <init>(); }
--keep class org.chromium.chrome.browser.dom_distiller.DistilledPagePrefsView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.DownloadActivity { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastManager { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadForegroundService { <init>(); }
--keep class org.chromium.chrome.browser.download.DownloadLocationCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AspectRatioFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.AsyncImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.CircularProgressView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.list.view.ForegroundRoundedCornerImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.toolbar.DownloadHomeToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.home.view.SelectionView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.DownloadManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.download.ui.OfflineGroupHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExperimentalExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.explore_sites.ExploreSitesTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunChooserView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.FirstRunView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.firstrun.TabbedModeFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryActivity { <init>(); }
--keep class org.chromium.chrome.browser.history.HistoryItemView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.history.HistoryManagerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.incognito.IncognitoDisclosureActivity { <init>(); }
--keep class org.chromium.chrome.browser.incognito.IncognitoNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.infobar.InfoBarMessageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.TextViewEllipsizerSafe { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabContent { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.infobar.translate.TranslateTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService { <init>(); }
--keep class org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService { <init>(); }
--keep class org.chromium.chrome.browser.jsdialog.JavascriptDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaCaptureNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.media.MediaLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.ExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.remote.FullscreenMediaRouteButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationListenerService { <init>(); }
--keep class org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver { <init>(); }
--keep class org.chromium.chrome.browser.modaldialog.ModalDialogView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationJobService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService { <init>(); }
--keep class org.chromium.chrome.browser.notifications.NotificationService$Receiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$TimeoutReceiver { <init>(); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.IncognitoNewTabPageViewMD { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.LogoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NativePageRootFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageLayout$SearchBoxContainerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.NewTabPageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsExpandableListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.RecentTabsGroupView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.cards.ProgressIndicatorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.ntp.snippets.SectionHeaderView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$ClickReceiver { <init>(); }
--keep class org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$SettingsReceiver { <init>(); }
--keep class org.chromium.chrome.browser.omaha.OmahaClient { <init>(); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.LocationBarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.UrlBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.omnibox.status.StatusView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.page_info.PageInfoView$ElidedUrlTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.password_manager.PasswordGenerationDialogCustomView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestBottomBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestHeader { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.payments.ui.PaymentRequestUiErrorView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.DecoderService { <init>(); }
--keep class org.chromium.chrome.browser.photo_picker.PhotoPickerToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.photo_picker.PickerBitmapView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.AboutChromePreferenceOSVersion { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBaseListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeBasePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ChromeSwitchPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.ExpandablePreferenceGroup { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.HyperlinkPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.LearnMorePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.Preferences { <init>(); }
--keep class org.chromium.chrome.browser.preferences.SeekBarLinkedCheckBoxPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SignInPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SpinnerPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncErrorCardPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.SyncedAccountPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextAndButtonPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextMessagePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.TextScalePreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionMainMenuItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionSiteBreakdownView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.preferences.datareduction.DataReductionStatsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.download.DownloadLocationPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.languages.LanguageListPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ClearWebsiteStorage { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.ManageSpaceActivity { <init>(); }
--keep class org.chromium.chrome.browser.preferences.website.SiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.preferences.website.TriStateSiteSettingsPreference { <init>(...); }
--keep class org.chromium.chrome.browser.prerender.ChromePrerenderService { <init>(); }
--keep class org.chromium.chrome.browser.printing.PrintShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.provider.ChromeBrowserProvider { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivity { <init>(); }
--keep class org.chromium.chrome.browser.searchwidget.SearchActivityLocationBarLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.searchwidget.SearchWidgetProvider { <init>(); }
--keep class org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity { <init>(); }
--keep class org.chromium.chrome.browser.services.AccountsChangedReceiver { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService { <init>(); }
--keep class org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.AccountSigninChooseView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninConfirmationView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.AccountSigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.PersonalizedSigninPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninActivity { <init>(); }
--keep class org.chromium.chrome.browser.signin.SigninScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SigninView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.signin.SyncPromoView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.BottomContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.snackbar.TemplatePreservingTextView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.SuggestionsTileView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.suggestions.TileGridLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.sync.ui.PassphraseActivity { <init>(); }
--keep class org.chromium.chrome.browser.tab.SadTabView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.HomeButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.MenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.NewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.TabSwitcherButtonView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.BottomToolbarNewTabButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.CloseAllTabsButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.SearchAccelerator { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.bottom.ShareButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.CustomTabToolbar$InterceptTouchLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.TabSwitcherModeTTPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer$ToolbarViewResourceFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.toolbar.top.ToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.tracing.TracingNotificationService { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeActivity { <init>(); }
--keep class org.chromium.chrome.browser.upgrade.UpgradeIntentService { <init>(); }
--keep class org.chromium.chrome.browser.util.ChromeFileProvider { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrCancelAnimationActivity { <init>(); }
--keep class org.chromium.chrome.browser.vr.VrFirstRunActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.SameTaskWebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebApkActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity0 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity1 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity2 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity3 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity4 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity5 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity6 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity7 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity8 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappActivity9 { <init>(); }
--keep class org.chromium.chrome.browser.webapps.WebappLauncherActivity { <init>(); }
--keep class org.chromium.chrome.browser.widget.AlertDialogEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.BoundedLinearLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.CompatibilityTextInputLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.DualControlLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.EmptyAlertEditText { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingEdgeScrollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.FadingShadowView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.ListMenuButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.LoadingView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.MaterialProgressBar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.NumberRollView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.PromoDialogLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.RadioButtonWithDescription { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListItem { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelListView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.accessibility.AccessibilityTabModelWrapper { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.BottomSheet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.bottomsheet.TouchRestrictingFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbar$FindQuery { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarPhone { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.findinpage.FindToolbarTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.incognitotoggle.IncognitoToggleButtonTablet { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.EditorDialogToolbar { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.prefeditor.ExpandableGridView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.chrome.browser.widget.selection.SelectableListLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskGcmTaskService { <init>(); }
--keep class org.chromium.components.background_task_scheduler.BackgroundTaskJobService { <init>(); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerAdvanced { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerMoreButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.components.embedder_support.delegate.ColorPickerSimple { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.content.app.PrivilegedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.PrivilegedProcessService0 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService1 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService2 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService3 { <init>(); }
--keep class org.chromium.content.app.PrivilegedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService {
-  public <init>();
-}
--keep class org.chromium.content.app.SandboxedProcessService0 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService1 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService10 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService11 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService12 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService13 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService14 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService15 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService16 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService17 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService18 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService19 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService2 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService20 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService21 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService22 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService23 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService24 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService25 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService26 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService27 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService28 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService29 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService3 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService30 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService31 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService32 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService33 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService34 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService35 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService36 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService37 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService38 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService39 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService4 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService5 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService6 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService7 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService8 { <init>(); }
--keep class org.chromium.content.app.SandboxedProcessService9 { <init>(); }
--keep class org.chromium.third_party.android.datausagechart.ChartDataUsageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.datausagechart.ChartNetworkSeriesView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.third_party.android.media.MediaController { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.AsyncViewStub { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ButtonCompat { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.CheckableImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChipView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageButton { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.ChromeImageView { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.OptimizedFrameLayout { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithClickableSpans { <init>(android.content.Context, android.util.AttributeSet); }
--keep class org.chromium.ui.widget.TextViewWithLeading { <init>(android.content.Context, android.util.AttributeSet); }
--keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
-    public <init>(android.content.Context, android.util.AttributeSet);
-    public <init>();
-}
--keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
-    public <init>(...);
-}
--keep public class * implements com.google.android.gms.cast.framework.OptionsProvider
--keep public class android.support.transition.FragmentTransitionSupport {
-}
--keep public class com.google.ar.core.** {*;}
--keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
-  public <init>();
-}
--keep public class org.chromium.support_lib_boundary.* { public *; }
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes *Annotation*
--keepattributes AnnotationDefault
--keepattributes RuntimeVisible*Annotations
--keepattributes SourceFile,LineNumberTable
--keepclasseswithmembernames,allowoptimization class com.google.common.logging.nano.Vr$VREvent$SdkConfigurationParams** {
-    *;
-}
--keepclasseswithmembernames,includedescriptorclasses class com.google.ar.** {
-    native <methods>;
-}
--keepclasseswithmembernames,includedescriptorclasses class com.google.vr.** {
-    native <methods>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <fields>;
-}
--keepclasseswithmembers class * {
-  @android.support.annotation.Keep <methods>;
-}
+
+# Keeps for method level annotations.
 -keepclasseswithmembers class * {
   @org.chromium.base.annotations.AccessedByNative <fields>;
 }
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <fields>;
-}
--keepclasseswithmembers class * {
-  @org.chromium.base.annotations.UsedByReflection <methods>;
-}
 -keepclasseswithmembers,includedescriptorclasses class * {
   @org.chromium.base.annotations.CalledByNative <methods>;
 }
 -keepclasseswithmembers,includedescriptorclasses class * {
   @org.chromium.base.annotations.CalledByNativeUnchecked <methods>;
 }
+-keepclasseswithmembers class * {
+  @org.chromium.base.annotations.UsedByReflection <methods>;
+}
+-keepclasseswithmembers class * {
+  @org.chromium.base.annotations.UsedByReflection <fields>;
+}
 -keepclasseswithmembers,includedescriptorclasses class * {
   native <methods>;
 }
--keepclassmembernames class * {
-  @com.google.android.gms.common.annotation.KeepName *;
+
+# Remove methods annotated with this if their return value is unused.
+-assumenosideeffects class ** {
+  @org.chromium.base.annotations.RemovableInRelease <methods>;
+}
+
+# Never inline classes or methods with this annotation, but allow shrinking and
+# obfuscation.
+-keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * {
+  *;
 }
 -keepclassmembernames,allowobfuscation class * {
   @org.chromium.base.annotations.DoNotInline <methods>;
 }
--keepclassmembers class * extends com.google.android.gms.internal.clearcut.zzcg {
-  <fields>;
-}
--keepclassmembers class * implements android.os.Parcelable {
+
+# Keep all CREATOR fields within Parcelable that are kept.
+-keepclassmembers class org.chromium.** implements android.os.Parcelable {
   public static *** CREATOR;
 }
--keepclassmembers class * implements android.os.Parcelable {
-  public static final *** CREATOR;
+
+# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
+# If we annotated all Parcelables that get put into Bundles other than
+# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
+# names of just those ones. For now, we'll just keep them all.
+-keepnames class org.chromium.** implements android.os.Parcelable
+
+# Keep all enum values and valueOf methods. See
+# http://proguard.sourceforge.net/index.html#manual/examples.html
+# for the reason for this. Also, see http://crbug.com/248037.
+-keepclassmembers enum org.chromium.** {
+    public static **[] values();
 }
--keepclassmembers class * {
-    @com.google.ar.core.annotations.UsedByNative *;
+
+################################################################################
+# ../../base/android/proguard/enable_obfuscation.flags
+################################################################################
+# 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.
+
+# As of August 11, 2016, obfuscation was found to save 660kb on our .dex size
+# and 53kb memory/process (through shrinking method/string counts).
+-renamesourcefileattribute PG
+-repackageclasses ''
+
+################################################################################
+# ../../build/android/buildhooks/proguard/build_hooks_android_impl.flags
+################################################################################
+# Copyright 2018 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.
+
+-keep class org.chromium.build.BuildHooksAndroidImpl
+
+################################################################################
+# ../../chrome/android/java/proguard.flags
+################################################################################
+# 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.
+
+# Fragments loaded by name via Fragment.instantiate(Context,String)
+# Not all fragments in this package are PreferenceFragments. E.g. HomepageEditor
+# is a normal Fragment.
+-keep public class org.chromium.chrome.browser.** extends android.app.Fragment {
+  public <init>();
 }
--keepclassmembers class * {
-    @com.google.ar.core.annotations.UsedByReflection *;
+
+# These classes aren't themselves referenced, but __ProcessService[0,1,2...] are
+# referenced, and we look up these services by appending a number onto the name
+# of the base class. Thus, we need to keep the base class name around so that
+# the child classes can be looked up.
+-keep class org.chromium.content.app.SandboxedProcessService {
+  public <init>();
 }
--keepclassmembers class * {
-    @com.google.vr.cardboard.UsedByNative *;
+-keep class org.chromium.content.app.PrivilegedProcessService {
+  public <init>();
 }
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByNative *;
+
+# SearchView is used in website_preferences_menu.xml and is constructed by
+# Android using reflection.
+-keep class android.support.v7.widget.SearchView {
+  public <init>(...);
 }
--keepclassmembers class * {
-    @com.google.vr.cardboard.annotations.UsedByReflection *;
-}
--keepclassmembers class * {
-    @com.google.vr.dynamite.client.UsedByNative *;
-}
--keepclassmembers class * {
-    @com.google.vr.dynamite.client.UsedByReflection *;
-}
--keepclassmembers class ** {
-    @android.arch.lifecycle.OnLifecycleEvent *;
-}
+
+# This class member is referenced in BottomSheetBottomNav as a temporary
+# measure until the support library contains a solution for disabling shifting
+# mode. TODO(twellington): remove once support library has a fix and is rolled.
 -keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
     boolean mShiftingMode;
 }
+
+# Trichrome builds don't include a native library list in the main APK; it's
+# picked up from the library APK at runtime.
+-dontwarn org.chromium.base.library_loader.NativeLibraries
+
+################################################################################
+# ../../components/background_task_scheduler/android/proguard.flags
+################################################################################
+# Copyright 2018 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.
+
+# Classes are created via reflection, so may not be renamed.
+-keepnames class * implements org.chromium.components.background_task_scheduler.BackgroundTask {
+  public <init>();
+}
+
+################################################################################
+# ../../third_party/gvr-android-sdk/proguard-gvr-chromium.txt
+################################################################################
+-dontwarn com.google.common.logging.nano.Vr$**
+-dontwarn com.google.vr.**
+
+################################################################################
+# ../../third_party/gvr-android-sdk/src/proguard-gvr.txt
+################################################################################
+# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces
+# in release builds easier.
+-keepnames class com.google.vr.ndk.** { *; }
+-keepnames class com.google.vr.sdk.** { *; }
+
+# These are part of the Java <-> native interfaces for GVR.
+-keepclasseswithmembernames,includedescriptorclasses class com.google.vr.** {
+    native <methods>;
+}
+
+# The SDK configuration class member names are useful for debugging client logs.
+-keepclasseswithmembernames,allowoptimization class com.google.common.logging.nano.Vr$VREvent$SdkConfigurationParams** {
+    *;
+}
+
+-keep class com.google.vr.cardboard.UsedByNative
+-keep @com.google.vr.cardboard.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.annotations.UsedByNative
+-keep @com.google.vr.cardboard.annotations.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.annotations.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.annotations.UsedByReflection
+-keep @com.google.vr.cardboard.annotations.UsedByReflection class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.annotations.UsedByReflection *;
+}
+
+-dontwarn com.google.protobuf.nano.NanoEnumValue
+
+################################################################################
+# gen/third_party/android_deps/android_arch_lifecycle_runtime_java/proguard.txt
+################################################################################
+-keepattributes *Annotation*
+
+-keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
+    <fields>;
+}
+
+-keep class * implements android.arch.lifecycle.LifecycleObserver {
+}
+
+-keep class * implements android.arch.lifecycle.GenericLifecycleObserver {
+    <init>(...);
+}
+
+-keepclassmembers class ** {
+    @android.arch.lifecycle.OnLifecycleEvent *;
+}
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_animated_vector_drawable_java/proguard.txt
+################################################################################
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# keep setters in VectorDrawables so that animations can still work.
 -keepclassmembers class android.support.graphics.drawable.VectorDrawableCompat$* {
    void set*(***);
    *** get*();
 }
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_design_java/proguard.txt
+################################################################################
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# CoordinatorLayout resolves the behaviors of its child components with reflection.
+-keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>();
+}
+
+# Make sure we keep annotations for CoordinatorLayout's DefaultBehavior
+-keepattributes *Annotation*
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_recyclerview_v7_java/proguard.txt
+################################################################################
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# When layoutManager xml attribute is used, RecyclerView inflates
+#LayoutManagers' constructors using reflection.
+-keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
+    public <init>(...);
+}
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_support_core_ui_java/proguard.txt
+################################################################################
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make sure we keep annotations for ViewPager's DecorView
+-keepattributes *Annotation*
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_support_media_compat_java/proguard.txt
+################################################################################
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Prevent Parcelable objects from being removed or renamed.
+-keep class android.support.v4.media.** implements android.os.Parcelable {
+    public static final android.os.Parcelable$Creator *;
+}
+
+################################################################################
+# gen/third_party/android_deps/com_android_support_transition_java/proguard.txt
+################################################################################
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# FragmentTransitionSupport is instantiated in support-fragment via reflection.
+-keep public class android.support.transition.FragmentTransitionSupport {
+}
+
+# Keep a field in transition that is used to keep a reference to weakly-referenced object
 -keepclassmembers class android.support.transition.ChangeBounds$* extends android.animation.AnimatorListenerAdapter {
   android.support.transition.ChangeBounds$ViewBounds mViewBounds;
 }
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_base_java/proguard.txt
+################################################################################
+# b/35135904 Ensure that proguard will not strip the mResultGuardian.
 -keepclassmembers class com.google.android.gms.common.api.internal.BasePendingResult {
   com.google.android.gms.common.api.internal.BasePendingResult$ReleasableResultGuardian mResultGuardian;
 }
--keepclassmembers class com.google.android.gms.gcm.GcmListenerService {
-    public void handleIntent(android.content.Intent);
-}
--keepclassmembers class org.chromium.** implements android.os.Parcelable {
-  public static *** CREATOR;
-}
--keepclassmembers class org.chromium.android_webview.AwPdfExporter {
-    android.view.ViewGroup mContainerView;
-}
--keepclassmembers enum * {
-    public static **[] values();
-}
--keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
-    <fields>;
-}
--keepclassmembers enum org.chromium.** {
-    public static **[] values();
-}
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_basement_java/proguard.txt
+################################################################################
+# Proguard flags for consumers of the Google Play services SDK
+# https://developers.google.com/android/guides/setup#add_google_play_services_to_your_project
+
+# Keep SafeParcelable NULL value, needed for reflection by DowngradeableSafeParcel
 -keepclassmembers public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
     public static final *** NULL;
 }
--keepnames @com.google.android.gms.common.annotation.KeepName class *
--keepnames class * implements android.os.Parcelable
+
+# Needed for Parcelable/SafeParcelable classes & their creators to not get renamed, as they are
+# found via reflection.
+-keep class com.google.android.gms.common.internal.ReflectedParcelable
 -keepnames class * implements com.google.android.gms.common.internal.ReflectedParcelable
--keepnames class * implements org.chromium.components.background_task_scheduler.BackgroundTask {
-  public <init>();
+-keepclassmembers class * implements android.os.Parcelable {
+  public static final *** CREATOR;
 }
--keepnames class com.google.vr.ndk.** { *; }
--keepnames class com.google.vr.sdk.** { *; }
--keepnames class org.chromium.** implements android.os.Parcelable
--keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * {
-  *;
+
+# Keep the classes/members we need for client functionality.
+-keep @interface android.support.annotation.Keep
+-keep @android.support.annotation.Keep class *
+-keepclasseswithmembers class * {
+  @android.support.annotation.Keep <fields>;
 }
+-keepclasseswithmembers class * {
+  @android.support.annotation.Keep <methods>;
+}
+
+# Keep the names of classes/members we need for client functionality.
+-keep @interface com.google.android.gms.common.annotation.KeepName
+-keepnames @com.google.android.gms.common.annotation.KeepName class *
+-keepclassmembernames class * {
+  @com.google.android.gms.common.annotation.KeepName *;
+}
+
+# Keep Dynamite API entry points
+-keep @interface com.google.android.gms.common.util.DynamiteApi
+-keep @com.google.android.gms.common.util.DynamiteApi public class * {
+  public <fields>;
+  public <methods>;
+}
+
+# Needed when building against pre-Marshmallow SDK.
+-dontwarn android.security.NetworkSecurityPolicy
+
+# Needed when building against Marshmallow SDK.
+-dontwarn android.app.Notification
+
+# Protobuf has references not on the Android boot classpath
+-dontwarn sun.misc.Unsafe
+-dontwarn libcore.io.Memory
+
+# Internal Google annotations for generating Proguard keep rules.
+-dontwarn com.google.android.apps.common.proguard.UsedBy*
+
+# Annotations referenced by the SDK but whose definitions are contained in
+# non-required dependencies.
+-dontwarn javax.annotation.**
+-dontwarn org.checkerframework.**
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_cast_framework_java/proguard.txt
+################################################################################
+-keep public class * implements com.google.android.gms.cast.framework.OptionsProvider
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_clearcut_java/proguard.txt
+################################################################################
+# We keep all fields for every generated proto file as the runtime uses
+# reflection over them that ProGuard cannot detect. Without this keep
+# rule, fields may be removed that would cause runtime failures.
+-keepclassmembers class * extends com.google.android.gms.internal.clearcut.zzcg {
+  <fields>;
+}
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_fido_java/proguard.txt
+################################################################################
+# Methods enable and disable in this class are complained as unresolved
+# references, but they are system APIs and are not used by Fido client apps.
+-dontwarn android.nfc.NfcAdapter
+
+################################################################################
+# gen/third_party/android_deps/com_google_android_gms_play_services_gcm_java/proguard.txt
+################################################################################
+# Ensure that proguard will not strip the handleIntent method.
+-keepclassmembers class com.google.android.gms.gcm.GcmListenerService {
+    public void handleIntent(android.content.Intent);
+}
+
+################################################################################
+# gen/third_party/android_deps/com_google_ar_core_java/proguard.txt
+################################################################################
+# Keep ARCore public-facing classes
 -keepparameternames
--optimizationpasses 3
--optimizations !class/merging/horizontal
--renamesourcefileattribute PG
--repackageclasses ''
\ No newline at end of file
+
+# These are part of the Java <-> native interfaces for ARCore.
+-keepclasseswithmembernames,includedescriptorclasses class com.google.ar.** {
+    native <methods>;
+}
+
+-keep public class com.google.ar.core.** {*;}
+# If you need to build a library on top of arcore_client, and use this library for your project
+# Please un-comment this line below.
+# -keepattributes *Annotation*
+
+-keep class com.google.ar.core.annotations.UsedByNative
+-keep @com.google.ar.core.annotations.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.ar.core.annotations.UsedByNative *;
+}
+
+-keep class com.google.ar.core.annotations.UsedByReflection
+-keep @com.google.ar.core.annotations.UsedByReflection class *
+-keepclassmembers class * {
+    @com.google.ar.core.annotations.UsedByReflection *;
+}
+# Keep Dynamite classes
+
+# .aidl file will be proguarded, we should keep all Aidls.
+-keep class com.google.vr.dynamite.client.IObjectWrapper { *; }
+-keep class com.google.vr.dynamite.client.ILoadedInstanceCreator { *; }
+-keep class com.google.vr.dynamite.client.INativeLibraryLoader { *; }
+
+# Keep annotation files and the file got annotated.
+-keep class com.google.vr.dynamite.client.UsedByNative
+-keep @com.google.vr.dynamite.client.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.dynamite.client.UsedByNative *;
+}
+
+-keep class com.google.vr.dynamite.client.UsedByReflection
+-keep @com.google.vr.dynamite.client.UsedByReflection class *
+-keepclassmembers class * {
+    @com.google.vr.dynamite.client.UsedByReflection *;
+}
+
diff --git a/chrome/android/java/res/drawable/ic_book_round.xml b/chrome/android/java/res/drawable/ic_book_round.xml
new file mode 100644
index 0000000..a80ae5d2
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_book_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M24,8L12,8C10.9,8 10,8.9 10,10L10,26C10,27.1 10.9,28 12,28L24,28C25.1,28 26,27.1 26,26L26,10C26,8.9 25.1,8 24,8ZM12,10L17,10L17,18L14.5,16.5L12,18L12,10Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_equals_sign_round.xml b/chrome/android/java/res/drawable/ic_equals_sign_round.xml
new file mode 100644
index 0000000..b94618d2
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_equals_sign_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M10,14L26,14L26,17L10,17L10,14ZM10,19L26,19L26,22L10,22L10,19Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_event_round.xml b/chrome/android/java/res/drawable/ic_event_round.xml
new file mode 100644
index 0000000..87a6940
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_event_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M23,18L18,18L18,23L23,23L23,18ZM22,7L22,9L14,9L14,7L12,7L12,9L11,9C9.89,9 9.01,9.9 9.01,11L9,25C9,26.1 9.89,27 11,27L25,27C26.1,27 27,26.1 27,25L27,11C27,9.9 26.1,9 25,9L24,9L24,7L22,7ZM25,25L11,25L11,14L25,14L25,25Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_google_round.xml b/chrome/android/java/res/drawable/ic_google_round.xml
new file mode 100644
index 0000000..bd95728
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_google_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z"/>
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M18,19.9L18,16.18L27.36,16.18C27.5,16.81 27.61,17.4 27.61,18.23C27.61,23.94 23.78,28 18.01,28C12.48,28 8,23.52 8,18C8,12.48 12.48,8 18,8C20.7,8 22.96,8.99 24.69,10.61L21.85,13.37C21.13,12.69 19.88,11.88 18,11.88C14.69,11.88 11.99,14.63 11.99,18C11.99,21.37 14.69,24.12 18,24.12C21.83,24.12 23.24,21.47 23.5,19.9L18,19.9Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_loop_round.xml b/chrome/android/java/res/drawable/ic_loop_round.xml
new file mode 100644
index 0000000..3a54b769
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_loop_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M18,12L18,15L22,11L18,7L18,10C13.58,10 10,13.58 10,18C10,19.57 10.46,21.03 11.24,22.26L12.7,20.8C12.25,19.97 12,19.01 12,18C12,14.69 14.69,12 18,12ZM24.76,13.74L23.3,15.2C23.74,16.04 24,16.99 24,18C24,21.31 21.31,24 18,24L18,21L14,25L18,29L18,26C22.42,26 26,22.42 26,18C26,16.43 25.54,14.97 24.76,13.74Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_swap_vert_round.xml b/chrome/android/java/res/drawable/ic_swap_vert_round.xml
new file mode 100644
index 0000000..532e868
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_swap_vert_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M22,23.01L22,16L20,16L20,23.01L17,23.01L21,27L25,23.01L22,23.01ZM15,9L11,12.99L14,12.99L14,20L16,20L16,12.99L19,12.99L15,9Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_wb_sunny_round.xml b/chrome/android/java/res/drawable/ic_wb_sunny_round.xml
new file mode 100644
index 0000000..1c4a74b
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_wb_sunny_round.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="36"
+    android:viewportWidth="36" android:width="24dp">
+    <path android:fillColor="@color/default_icon_color_blue" android:fillType="evenOdd"
+        android:pathData="M18,0L18,0C27.9411,-0 36,8.0589 36,18L36,18C36,27.9411 27.9411,36 18,36L18,36C8.0589,36 0,27.9411 0,18L0,18C-0,8.0589 8.0589,0 18,0Z" />
+    <path android:fillColor="#FFFFFF" android:fillType="nonZero"
+        android:pathData="M12.76,11.29L10.96,9.5L9.55,10.91L11.34,12.7L12.76,11.29ZM10,16.95L7,16.95L7,18.95L10,18.95L10,16.95ZM19,7L17,7L17,9.95L19,9.95L19,7ZM26.45,10.91L25.04,9.5L23.25,11.29L24.66,12.7L26.45,10.91ZM23.24,24.61L25.03,26.41L26.44,25L24.64,23.21L23.24,24.61ZM26,16.95L26,18.95L29,18.95L29,16.95L26,16.95ZM18,11.95C14.69,11.95 12,14.64 12,17.95C12,21.26 14.69,23.95 18,23.95C21.31,23.95 24,21.26 24,17.95C24,14.64 21.31,11.95 18,11.95ZM17,28.9L19,28.9L19,25.95L17,25.95L17,28.9ZM9.55,24.99L10.96,26.4L12.75,24.6L11.34,23.19L9.55,24.99Z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/logo_partly_cloudy_light.xml b/chrome/android/java/res/drawable/logo_partly_cloudy_light.xml
new file mode 100644
index 0000000..1238f7a
--- /dev/null
+++ b/chrome/android/java/res/drawable/logo_partly_cloudy_light.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:height="24dp" android:viewportHeight="48"
+    android:viewportWidth="48" android:width="24dp">
+    <path android:fillColor="#FDD835" android:pathData="M24,24m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"/>
+    <path android:fillAlpha="0.1" android:fillColor="#FFFFFF" android:pathData="M24,4.25c11,0 19.93,8.89 20,19.88c0,-0.04 0,-0.08 0,-0.12c0,-11.05 -8.95,-20 -20,-20S4,12.95 4,24c0,0.04 0,0.08 0,0.12C4.07,13.14 13,4.25 24,4.25z"/>
+    <path android:fillAlpha="0.1" android:fillColor="#BF360C" android:pathData="M44,23.88C43.93,34.86 35,43.75 24,43.75S4.07,34.86 4,23.88c0,0.04 0,0.08 0,0.12c0,11.05 8.95,20 20,20s20,-8.95 20,-20C44,23.96 44,23.92 44,23.88z"/>
+    <group>
+        <clip-path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z M 0,0"/>
+        <path android:pathData="M44.25,36.69l-10.59,-10.349998l-28.13,10.150002l0.0,7.5099983l38.72,0.0z">
+            <aapt:attr name="android:fillColor">
+                <gradient android:endX="27.1957" android:endY="44.8146"
+                    android:startX="21.0923" android:startY="32.9892" android:type="linear">
+                    <item android:color="#19BF360C" android:offset="0"/>
+                    <item android:color="#05BF360C" android:offset="1"/>
+                </gradient>
+            </aapt:attr>
+        </path>
+    </group>
+    <group>
+        <clip-path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z M 0,0"/>
+        <path android:fillAlpha="0.1" android:fillColor="#212121" android:pathData="M28,39.75H14.01     c-6.59,0,-11.93,-5.31,-12,-11.88c0,0.04,0,0.08,0,0.12c0,6.63,5.37,12,12,12H28c4.42,0,8,-3.58,8,-8c0,-0.04,0,-0.08,0,-0.12     C35.93,36.24,32.38,39.75,28,39.75z"/>
+    </group>
+    <group>
+        <clip-path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z M 0,0"/>
+        <path android:fillColor="#EEEEEE" android:pathData="M28,24h-2.67c-1.65,-4.66,-6.09,-8,-11.31,-8c-6.63,0,-12,5.37,-12,12s5.37,12,12,12H28       c4.42,0,8,-3.58,8,-8C36,27.58,32.42,24,28,24z"/>
+    </group>
+    <group>
+        <clip-path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z M 0,0"/>
+        <path android:fillAlpha="0.1" android:fillColor="#BF360C" android:pathData="M28,40H14.01     c-6.59,0,-11.93,-5.31,-12,-11.88c0,0.04,0,0.08,0,0.12c0,6.63,5.37,12,12,12H28c4.42,0,8,-3.58,8,-8c0,-0.04,0,-0.08,0,-0.12     C35.93,36.49,32.38,40,28,40z"/>
+    </group>
+    <group>
+        <clip-path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z M 0,0"/>
+        <path android:fillAlpha="0.2" android:fillColor="#FFFFFF" android:pathData="M14.01,16.25c5.22,0,9.67,3.34,11.31,8H28c4.38,0,7.93,3.51,8,7.88      c0,-0.04,0,-0.08,0,-0.12c0,-4.42,-3.58,-8,-8,-8h-2.67c-1.65,-4.66,-6.09,-8,-11.31,-8c-6.63,0,-12,5.37,-12,12c0,0.04,0,0.08,0,0.12      C2.08,21.56,7.43,16.25,14.01,16.25z"/>
+    </group>
+    <path android:pathData="M24,4C13.95,4 5.63,11.42 4.22,21.07c-1.39,1.96 -2.2,4.35 -2.2,6.93c0,5.84 4.18,10.71 9.71,11.78C15.11,42.42 19.37,44 24,44c11.05,0 20,-8.95 20,-20C44,12.95 35.05,4 24,4z">
+        <aapt:attr name="android:fillColor">
+            <gradient android:centerX="9.8293" android:centerY="9.8583"
+                android:gradientRadius="40.0515" android:type="radial">
+                <item android:color="#19FFFFFF" android:offset="0"/>
+                <item android:color="#00FFFFFF" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+</vector>
diff --git a/chrome/android/java/res/drawable/logo_translate_round.xml b/chrome/android/java/res/drawable/logo_translate_round.xml
new file mode 100644
index 0000000..438a031
--- /dev/null
+++ b/chrome/android/java/res/drawable/logo_translate_round.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:height="24dp" android:viewportHeight="48"
+    android:viewportWidth="48" android:width="24dp"
+    tools:targetApi="21">
+
+    <path android:fillColor="#eee" android:pathData="M33.92,43.63C41.09,40.01 46,32.58 46,24c0,-12.15 -9.85,-22 -22,-22 -1.31,0 -2.59,0.12 -3.84,0.34l-0.9,1.6L32.27,43.1l1.65,0.53z"/>
+    <path android:fillColor="#607d8b" android:pathData="M24.71,19v-2h6.31v-2H33v2h6v2h-1.58c-0.57,2.33 -1.84,4.78 -3.46,6.64l4.89,4.85 -1.32,1.32 -4.85,-4.85 -4.85,4.85 -1.32,-1.32 4.89,-4.85c-1.16,-1.34 -2.17,-3.07 -2.85,-4.64h2.08c0.43,0.86 1.1,2.14 2.05,3.25 2.2,-2.57 2.81,-5.25 2.81,-5.25H24.71z"/>
+    <path android:fillAlpha=".2" android:fillColor="#fff" android:pathData="M24,2.23c12.11,0 21.94,9.79 22,21.89v-0.11c0,-12.15 -9.85,-22 -22,-22C11.85,2 2,11.85 2,24v0.11c0.06,-12.09 9.89,-21.88 22,-21.88z"/>
+    <path android:fillAlpha=".15" android:fillColor="#263238" android:pathData="M24,45.77c12.11,0 21.94,-9.79 22,-21.89v0.11c0,12.15 -9.85,22 -22,22C11.85,46 2,36.15 2,24v-0.11c0.06,12.09 9.89,21.88 22,21.88z"/>
+    <path android:fillAlpha=".1" android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0">
+        <aapt:attr name="android:fillColor">
+            <gradient android:centerX="8.649595"
+                android:centerY="8.650356"
+                android:gradientRadius="43.711006" android:type="radial">
+                <item android:color="#FFFFFFFF" android:offset="0"/>
+                <item android:color="#00FFFFFF" android:offset="1"/>
+            </gradient>
+        </aapt:attr>
+    </path>
+    <group>
+        <clip-path android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0 M 0,0"/>
+        <path android:pathData="M20.03 2.25l32.38 32.38V56.9H22.05">
+            <aapt:attr name="android:fillColor">
+                <gradient android:endX="52.411" android:endY="29.579"
+                    android:startX="20.03" android:startY="29.579" android:type="linear">
+                    <item android:color="#19212121" android:offset="0"/>
+                    <item android:color="#05212121" android:offset="1"/>
+                </gradient>
+            </aapt:attr>
+        </path>
+    </group>
+    <group>
+        <clip-path android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0 M 0,0"/>
+        <path android:fillColor="#4285f4" android:pathData="M2 24c0 12.15 9.85 22 22 22 3.57 0 6.94,-.85 9.93,-2.36L20.16 2.33C9.84 4.15 2 13.16 2 24z"/>
+        <path android:fillAlpha="0.1" android:fillColor="#fff"
+            android:pathData="M18.22,-2.68L35.2 48.24l.31.13L18.39,-2.96" android:strokeAlpha="0.1"/>
+        <path android:fillColor="#eee" android:pathData="M16.02 23v2h3.97c-.16 1.43,-1.2 3.42,-3.97 3.42,-2.39 0,-4.34,-1.98,-4.34,-4.42s1.95,-4.42 4.34,-4.42c1.36 0 2.27.58 2.79 1.08l1.9,-1.83c-1.22,-1.14,-2.8,-1.83,-4.69,-1.83,-3.87 0,-7 3.13,-7 7s3.13 7 7 7c4.04 0 6.72,-2.84 6.72,-6.84 0,-.46,-.05,-.81,-.11,-1.16h-6.61z"/>
+    </group>
+</vector>
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index 5671ee14b..e7ee576 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -132,6 +132,4 @@
     <item type="id" name="highlight_state" />
     <item type="id" name="item_animator" />
     <item type="id" name="highlight_color" />
-    <item type="id" name="view_model" />
-    <item type="id" name="view_type" />
 </resources>
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
index d3aaaec8..67e65c0 100644
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -34,6 +34,11 @@
         android:key="can_make_payment"
         android:title="@string/can_make_payment_title"
         android:summary="@string/settings_can_make_payment_toggle_label" />
+    <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+        android:key="usage_stats_reporting"
+        android:title="@string/usage_stats_setting_title"
+        android:summary="@string/usage_stats_consent_prompt"
+        android:persistent="false" />
     <Preference
         android:key="usage_and_crash_reports"
         android:title="@string/usage_and_crash_reports_title_legacy"
diff --git a/chrome/android/java/res_download/layout/downloads_empty_view.xml b/chrome/android/java/res_download/layout/downloads_empty_view.xml
index bfbb309..257d902d 100644
--- a/chrome/android/java/res_download/layout/downloads_empty_view.xml
+++ b/chrome/android/java/res_download/layout/downloads_empty_view.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Copyright 2018 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. -->
+    Use of this source code is governed by a BSD-style license that can be
+    found in the LICENSE file. -->
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -12,8 +12,11 @@
         android:id="@+id/empty"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginStart="36dp"
+        android:layout_marginEnd="36dp"
         android:layout_gravity="center"
-        android:drawablePadding="3dp"
+        android:gravity="center"
+        android:drawablePadding="20dp"
         android:textAppearance="@style/TextAppearance.BlackDisabledText1"/>
 
     <org.chromium.chrome.browser.widget.LoadingView
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 6d0bc9e..d3225d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -49,6 +49,9 @@
         // when it is initialized. Return whether the native FeatureList has been initialized,
         // so the return value can be tested, or asserted for a more actionable stack trace
         // on failure.
+        //
+        // The FeatureList is however guaranteed to be initialized by the time
+        // AsyncInitializationActivity#finishNativeInitialization is called.
         return nativeIsInitialized();
     }
 
@@ -169,6 +172,7 @@
     public static final String CCT_MODULE = "CCTModule";
     public static final String CCT_MODULE_CACHE = "CCTModuleCache";
     public static final String CCT_MODULE_CUSTOM_HEADER = "CCTModuleCustomHeader";
+    public static final String CCT_MODULE_CUSTOM_REQUEST_HEADER = "CCTModuleCustomRequestHeader";
     public static final String CCT_MODULE_POST_MESSAGE = "CCTModulePostMessage";
     public static final String CCT_EXTERNAL_LINK_HANDLING = "CCTExternalLinkHandling";
     public static final String CCT_POST_MESSAGE_API = "CCTPostMessageAPI";
@@ -263,6 +267,8 @@
     public static final String PAY_WITH_GOOGLE_V1 = "PayWithGoogleV1";
     public static final String PASSWORDS_KEYBOARD_ACCESSORY = "PasswordsKeyboardAccessory";
     public static final String PERMISSION_DELEGATION = "PermissionDelegation";
+    public static final String PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES =
+            "PredictivePrefetchingAllowedOnAllConnectionTypes";
     public static final String PROGRESS_BAR_THROTTLE = "ProgressBarThrottle";
     public static final String PWA_PERSISTENT_NOTIFICATION = "PwaPersistentNotification";
     public static final String READER_MODE_IN_CCT = "ReaderModeInCCT";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 60c080d0..cc969c95e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -37,6 +37,7 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
+import org.chromium.base.BuildInfo;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -136,6 +137,7 @@
 import org.chromium.chrome.browser.tasks.TasksUma;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonInProductHelpController;
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer;
+import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
@@ -631,6 +633,10 @@
                 FeedProcessScopeFactory.getFeedAppLifecycle();
             }
 
+            if (BuildInfo.isAtLeastQ()) {
+                UsageStatsService.getInstance().createPageViewObserver(mTabModelSelectorImpl, this);
+            }
+
             super.finishNativeInitialization();
         } finally {
             TraceEvent.end("ChromeTabbedActivity.finishNativeInitialization");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationAdapter.java
index 935c3a7..93f40aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationAdapter.java
@@ -5,9 +5,6 @@
 package org.chromium.chrome.browser.autofill;
 
 import android.content.Context;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -17,7 +14,6 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.ui.text.NoUnderlineClickableSpan;
 
 import java.util.Arrays;
 import java.util.List;
@@ -30,13 +26,10 @@
  */
 public class PasswordGenerationAdapter extends BaseAdapter {
     private final Context mContext;
-    private final Delegate mDelegate;
     private final List<Integer> mViewTypes;
     private final String mPassword;
     private final String mSuggestionTitle;
     private final String mExplanationText;
-    private final int mExplanationTextLinkRangeStart;
-    private final int mExplanationTextLinkRangeEnd;
     private final int mSuggestionMeasuredWidth;
 
     /**
@@ -55,44 +48,24 @@
     private static final int VIEW_TYPE_COUNT = 2;
 
     /**
-     * Handler for clicks on the "saved passwords" link.
-     */
-    public interface Delegate {
-        /**
-         * Called when the user clicks the "saved passwords" link.
-         */
-        public void onSavedPasswordsLinkClicked();
-    }
-
-    /**
      * Builds the adapter to display views using data from delegate.
      * @param context Android context.
-     * @param delegate The handler for clicking on the "saved passwords" link.
      * @param passwordDisplayed Whether the auto-generated password should be suggested.
      * @param password The auto-generated password to suggest.
      * @param suggestionTitle The translated title of the suggestion part of the UI.
      * @param explanationText The translated text for the explanation part of the UI.
-     * @param explanationTextLinkRangeStart The start of the range in the explanation text that
-     * should be a link to the saved passwords.
-     * @param explanationTextLinkRangeEnd The end of the range in the explanation text that should
-     * be a link to the saved passwords.
      * @param anchorWidthInDp The width of the anchor to which the popup is attached. Used to size
      * the explanation view.
      */
-    public PasswordGenerationAdapter(Context context, Delegate delegate, boolean passwordDisplayed,
-            String password, String suggestionTitle, String explanationText,
-            int explanationTextLinkRangeStart, int explanationTextLinkRangeEnd,
-            float anchorWidthInDp) {
+    public PasswordGenerationAdapter(Context context, boolean passwordDisplayed, String password,
+            String suggestionTitle, String explanationText, float anchorWidthInDp) {
         super();
         mContext = context;
-        mDelegate = delegate;
         mViewTypes = passwordDisplayed ? Arrays.asList(SUGGESTION, EXPLANATION)
                 : Arrays.asList(EXPLANATION);
         mPassword = password;
         mSuggestionTitle = suggestionTitle;
         mExplanationText = explanationText;
-        mExplanationTextLinkRangeStart = explanationTextLinkRangeStart;
-        mExplanationTextLinkRangeEnd = explanationTextLinkRangeEnd;
 
         int horizontalMarginInPx = Math.round(mContext.getResources().getDimension(
                 R.dimen.password_generation_horizontal_margin));
@@ -140,13 +113,7 @@
                 view = inflater.inflate(R.layout.password_generation_popup_explanation, null);
                 TextView explanation = (TextView) view
                         .findViewById(R.id.password_generation_explanation);
-                SpannableString explanationSpan = new SpannableString(mExplanationText);
-                explanationSpan.setSpan(new NoUnderlineClickableSpan(
-                                                (view1) -> mDelegate.onSavedPasswordsLinkClicked()),
-                        mExplanationTextLinkRangeStart, mExplanationTextLinkRangeEnd,
-                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                explanation.setText(explanationSpan);
-                explanation.setMovementMethod(LinkMovementMethod.getInstance());
+                explanation.setText(mExplanationText);
                 explanation.setLayoutParams(new LayoutParams(mSuggestionMeasuredWidth,
                         LayoutParams.WRAP_CONTENT));
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
index be516ae..71826887 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
@@ -21,8 +21,7 @@
  */
 @JNINamespace("autofill")
 public class PasswordGenerationPopupBridge
-        implements AdapterView.OnItemClickListener, PopupWindow.OnDismissListener,
-                   PasswordGenerationAdapter.Delegate {
+        implements AdapterView.OnItemClickListener, PopupWindow.OnDismissListener {
     private final long mNativePasswordGenerationPopupViewAndroid;
     private final Context mContext;
     private final DropdownPopupWindow mPopup;
@@ -100,21 +99,15 @@
      * @param password The auto-generated password to suggest.
      * @param suggestionTitle The translated title of the suggestion part of the popup.
      * @param explanationText The translated text that explains the popup.
-     * @param explanationTextLinkRangeStart The start of the range in the explanation text that
-     * should be a link to the saved passwords.
-     * @param explanationTextLinkRangeEnd The end of the range in the explanation text that should
-     * be a link to the saved passwords.
      */
     @CalledByNative
     private void show(boolean isRtl, boolean passwordDisplayed, String password,
-            String suggestionTitle, String explanationText, int explanationTextLinkRangeStart,
-            int explanationTextLinkRangeEnd) {
+            String suggestionTitle, String explanationText) {
         if (mPopup != null) {
             float anchorWidth = mAnchorView.getLayoutParams().width;
             assert anchorWidth > 0;
-            PasswordGenerationAdapter adapter = new PasswordGenerationAdapter(mContext, this,
-                    passwordDisplayed, password, suggestionTitle, explanationText,
-                    explanationTextLinkRangeStart, explanationTextLinkRangeEnd, anchorWidth);
+            PasswordGenerationAdapter adapter = new PasswordGenerationAdapter(mContext,
+                    passwordDisplayed, password, suggestionTitle, explanationText, anchorWidth);
             mPopup.setAdapter(adapter);
             mPopup.setRtl(isRtl);
             mPopup.show();
@@ -122,17 +115,6 @@
     }
 
     /**
-     * Called from adapter when the "saved passwords" link is clicked.
-     */
-    @Override
-    public void onSavedPasswordsLinkClicked() {
-        nativeSavedPasswordsLinkClicked(mNativePasswordGenerationPopupViewAndroid);
-    }
-
-    private native void nativeSavedPasswordsLinkClicked(
-            long nativePasswordGenerationPopupViewAndroid);
-
-    /**
      * Hides the password generation popup.
      */
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
index f5abe230..739fa2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java
@@ -13,8 +13,8 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModelChangeProcessor;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
index 979f58c..e5a4c1aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java
@@ -19,10 +19,10 @@
 import android.support.v7.widget.RecyclerView;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
index cb99d91..805423a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java
@@ -7,7 +7,7 @@
 import android.support.v4.view.ViewPager;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabModel.java
index f100fd4..03c456b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabModel.java
@@ -8,7 +8,7 @@
 import android.support.v7.widget.RecyclerView;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
index 662edcd8..3088fcca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java
@@ -15,7 +15,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 
 /**
  * This stateless class provides methods to bind a {@link ListModel<AccessorySheetDataPiece>}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
index e00759a..50e65b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
@@ -19,14 +19,14 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModernViewBinder.ModernBarItemViewHolder;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryViewBinder.BarItemViewHolder;
-import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 
 /**
  * Creates and owns all elements which are part of the keyboard accessory component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
index 6be49a9..2bed6ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryCoordinator.VisibilityDelegate;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
-import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
index 9b312d9..759bc02b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
@@ -16,8 +16,8 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
index b3666e6b..8a2eab5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java
@@ -9,7 +9,7 @@
 import android.support.v7.widget.RecyclerView;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
index 59d4db4..4a3b6b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutCoordinator.java
@@ -12,10 +12,10 @@
 import android.support.v4.view.ViewPager;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * This component reflects the state of selected tabs in the keyboard accessory. It can be assigned
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
index 6ed6194..a8ad3cc5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutMediator.java
@@ -13,7 +13,7 @@
 import android.support.v4.view.ViewPager;
 
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryTabLayoutCoordinator.AccessoryTabObserver;
-import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
index 33de544..8b611fdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutProperties.java
@@ -6,7 +6,7 @@
 
 import android.support.design.widget.TabLayout;
 
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
index 02b1c67..dd4235f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewBinder.java
@@ -11,8 +11,8 @@
 import android.support.design.widget.TabLayout;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
index 696b0c5..0b9b7f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java
@@ -17,9 +17,9 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 
 /**
  * This component is a tab that can be added to the {@link ManualFillingCoordinator} which shows it
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
index 005ca05..9e66cef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java
@@ -9,8 +9,8 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.FooterCommand;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java
index 6e66c88b..3850d07e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabViewBinder.ElementViewHolder;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.widget.ChipView;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
index 38da317..615afec55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabViewBinder.ElementViewHolder;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.FooterCommand;
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 
 /**
  * This stateless class provides methods to bind the items in a {@link ListModel <Item>}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index 074d2a4..2bd6a520 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -12,7 +12,9 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.IntentUtils;
 
 import java.util.HashMap;
@@ -57,8 +59,22 @@
     /** Starts Autofill Assistant on the given {@code activity}. */
     public static void start(ChromeActivity activity) {
         startWithCallback(activity, (canStart) -> {
-            if (canStart) {
-                initiateAutofillAssistant(activity);
+            if (!canStart) return;
+
+            Tab tab = activity.getActivityTab();
+            if (tab != null) {
+                initiateAutofillAssistant(activity, tab);
+            } else {
+                // The tab is not yet available. We need to register as listener and wait for it.
+                activity.getActivityTabProvider().addObserverAndTrigger(
+                        new ActivityTabProvider.HintlessActivityTabObserver() {
+                            @Override
+                            public void onActivityTabChanged(Tab tab) {
+                                if (tab == null) return;
+                                activity.getActivityTabProvider().removeObserver(this);
+                                initiateAutofillAssistant(activity, tab);
+                            }
+                        });
             }
         });
     }
@@ -84,13 +100,13 @@
     /**
      * Instantiates all essential Autofill Assistant components and starts it.
      */
-    private static void initiateAutofillAssistant(ChromeActivity activity) {
+    private static void initiateAutofillAssistant(ChromeActivity activity, Tab tab) {
         Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
         parameters.remove(PARAMETER_ENABLED);
         String initialUrl = activity.getInitialIntent().getDataString();
 
         AutofillAssistantClient client =
-                AutofillAssistantClient.fromWebContents(activity.getActivityTab().getWebContents());
+                AutofillAssistantClient.fromWebContents(tab.getWebContents());
         client.start(initialUrl, parameters, activity.getInitialIntent().getExtras());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
index cf06271..3fdedd6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
@@ -29,6 +29,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Checks whether the currently seen web page belongs to a verified origin and updates the
  * {@link TrustedWebActivityModel} accordingly.
@@ -38,10 +40,11 @@
     /** The Digital Asset Link relationship used for Trusted Web Activities. */
     private final static int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS;
 
-    private final ClientAppDataRecorder mClientAppDataRecorder;
+    private final Lazy<ClientAppDataRecorder> mClientAppDataRecorder;
     private final CustomTabsConnection mCustomTabsConnection;
     private final CustomTabIntentDataProvider mIntentDataProvider;
     private final ActivityTabProvider mActivityTabProvider;
+    private final TabObserverRegistrar mTabObserverRegistrar;
     private final String mClientPackageName;
 
     @Nullable private VerificationState mState;
@@ -76,6 +79,10 @@
                 boolean isFragmentNavigation, Integer pageTransition, int errorCode,
                 int httpStatusCode) {
             if (!hasCommitted || !isInMainFrame) return;
+            if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY)) {
+                assert false : "Shouldn't observe navigation when TWAs are disabled";
+                return;
+            }
 
             // This doesn't perform a network request or attempt new verification - it checks to
             // see if a verification already exists for the given inputs.
@@ -87,7 +94,7 @@
     };
 
     @Inject
-    public TrustedWebActivityVerifier(ClientAppDataRecorder clientAppDataRecorder,
+    public TrustedWebActivityVerifier(Lazy<ClientAppDataRecorder> clientAppDataRecorder,
             CustomTabIntentDataProvider intentDataProvider,
             CustomTabsConnection customTabsConnection,
             ActivityLifecycleDispatcher lifecycleDispatcher,
@@ -97,6 +104,7 @@
         mCustomTabsConnection = customTabsConnection;
         mIntentDataProvider = intentDataProvider;
         mActivityTabProvider = activityTabProvider;
+        mTabObserverRegistrar =  tabObserverRegistrar;
         mClientPackageName = customTabsConnection.getClientPackageNameForSession(
                 intentDataProvider.getSession());
         assert mClientPackageName != null;
@@ -152,12 +160,14 @@
      */
     private void attemptVerificationForInitialUrl(String url, Tab tab) {
         Origin origin = new Origin(url);
-
-        mState = new VerificationState(origin, VERIFICATION_PENDING);
-        for (Runnable observer : mObservers) {
-            observer.run();
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY)) {
+            mTabObserverRegistrar.unregisterTabObserver(mVerifyOnPageLoadObserver);
+            updateState(origin, VERIFICATION_FAILURE);
+            return;
         }
 
+        updateState(origin, VERIFICATION_PENDING);
+
         new OriginVerifier((packageName2, origin2, verified, online) -> {
             if (!origin.equals(new Origin(tab.getUrl()))) return;
 
@@ -169,8 +179,11 @@
         if (verified) {
             registerClientAppData(origin);
         }
-        mState = new VerificationState(origin,
-                verified ? VERIFICATION_SUCCESS : VERIFICATION_FAILURE);
+        updateState(origin, verified ? VERIFICATION_SUCCESS : VERIFICATION_FAILURE);
+    }
+
+    private void updateState(Origin origin, @VerificationStatus int status) {
+        mState = new VerificationState(origin, status);
         for (Runnable observer : mObservers) {
             observer.run();
         }
@@ -196,6 +209,6 @@
      * for that origin with that app.
      */
     private void registerClientAppData(Origin origin) {
-        mClientAppDataRecorder.register(mClientPackageName, origin);
+        mClientAppDataRecorder.get().register(mClientPackageName, origin);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index d3c722d..d27ed854 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -334,7 +334,7 @@
 
                     @Override
                     public void didStartNavigation(String url, boolean isInMainFrame,
-                            boolean isSameDocument, boolean isErrorPage) {
+                            boolean isSameDocument, long navigationHandleProxy) {
                         if (isInMainFrame && !isSameDocument) {
                             mContentDelegate.onMainFrameLoadStarted(
                                     url, !TextUtils.equals(url, mLoadedUrl));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
index 4638ee48..873560b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
@@ -5,9 +5,9 @@
 package org.chromium.chrome.browser.contextual_suggestions;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.ntp.cards.InnerNode;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.util.List;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
index 2c1b9c09..aacb9336 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
@@ -6,7 +6,6 @@
 
 import android.view.ViewGroup;
 
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.ItemViewType;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
@@ -17,6 +16,7 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /**
  * An adapter that contains the view binder for the content component.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java
index 2462f764..d3f2776 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java
@@ -11,7 +11,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * Coordinator for the toolbar sub-component. Responsible for communication with the parent
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java
index 7da73b67..243d1c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.contextual_suggestions;
 
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.PropertyKey;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * A view binder for use with a {@link ToolbarView}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 3520a4f4b..c0dffe4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -146,6 +146,11 @@
     public static final String EXTRA_MODULE_CLASS_NAME =
             "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_CLASS_NAME";
 
+    /** The custom header's value sent for module managed URLs */
+    @VisibleForTesting
+    public static final String EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE =
+            "org.chromium.chrome.browser.customtabs.EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE";
+
     /** Extra that indicates whether to hide the CCT header on module managed URLs. */
     @VisibleForTesting
     public static final String EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS =
@@ -176,6 +181,8 @@
     private final ComponentName mModuleComponentName;
     @Nullable
     private final Pattern mModuleManagedUrlsPattern;
+    @Nullable
+    private final String mModuleManagedUrlsHeaderValue;
     private final boolean mHideCctHeaderOnModuleManagedUrls;
     private final boolean mIsIncognito;
     @Nullable
@@ -309,11 +316,14 @@
             mModuleManagedUrlsPattern = (moduleManagedUrlsRegex != null)
                     ? Pattern.compile(moduleManagedUrlsRegex)
                     : null;
+            mModuleManagedUrlsHeaderValue =
+                    IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE);
             mHideCctHeaderOnModuleManagedUrls = IntentUtils.safeGetBooleanExtra(
                     intent, EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS, false);
         } else {
             mModuleComponentName = null;
             mModuleManagedUrlsPattern = null;
+            mModuleManagedUrlsHeaderValue = null;
             mHideCctHeaderOnModuleManagedUrls = false;
         }
     }
@@ -769,15 +779,8 @@
 
     /**
      * @return Whether the Custom Tab should attempt to display a Trusted Web Activity.
-     * Will return false if native is not initialized.
-     *
-     * Trusted Web Activities require CustomTabsClient#warmup to have been called, meaning that
-     * native will have been initialized when the client is trying to use a TWA.
      */
     boolean isTrustedWebActivity() {
-        if (!ChromeFeatureList.isInitialized()) return false;
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY)) return false;
-
         return mIsTrustedWebActivity;
     }
 
@@ -831,6 +834,16 @@
     }
 
     /**
+     * See {@link #EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE}.
+     * @return The header value sent to managed hosts when the URL matches the
+     *         EXTRA_MODULE_MANAGED_URLS_REGEX.
+     */
+    @Nullable
+    public String getExtraModuleManagedUrlsHeaderValue() {
+        return mModuleManagedUrlsHeaderValue;
+    }
+
+    /**
      * @return the Intent this instance was created with.
      */
     public Intent getIntent() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index db15dc4..aff45b8b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -1318,13 +1318,17 @@
         if (!prefs.getNetworkPredictionEnabled()) {
             return SPECULATION_STATUS_ON_START_NOT_ALLOWED_NETWORK_PREDICTION_DISABLED;
         }
-        if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
+        if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()
+                && !ChromeFeatureList.isEnabled(
+                        ChromeFeatureList.PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES)) {
             return SPECULATION_STATUS_ON_START_NOT_ALLOWED_DATA_REDUCTION_ENABLED;
         }
         ConnectivityManager cm =
                 (ConnectivityManager) ContextUtils.getApplicationContext().getSystemService(
                         Context.CONNECTIVITY_SERVICE);
-        if (cm.isActiveNetworkMetered() && !shouldSpeculateLoadOnCellularForSession(session)) {
+        if (cm.isActiveNetworkMetered() && !shouldSpeculateLoadOnCellularForSession(session)
+                && !ChromeFeatureList.isEnabled(
+                        ChromeFeatureList.PREDICTIVE_PREFETCHING_ALLOWED_ON_ALL_CONNECTION_TYPES)) {
             return SPECULATION_STATUS_ON_START_NOT_ALLOWED_NETWORK_METERED;
         }
         return SPECULATION_STATUS_ON_START_ALLOWED;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleConstants.java
index d2599f6..e60c862 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleConstants.java
@@ -9,6 +9,11 @@
  */
 public class DynamicModuleConstants {
     /**
+     * The custom header's name used for module managed URLs.
+     */
+    public static final String MANAGED_URL_HEADER = "X-CCT-Module-Client-Data";
+
+    /**
      * The module version when {@link IActivityDelegate#onNavigationEvent} is introduced.
      */
     public static final int ON_NAVIGATION_EVENT_MODULE_API_VERSION = 4;
@@ -17,4 +22,4 @@
      * The module version when {@link IActivityDelegate#onBackPressedAsync} is introduced.
      */
     public static final int ON_BACK_PRESSED_ASYNC_API_VERSION = 2;
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
index fd604f3..6a20c30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
@@ -19,6 +19,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -40,6 +41,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.util.UrlUtilities;
+import org.chromium.content_public.browser.NavigationHandleProxy;
 import org.chromium.content_public.browser.WebContents;
 
 import java.lang.annotation.Retention;
@@ -111,6 +113,45 @@
             maybeCustomizeCctHeader(url);
         }
     };
+
+    // Update the request's header on module managed URLs.
+    private final EmptyTabObserver mCustomRequestHeaderModifier = new EmptyTabObserver() {
+        @Override
+        public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame,
+                boolean isSameDocument, long navigationHandleProxy) {
+            if (!isInMainFrame || isSameDocument) return;
+
+            updateCustomRequestHeader(url, navigationHandleProxy, false /* isRedirect */);
+        }
+
+        @Override
+        public void onDidRedirectNavigation(
+                Tab tab, String url, boolean isInMainFrame, long navigationHandleProxy) {
+            if (!isInMainFrame) return;
+
+            updateCustomRequestHeader(url, navigationHandleProxy, true /* isRedirect */);
+        }
+
+        private void updateCustomRequestHeader(
+                String url, long navigationHandleProxy, boolean isRedirect) {
+            if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER))
+                return;
+            try (TraceEvent e = TraceEvent.scoped(
+                         "DynamicModuleCoordinator.updateCustomRequestHeader")) {
+                if (isModuleManagedUrl(url)) {
+                    String headerValue = mIntentDataProvider.getExtraModuleManagedUrlsHeaderValue();
+                    if (headerValue != null) {
+                        NavigationHandleProxy.nativeSetRequestHeader(navigationHandleProxy,
+                                DynamicModuleConstants.MANAGED_URL_HEADER, headerValue);
+                    }
+                } else if (isRedirect) {
+                    NavigationHandleProxy.nativeRemoveRequestHeader(
+                            navigationHandleProxy, DynamicModuleConstants.MANAGED_URL_HEADER);
+                }
+            }
+        }
+    };
+
     private final DynamicModuleNavigationEventObserver mModuleNavigationEventObserver =
             new DynamicModuleNavigationEventObserver();
 
@@ -131,6 +172,7 @@
 
         mTabObserverRegistrar.registerTabObserver(mModuleNavigationEventObserver);
         mTabObserverRegistrar.registerTabObserver(mHeaderVisibilityObserver);
+        mTabObserverRegistrar.registerTabObserver(mCustomRequestHeaderModifier);
 
         mActivityDelegate = activityDelegate;
         mTopBarDelegate = topBarDelegate;
@@ -306,7 +348,7 @@
                         >= DynamicModuleConstants.ON_NAVIGATION_EVENT_MODULE_API_VERSION) {
                     mModuleNavigationEventObserver.setActivityDelegate(mActivityDelegate);
                 } else {
-                    unregisterModuleObservers();
+                    unregisterObserver(mModuleNavigationEventObserver);
                 }
 
                 // Initialise the PostMessageHandler for the current web contents.
@@ -393,6 +435,7 @@
     private void unregisterModuleObservers() {
         unregisterObserver(mModuleNavigationEventObserver);
         unregisterObserver(mHeaderVisibilityObserver);
+        unregisterObserver(mCustomRequestHeaderModifier);
     }
 
     private void unregisterObserver(TabObserver observer) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index fd60f578..5023452 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -313,7 +313,7 @@
 
             @Override
             public void didStartNavigation(String url, boolean isInMainFrame,
-                    boolean isSameDocument, boolean isErrorPage) {
+                    boolean isSameDocument, long navigationHandleProxy) {
                 if (!isInMainFrame || isSameDocument) return;
 
                 // Reader Mode should not pollute the navigation stack. To avoid this, watch for
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
index 1dcfb46..963e1edc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyCoordinator.java
@@ -15,10 +15,10 @@
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterObserver;
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.util.Collection;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
index 6c349a0..8cb1a83 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/empty/EmptyViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.download.home.empty;
 
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
 
 /**
  * A helper {@link ViewBinder} responsible for gluing {@link EmptyProperties} to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
index ea772873..3097533c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
@@ -11,9 +11,9 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.filter.chips.ChipsCoordinator;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
index 011afcf5..5e87012b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.download.home.filter;
 
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
 
 /**
  * A helper {@link ViewBinder} responsible for gluing {@link FilterProperties} to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
index f42e5d7d..46e27e49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
@@ -13,9 +13,9 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 
 /**
  * The coordinator responsible for managing a list of chips.  To get the {@link View} that
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsViewHolder.java
index b55a4f1..cfeb072d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsViewHolder.java
@@ -8,6 +8,7 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.widget.ChipView;
 
 /** The {@link ViewHolder} responsible for reflecting a {@link Chip} to a {@link View}. */
@@ -26,7 +27,7 @@
 
     /**
      * Used as a method reference for ViewHolderFactory.
-     * @see org.chromium.chrome.browser.modelutil.RecyclerViewAdapter
+     * @see RecyclerViewAdapter
      *         .ViewHolderFactory#createViewHolder
      */
     public static ChipsViewHolder create(ViewGroup parent, int viewType) {
@@ -39,7 +40,7 @@
      * Used as a method reference for ViewBinder, to push the properties of {@code chip} to
      * {@link #itemView}.
      * @param chip The {@link Chip} to visually reflect in the stored {@link View}.
-     * @see org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp.ViewBinder#onBindViewHolder
+     * @see SimpleRecyclerViewMcp.ViewBinder#onBindViewHolder
      */
     public void bind(Chip chip) {
         getChipView().setEnabled(chip.enabled);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java
index 5b0aa33..d876e7314 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/BatchListModel.java
@@ -8,7 +8,7 @@
 import android.support.v7.util.BatchingListUpdateCallback;
 import android.support.v7.util.ListUpdateCallback;
 
-import org.chromium.chrome.browser.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListModel;
 
 /**
  * Helper class to batch updates to ListModel before notifying observers.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index eb69194..ead0e3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -22,11 +22,11 @@
 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig;
 import org.chromium.chrome.browser.download.home.list.DateOrderedListCoordinator.DateOrderedListObserver;
 import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
-import org.chromium.chrome.browser.modelutil.ForwardingListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.ui.modelutil.ForwardingListObservable;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /**
  * The View component of a DateOrderedList.  This takes the DateOrderedListModel and creates the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListViewAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListViewAdapter.java
index 2569a90..ffd4bb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListViewAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListViewAdapter.java
@@ -7,7 +7,7 @@
 import android.support.v7.widget.RecyclerView;
 
 import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /**
  * A helper {@link RecyclerView.Adapter} implementation meant to glue a {@link ListItemModel}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
index b1e8e49..a9e69ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DecoratedListItemModel.java
@@ -7,11 +7,11 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.modelutil.ListObservableImpl;
-import org.chromium.chrome.browser.modelutil.SimpleList;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
+import org.chromium.ui.modelutil.ListObservableImpl;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.SimpleList;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
index 2d0947ff..c84b90b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java
@@ -8,9 +8,9 @@
 import android.support.v7.widget.RecyclerView.ItemAnimator;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
 
 class ListPropertyViewBinder implements ViewBinder<PropertyModel, RecyclerView, PropertyKey> {
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
index 823370a8..3892e8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
@@ -25,7 +25,7 @@
     /**
      * Used as a method reference for ViewHolderFactory.
      * @see
-     * org.chromium.chrome.browser.modelutil.RecyclerViewAdapter.ViewHolderFactory#createViewHolder
+     * RecyclerViewAdapter.ViewHolderFactory#createViewHolder
      */
     public static ListItemViewHolder create(ViewGroup parent, @ListUtils.ViewType int viewType) {
         switch (viewType) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
index e5c2f03ee..b363501 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageCoordinator.java
@@ -10,11 +10,11 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.download.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * The coordinator responsible for creating the storage summary view in download home.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
index 557b598..f7a9556 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -12,17 +12,17 @@
 import android.text.SpannableStringBuilder;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.ForwardingListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.widget.LoadingView;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
+import org.chromium.ui.modelutil.ForwardingListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.widget.ChromeBulletSpan;
 import org.chromium.ui.widget.TextViewWithLeading;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java
index b074812..0033478 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java
@@ -10,7 +10,7 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /** Factory to create CategoryCardViewHolder objects. */
 class CategoryCardViewHolderFactory implements RecyclerViewAdapter.ViewHolderFactory<
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
index 3236fab..405e7ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -15,7 +15,6 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -25,6 +24,7 @@
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 0cd8831..9eed295 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -22,8 +22,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageHost;
@@ -35,7 +33,9 @@
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProvider.java
index d16a1b9..90b24758 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProvider.java
@@ -229,17 +229,19 @@
     @Override
     public void onSessionStarted(CastSession session, String sessionId) {
         Log.d(TAG, "onSessionStarted");
-        sessionController().attachToCastSession(session);
-        sessionController().onSessionStarted();
 
-        MediaSink sink = mPendingCreateRouteRequestInfo.sink;
-        MediaSource source = mPendingCreateRouteRequestInfo.source;
-        MediaRoute route = new MediaRoute(
-                sink.getId(), source.getSourceId(), mPendingCreateRouteRequestInfo.presentationId);
-        addRoute(route, mPendingCreateRouteRequestInfo.origin, mPendingCreateRouteRequestInfo.tabId,
-                mPendingCreateRouteRequestInfo.nativeRequestId, /* wasLaunched= */ true);
+        if (session != CastUtils.getCastContext().getSessionManager().getCurrentCastSession()) {
+            // Sometimes the session start signal might come in for an earlier launch request, which
+            // should be ignored.
+            return;
+        }
 
-        mPendingCreateRouteRequestInfo = null;
+        if (session == sessionController().getSession() || mPendingCreateRouteRequestInfo == null) {
+            // Early return for any possible case that the session start signal comes in twice for
+            // the same session.
+            return;
+        }
+        handleSessionStart(session, sessionId);
     }
 
     @Override
@@ -273,6 +275,20 @@
     // SessionManagerListener implementation end
     ///////////////////////////////////////////////////////
 
+    protected void handleSessionStart(CastSession session, String sessionId) {
+        sessionController().attachToCastSession(session);
+        sessionController().onSessionStarted();
+
+        MediaSink sink = mPendingCreateRouteRequestInfo.sink;
+        MediaSource source = mPendingCreateRouteRequestInfo.source;
+        MediaRoute route = new MediaRoute(
+                sink.getId(), source.getSourceId(), mPendingCreateRouteRequestInfo.presentationId);
+        addRoute(route, mPendingCreateRouteRequestInfo.origin, mPendingCreateRouteRequestInfo.tabId,
+                mPendingCreateRouteRequestInfo.nativeRequestId, /* wasLaunched= */ true);
+
+        mPendingCreateRouteRequestInfo = null;
+    }
+
     private void handleSessionEnd() {
         if (mPendingCreateRouteRequestInfo != null) {
             // The Cast SDK notifies about session ending when a route is unselected, even when
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
index 6f8eda4..9c1d501 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
@@ -149,8 +149,8 @@
     }
 
     @Override
-    public void onSessionStarted(CastSession session, String sessionId) {
-        super.onSessionStarted(session, sessionId);
+    protected void handleSessionStart(CastSession session, String sessionId) {
+        super.handleSessionStart(session, sessionId);
 
         for (ClientRecord clientRecord : mClientIdToRecords.values()) {
             // Should be exactly one instance of MediaRoute/ClientRecord at this moment.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebappUma.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebappUma.java
index b5558b1..4118d3a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebappUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebappUma.java
@@ -10,6 +10,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.webapps.SplashscreenObserver;
 import org.chromium.chrome.browser.webapps.WebappSplashScreenController;
+import org.chromium.webapk.lib.common.splash.SplashLayout;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -97,11 +98,31 @@
 
     /**
      * Records the type of icon on the splash screen.
-     * @param type flag representing the type of icon.
+     * @param selectedIconClassification
+     * @param usingDedicatedIcon Whether the PWA provides different icons for the splash screen and
+     *                           for the app icon.
      */
-    public void recordSplashscreenIconType(@SplashIconType int type) {
+    public void recordSplashscreenIconType(
+            @SplashLayout.IconClassification int selectedIconClassification,
+            boolean usingDedicatedIcon) {
         assert !mCommitted;
-        mSplashScreenIconType = type;
+        mSplashScreenIconType =
+                determineIconTypeForUma(selectedIconClassification, usingDedicatedIcon);
+    }
+
+    private static @WebappUma.SplashIconType int determineIconTypeForUma(
+            @SplashLayout.IconClassification int selectedIconClassification,
+            boolean usingDedicatedIcon) {
+        if (selectedIconClassification == SplashLayout.IconClassification.INVALID) {
+            return WebappUma.SplashIconType.NONE;
+        }
+        if (!usingDedicatedIcon) {
+            return WebappUma.SplashIconType.FALLBACK;
+        }
+        if (selectedIconClassification == SplashLayout.IconClassification.SMALL) {
+            return WebappUma.SplashIconType.CUSTOM_SMALL;
+        }
+        return WebappUma.SplashIconType.CUSTOM;
     }
 
     public void recordSplashscreenIconSize(int size) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
index df6a990..51abadc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
@@ -9,12 +9,12 @@
 import android.view.LayoutInflater;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
 public class AppModalPresenter extends ModalDialogManager.Presenter {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
index 0d4f45d..f2439a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java
@@ -4,10 +4,10 @@
 
 package org.chromium.chrome.browser.modaldialog;
 
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * This class is responsible for binding view properties from {@link ModalDialogProperties} to a
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
index f90c28b..a8c3b5ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
@@ -21,7 +21,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
@@ -37,6 +36,7 @@
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * The presenter that displays a single tab modal dialog.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
deleted file mode 100644
index b5edb89d..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://ui/android/java/src/org/chromium/ui/modelutil/OWNERS
-
-# COMPONENT: UI>Browser>Mobile
-# OS: Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java
index 7532f7d9..0074a6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java
@@ -18,8 +18,11 @@
 public interface ChromeNotificationBuilder {
     ChromeNotificationBuilder setAutoCancel(boolean autoCancel);
 
+    @Deprecated
     ChromeNotificationBuilder setContentIntent(PendingIntent contentIntent);
 
+    ChromeNotificationBuilder setContentIntent(PendingIntentProvider contentIntent);
+
     ChromeNotificationBuilder setContentTitle(CharSequence title);
 
     ChromeNotificationBuilder setContentText(CharSequence text);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilder.java
index 7de93a11..0324d666 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilder.java
@@ -23,15 +23,17 @@
 public class NotificationBuilder implements ChromeNotificationBuilder {
     private final Notification.Builder mBuilder;
     private final Context mContext;
+    private final NotificationMetadata mMetadata;
 
-    NotificationBuilder(
-            Context context, String channelId, ChannelsInitializer channelsInitializer) {
+    NotificationBuilder(Context context, String channelId, ChannelsInitializer channelsInitializer,
+            NotificationMetadata metadata) {
         mContext = context;
         mBuilder = new Notification.Builder(mContext);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             channelsInitializer.safeInitialize(channelId);
             mBuilder.setChannelId(channelId);
         }
+        mMetadata = metadata;
     }
 
     @Override
@@ -47,6 +49,16 @@
     }
 
     @Override
+    public ChromeNotificationBuilder setContentIntent(PendingIntentProvider contentIntent) {
+        assert (mMetadata != null);
+        PendingIntent pendingIntent = NotificationIntentInterceptor.createInterceptPendingIntent(
+                NotificationIntentInterceptor.IntentType.CONTENT_INTENT, 0 /* intentId */,
+                mMetadata, contentIntent);
+        mBuilder.setContentIntent(pendingIntent);
+        return this;
+    }
+
+    @Override
     public ChromeNotificationBuilder setContentTitle(CharSequence title) {
         mBuilder.setContentTitle(title);
         return this;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
index 891e3b0..9cccde6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java
@@ -32,16 +32,28 @@
      */
     public static ChromeNotificationBuilder createChromeNotificationBuilder(
             boolean preferCompat, String channelId) {
-        return createChromeNotificationBuilder(preferCompat, channelId, null);
+        return createChromeNotificationBuilder(
+                preferCompat, channelId, null /* remoteAppPackageName */, null /* metadata */);
+    }
+
+    /**
+     * See {@link #createChromeNotificationBuilder(boolean, String, String, NotificationMetadata)}.
+     */
+    public static ChromeNotificationBuilder createChromeNotificationBuilder(
+            boolean preferCompat, String channelId, @Nullable String remoteAppPackageName) {
+        return createChromeNotificationBuilder(
+                preferCompat, channelId, remoteAppPackageName, null /* metadata */);
     }
 
     /**
      * Same as above, with additional parameter:
      * @param remoteAppPackageName if not null, tries to create a Context from the package name
      * and passes it to the builder.
+     * @param metadata Metadata contains notification id, tag, etc.
      */
-    public static ChromeNotificationBuilder createChromeNotificationBuilder(
-            boolean preferCompat, String channelId, @Nullable String remoteAppPackageName) {
+    public static ChromeNotificationBuilder createChromeNotificationBuilder(boolean preferCompat,
+            String channelId, @Nullable String remoteAppPackageName,
+            @Nullable NotificationMetadata metadata) {
         Context context = ContextUtils.getApplicationContext();
         if (remoteAppPackageName != null) {
             assert ChromeFeatureList.isEnabled(ALLOW_REMOTE_CONTEXT_FOR_NOTIFICATIONS);
@@ -59,7 +71,8 @@
         ChannelsInitializer channelsInitializer =
                 new ChannelsInitializer(notificationManagerProxy, context.getResources());
 
-        return preferCompat ? new NotificationCompatBuilder(context, channelId, channelsInitializer)
-                            : new NotificationBuilder(context, channelId, channelsInitializer);
+        return preferCompat
+                ? new NotificationCompatBuilder(context, channelId, channelsInitializer, metadata)
+                : new NotificationBuilder(context, channelId, channelsInitializer, metadata);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
index 54fc825..7f50125 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationCompatBuilder.java
@@ -22,13 +22,15 @@
  */
 public class NotificationCompatBuilder implements ChromeNotificationBuilder {
     private final NotificationCompat.Builder mBuilder;
+    private final NotificationMetadata mMetadata;
 
-    NotificationCompatBuilder(
-            Context context, String channelId, ChannelsInitializer channelsInitializer) {
+    NotificationCompatBuilder(Context context, String channelId,
+            ChannelsInitializer channelsInitializer, NotificationMetadata metadata) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             channelsInitializer.safeInitialize(channelId);
         }
         mBuilder = new NotificationCompat.Builder(context, channelId);
+        mMetadata = metadata;
     }
 
     @Override
@@ -44,6 +46,16 @@
     }
 
     @Override
+    public ChromeNotificationBuilder setContentIntent(PendingIntentProvider contentIntent) {
+        assert mMetadata != null;
+        PendingIntent pendingIntent = NotificationIntentInterceptor.createInterceptPendingIntent(
+                NotificationIntentInterceptor.IntentType.CONTENT_INTENT, 0 /* intentId */,
+                mMetadata, contentIntent);
+        mBuilder.setContentIntent(pendingIntent);
+        return this;
+    }
+
+    @Override
     public ChromeNotificationBuilder setContentTitle(CharSequence title) {
         mBuilder.setContentTitle(title);
         return this;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
index 6af94f2..edbfba5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java
@@ -17,7 +17,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Random;
 
 /**
  * Class to intercept {@link PendingIntent}s from notifications, including
@@ -30,13 +29,17 @@
             "notifications.NotificationIntentInterceptor.EXTRA_PENDING_INTENT";
     private static final String EXTRA_INTENT_TYPE =
             "notifications.NotificationIntentInterceptor.EXTRA_INTENT_TYPE";
+    private static final String EXTRA_NOTIFICATION_TYPE =
+            "notifications.NotificationIntentInterceptor.EXTRA_NOTIFICATION_TYPE";
 
     /**
      * Enum that defines type of notification intent.
      */
-    @IntDef({IntentType.CONTENT_INTENT, IntentType.ACTION_INTENT, IntentType.DELETE_INTENT})
+    @IntDef({IntentType.UNKNOWN, IntentType.CONTENT_INTENT, IntentType.ACTION_INTENT,
+            IntentType.DELETE_INTENT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface IntentType {
+        int UNKNOWN = -1;
         int CONTENT_INTENT = 0;
         int ACTION_INTENT = 1;
         int DELETE_INTENT = 2;
@@ -51,7 +54,27 @@
     public static final class Receiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // TODO(xingliu): Record notification CTR UMA.
+            @IntentType
+            int intentType = intent.getIntExtra(EXTRA_INTENT_TYPE, IntentType.UNKNOWN);
+            @NotificationUmaTracker.SystemNotificationType
+            int notificationType = intent.getIntExtra(
+                    EXTRA_NOTIFICATION_TYPE, NotificationUmaTracker.SystemNotificationType.UNKNOWN);
+
+            switch (intentType) {
+                case IntentType.UNKNOWN:
+                    break;
+                case IntentType.CONTENT_INTENT:
+                    NotificationUmaTracker.getInstance().onNotificationContentClick(
+                            notificationType);
+                    break;
+                case IntentType.DELETE_INTENT:
+                    // TODO(xingliu): Tracks dismiss event.
+                    break;
+                case IntentType.ACTION_INTENT:
+                    // TODO(xingliu): Tracks action click event.
+                    break;
+            }
+
             forwardPendingIntent(intent);
         }
     }
@@ -62,25 +85,29 @@
      * Wraps the notification {@link PendingIntent }into another PendingIntent, to intercept clicks
      * and dismiss events for metrics purpose.
      * @param intentType The type of the pending intent to intercept.
-     * @param pendingIntent The {@link PendingIntent} of the notification that performs actual task.
+     * @param intentId The unique ID of the {@link PendingIntent}, used to distinguish action
+     *                 intents.
+     * @param metadata The metadata including notification id, tag, type, etc.
+     * @param pendingIntent The {@link PendingIntentProvider} of the notification that contains the
+     *                      {@link Notification#contentIntent}.
      */
-    public static PendingIntent createInterceptPendingIntent(
-            @IntentType int intentType, PendingIntent pendingIntent) {
+    public static PendingIntent createInterceptPendingIntent(@IntentType int intentType,
+            int intentId, NotificationMetadata metadata, PendingIntentProvider pendingIntent) {
         Context applicationContext = ContextUtils.getApplicationContext();
         Intent intent = new Intent(applicationContext, Receiver.class);
-        intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
+        intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent.getPendingIntent());
         intent.putExtra(EXTRA_INTENT_TYPE, intentType);
-        // TODO(xingliu): Add more extras to track notification type and action type. Use the
-        // combination of notification type and id as the request code.
-        int requestCode = new Random().nextInt(Integer.MAX_VALUE);
+        intent.putExtra(EXTRA_NOTIFICATION_TYPE, metadata.type);
 
         // This flag ensures the broadcast is delivered with foreground priority to speed up the
         // broadcast delivery.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         }
+        // Use request code to distinguish different PendingIntents on Android.
+        int requestCode = computeHashCode(metadata, intentType, intentId);
         return PendingIntent.getBroadcast(
-                applicationContext, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+                applicationContext, requestCode, intent, pendingIntent.getFlags());
     }
 
     // Launches the notification's pending intent, which will perform Chrome feature related tasks.
@@ -104,4 +131,24 @@
             e.printStackTrace();
         }
     }
+
+    /**
+     * Computes an unique hash code to identify the intercept {@link PendingIntent} that wraps the
+     * notification's {@link PendingIntent}.
+     * @param metadata Notification metadata including notification id, tag, etc.
+     * @param intentType The type of the {@link PendingIntent}.
+     * @param intentId The unique ID of the {@link PendingIntent}, used to distinguish action
+     *                 intents.
+     * @return The hashcode for the intercept {@link PendingIntent}.
+     */
+    private static int computeHashCode(
+            NotificationMetadata metadata, @IntentType int intentType, int intentId) {
+        assert metadata != null;
+        int hashcode = metadata.type;
+        hashcode = hashcode * 31 + intentType;
+        hashcode = hashcode * 31 + intentId;
+        hashcode = hashcode * 31 + (metadata.tag == null ? 0 : metadata.tag.hashCode());
+        hashcode = hashcode * 31 + metadata.id;
+        return hashcode;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationMetadata.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationMetadata.java
index d2a60bca6..bf25510 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationMetadata.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationMetadata.java
@@ -30,6 +30,7 @@
 
     public NotificationMetadata(
             int notificationType, @Nullable String notificationTag, int notificationId) {
+        // TODO(xingliu): NotificationMetadata should have the channel information as well.
         type = notificationType;
         tag = notificationTag;
         id = notificationId;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
index a6ded43..227f8f42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -15,6 +15,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 
@@ -35,22 +36,21 @@
      *
      * A SystemNotificationType value can also be saved in shared preferences.
      */
-    @IntDef({SystemNotificationType.DOWNLOAD_FILES, SystemNotificationType.DOWNLOAD_PAGES,
-            SystemNotificationType.CLOSE_INCOGNITO, SystemNotificationType.CONTENT_SUGGESTION,
-            SystemNotificationType.MEDIA_CAPTURE, SystemNotificationType.PHYSICAL_WEB,
-            SystemNotificationType.MEDIA, SystemNotificationType.SITES, SystemNotificationType.SYNC,
+    @IntDef({SystemNotificationType.UNKNOWN, SystemNotificationType.DOWNLOAD_FILES,
+            SystemNotificationType.DOWNLOAD_PAGES, SystemNotificationType.CLOSE_INCOGNITO,
+            SystemNotificationType.CONTENT_SUGGESTION, SystemNotificationType.MEDIA_CAPTURE,
+            SystemNotificationType.PHYSICAL_WEB, SystemNotificationType.MEDIA,
+            SystemNotificationType.SITES, SystemNotificationType.SYNC,
             SystemNotificationType.WEBAPK, SystemNotificationType.BROWSER_ACTIONS,
             SystemNotificationType.WEBAPP_ACTIONS,
             SystemNotificationType.OFFLINE_CONTENT_SUGGESTION,
             SystemNotificationType.TRUSTED_WEB_ACTIVITY_SITES})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SystemNotificationType {
+        int UNKNOWN = -1;
         int DOWNLOAD_FILES = 0;
-
         int DOWNLOAD_PAGES = 1;
-
         int CLOSE_INCOGNITO = 2;
-
         int CONTENT_SUGGESTION = 3;
         int MEDIA_CAPTURE = 4;
         int PHYSICAL_WEB = 5;
@@ -62,7 +62,6 @@
         int WEBAPP_ACTIONS = 11;
         int OFFLINE_CONTENT_SUGGESTION = 12;
         int TRUSTED_WEB_ACTIVITY_SITES = 13;
-
         int NUM_ENTRIES = 14;
     }
 
@@ -95,6 +94,8 @@
      * @see SystemNotificationType
      */
     public void onNotificationShown(@SystemNotificationType int type, Notification notification) {
+        if (type == SystemNotificationType.UNKNOWN) return;
+
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             logNotificationShown(type, notification.getChannelId());
         } else {
@@ -102,6 +103,19 @@
         }
     }
 
+    /**
+     * Logs notification click event when the user taps on the notification body.
+     * @param type Type of the notification.
+     */
+    public void onNotificationContentClick(@SystemNotificationType int type) {
+        if (type == SystemNotificationType.UNKNOWN) return;
+
+        new CachedMetrics
+                .EnumeratedHistogramSample("Mobile.SystemNotification.Content.Click",
+                        SystemNotificationType.NUM_ENTRIES)
+                .record(type);
+    }
+
     private void logNotificationShown(
             @SystemNotificationType int type, @ChannelDefinitions.ChannelId String channelId) {
         if (!mNotificationManager.areNotificationsEnabled()) {
@@ -132,7 +146,8 @@
     }
 
     private void logPotentialBlockedCause() {
-        int lastType = mSharedPreferences.getInt(LAST_SHOWN_NOTIFICATION_TYPE_KEY, -1);
+        int lastType = mSharedPreferences.getInt(
+                LAST_SHOWN_NOTIFICATION_TYPE_KEY, SystemNotificationType.UNKNOWN);
         if (lastType == -1) return;
         mSharedPreferences.edit().remove(LAST_SHOWN_NOTIFICATION_TYPE_KEY).apply();
 
@@ -140,6 +155,8 @@
     }
 
     private static void recordHistogram(String name, @SystemNotificationType int type) {
+        if (type == SystemNotificationType.UNKNOWN) return;
+
         if (!LibraryLoader.getInstance().isInitialized()) return;
         RecordHistogram.recordEnumeratedHistogram(name, type, SystemNotificationType.NUM_ENTRIES);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
index 8662b0cd..4e218f48a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
@@ -6,8 +6,8 @@
 
 import android.support.annotation.Nullable;
 
-import org.chromium.chrome.browser.modelutil.ListObservableImpl;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.ListObservableImpl;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /**
  * A node in the tree that has a parent and can notify it about changes.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
index eae92443..6847301 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
@@ -8,9 +8,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 0996ad5..7e816c02 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -12,7 +12,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
@@ -28,6 +27,7 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.ui.modelutil.ListObservable;
 
 import java.util.List;
 import java.util.Set;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PartiallyBindable.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PartiallyBindable.java
index 352ad401..3ee3634f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PartiallyBindable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/PartiallyBindable.java
@@ -6,8 +6,8 @@
 
 import android.support.annotation.Nullable;
 
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 /**
  * Helper class for implementations of {@link RecyclerViewAdapter.Delegate} using partial
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index e613b148..8896d15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -10,10 +10,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.modelutil.ListModelBase;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.PropertyListModel;
-import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcpBase;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
@@ -31,6 +27,10 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsOfflineModelObserver;
 import org.chromium.chrome.browser.suggestions.SuggestionsRanker;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.ui.modelutil.ListModelBase;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.PropertyListModel;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase;
 
 import java.util.Arrays;
 import java.util.Collections;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index 0388838c..d9e2849 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -9,12 +9,12 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.WindowDelegate;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlDirectionListener;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index b57bd7b..fb31799 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -24,7 +24,7 @@
 class UrlBarViewBinder {
     /**
      * @see
-     * org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
+     * PropertyModelChangeProcessor.ViewBinder#bind(Object,
      * Object, Object)
      */
     public static void bind(PropertyModel model, UrlBar view, PropertyKey propertyKey) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
index 61eec0f..d751c6b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.omnibox.status;
 
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
 
 /**
  * StatusViewBinder observes StatusModel changes and triggers StatusView updates.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index 83395de..07b0013 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -12,10 +12,10 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * A component for displaying a status icon (e.g. security icon or navigation icon) and optional
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 0a8e275..164915e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -17,8 +17,6 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
-import org.chromium.chrome.browser.modelutil.ModelListAdapter;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
@@ -35,6 +33,8 @@
 import org.chromium.ui.ViewProvider;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
+import org.chromium.ui.modelutil.ModelListAdapter;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 2dc5aa60..6bdbb5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -249,6 +249,7 @@
             mHandler.post(deferredRunnable);
         }
         mDeferredNativeRunnables.clear();
+        mBasicSuggestionProcessor.onNativeInitialized();
     }
 
     /** @see org.chromium.chrome.browser.omnibox.UrlFocusChangeListener#onUrlFocusChange(boolean) */
@@ -641,7 +642,7 @@
         if (mDeferredOnSelection != null) {
             mDeferredOnSelection.setShouldLog(newSuggestions.size() > mDeferredOnSelection.mPosition
                     && mDeferredOnSelection.mSuggestion.equals(
-                               newSuggestions.get(mDeferredOnSelection.mPosition)));
+                            newSuggestions.get(mDeferredOnSelection.mPosition)));
             mDeferredOnSelection.run();
             mDeferredOnSelection = null;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
index 2b3f176a..997dd54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java
@@ -7,8 +7,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.chromium.chrome.browser.modelutil.ModelListAdapter;
 import org.chromium.ui.UiUtils;
+import org.chromium.ui.modelutil.ModelListAdapter;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -34,7 +34,7 @@
 
     /**
      * @see
-     * org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
+     * PropertyModelChangeProcessor.ViewBinder#bind(Object,
      * Object, Object)
      */
     public static void bind(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index 5b66eab8..77f6f11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -15,6 +15,7 @@
 import android.view.View;
 
 import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
@@ -27,6 +28,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionTextContainer;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.omnibox.AnswerType;
 import org.chromium.components.omnibox.SuggestionAnswer;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -69,6 +71,7 @@
     private final SuggestionHost mSuggestionHost;
     private final AnswersImageFetcher mImageFetcher;
     private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
+    private boolean mEnableNewAnswerLayout;
 
     /**
      * @param context An Android context.
@@ -89,6 +92,15 @@
         return true;
     }
 
+    /**
+     * Signals that native initialization has completed.
+     */
+    public void onNativeInitialized() {
+        // Experiment: controls presence of certain answer icon types.
+        mEnableNewAnswerLayout =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_NEW_ANSWER_LAYOUT);
+    }
+
     @Override
     public int getViewTypeId() {
         return OmniboxSuggestionUiType.DEFAULT;
@@ -192,7 +204,41 @@
         model.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, secondLine.hasImage());
 
         model.set(SuggestionViewProperties.REFINABLE, true);
-        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.MAGNIFIER);
+
+        @SuggestionIcon
+        int icon = SuggestionIcon.MAGNIFIER;
+        if (mEnableNewAnswerLayout) {
+            switch (answer.getType()) {
+                case AnswerType.DICTIONARY:
+                    icon = SuggestionIcon.DICTIONARY;
+                    break;
+                case AnswerType.FINANCE:
+                    icon = SuggestionIcon.FINANCE;
+                    break;
+                case AnswerType.KNOWLEDGE_GRAPH:
+                    icon = SuggestionIcon.KNOWLEDGE;
+                    break;
+                case AnswerType.SUNRISE:
+                    icon = SuggestionIcon.SUNRISE;
+                    break;
+                case AnswerType.TRANSLATION:
+                    icon = SuggestionIcon.TRANSLATION;
+                    break;
+                case AnswerType.WEATHER:
+                    icon = SuggestionIcon.WEATHER;
+                    break;
+                case AnswerType.WHEN_IS:
+                    icon = SuggestionIcon.EVENT;
+                    break;
+                case AnswerType.CURRENCY:
+                    icon = SuggestionIcon.CURRENCY;
+                    break;
+                case AnswerType.SPORTS:
+                    icon = SuggestionIcon.SPORTS;
+            }
+        }
+
+        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, icon);
     }
 
     private void setStateForTextSuggestion(PropertyModel model, OmniboxSuggestion suggestion) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
index 702d527..4a12498 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
@@ -13,6 +13,7 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.VisibleForTesting;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -335,13 +336,20 @@
 
     /**
      * Updates the suggestion icon to the specified drawable with the specified tint.
+     * @param resId resource id (may be bitmap or vector) of icon to display
+     * @param useDarkTint specifies whether to apply dark or light tint to an icon
+     * @param allowTint specifies whether icon should receive a tint or displayed as is.
      */
-    void setSuggestionIconDrawable(@DrawableRes int resId, boolean useDarkTint) {
-        mContentsView.mSuggestionIcon = TintedDrawable.constructTintedDrawable(getContext(), resId,
-                useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint);
+    void setSuggestionIconDrawable(@DrawableRes int resId, boolean useDarkTint, boolean allowTint) {
+        mContentsView.mSuggestionIcon = AppCompatResources.getDrawable(getContext(), resId);
+        mContentsView.mAllowTint = allowTint;
         mContentsView.mSuggestionIcon.setBounds(0, 0,
                 mContentsView.mSuggestionIcon.getIntrinsicWidth(),
                 mContentsView.mSuggestionIcon.getIntrinsicHeight());
+        updateSuggestionIconTint(useDarkTint);
+        // Note: invalidate() does not immediately invoke onDraw(), but schedules it to be called at
+        // some point in the future.
+        // https://developer.android.com/reference/android/view/View.html#invalidate()
         mContentsView.invalidate();
     }
 
@@ -349,9 +357,10 @@
      * Updates the suggestion icon (if present) to use the specified tint.
      */
     void updateSuggestionIconTint(boolean useDarkTint) {
-        if (mContentsView.mSuggestionIcon == null) return;
-        mContentsView.mSuggestionIcon.setTint(AppCompatResources.getColorStateList(
-                getContext(), useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
+        if (!mContentsView.mAllowTint || mContentsView.mSuggestionIcon == null) return;
+        DrawableCompat.setTint(mContentsView.mSuggestionIcon,
+                ApiCompatibilityUtils.getColor(getContext().getResources(),
+                useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
         mContentsView.invalidate();
     }
 
@@ -388,7 +397,8 @@
      * icon).
      */
     private class SuggestionContentsContainer extends ViewGroup {
-        private TintedDrawable mSuggestionIcon;
+        private Drawable mSuggestionIcon;
+        private boolean mAllowTint;
 
         private final TextView mTextLine1;
         private final TextView mTextLine2;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
index baa9e7e..5c79d2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
@@ -26,7 +26,11 @@
  */
 class SuggestionViewProperties {
     @IntDef({SuggestionIcon.UNDEFINED, SuggestionIcon.BOOKMARK, SuggestionIcon.HISTORY,
-            SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE})
+            SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE,
+            SuggestionIcon.CALCULATOR, SuggestionIcon.DICTIONARY, SuggestionIcon.FINANCE,
+            SuggestionIcon.KNOWLEDGE, SuggestionIcon.SUNRISE, SuggestionIcon.TRANSLATION,
+            SuggestionIcon.WEATHER, SuggestionIcon.EVENT, SuggestionIcon.CURRENCY,
+            SuggestionIcon.SPORTS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SuggestionIcon {
         int UNDEFINED = -1;
@@ -35,6 +39,16 @@
         int GLOBE = 2;
         int MAGNIFIER = 3;
         int VOICE = 4;
+        int CALCULATOR = 5;
+        int DICTIONARY = 6;
+        int FINANCE = 7;
+        int KNOWLEDGE = 8;
+        int SUNRISE = 9;
+        int TRANSLATION = 10;
+        int WEATHER = 11;
+        int EVENT = 12;
+        int CURRENCY = 13;
+        int SPORTS = 14;
     }
 
     /**
@@ -145,4 +159,4 @@
 
     public static final PropertyKey[] ALL_KEYS =
             PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
index 20bdc438..b793ad3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
@@ -25,7 +25,7 @@
 public class SuggestionViewViewBinder {
     /**
      * @see
-     * org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
+     * PropertyModelChangeProcessor.ViewBinder#bind(Object,
      * Object, Object)
      */
     public static void bind(PropertyModel model, SuggestionView view, PropertyKey propertyKey) {
@@ -62,6 +62,7 @@
             int type = model.get(SuggestionViewProperties.SUGGESTION_ICON_TYPE);
 
             if (type == SuggestionIcon.UNDEFINED) return;
+            boolean allowTint = true;
 
             int drawableId = R.drawable.ic_omnibox_page;
             switch (type) {
@@ -77,11 +78,52 @@
                 case SuggestionIcon.VOICE:
                     drawableId = R.drawable.btn_mic;
                     break;
+                case SuggestionIcon.CALCULATOR:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_equals_sign_round;
+                    break;
+                case SuggestionIcon.DICTIONARY:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_book_round;
+                    break;
+                case SuggestionIcon.FINANCE:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_swap_vert_round;
+                    break;
+                case SuggestionIcon.KNOWLEDGE:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_google_round;
+                    break;
+                case SuggestionIcon.SUNRISE:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_wb_sunny_round;
+                    break;
+                case SuggestionIcon.TRANSLATION:
+                    allowTint = false;
+                    drawableId = R.drawable.logo_translate_round;
+                    break;
+                case SuggestionIcon.WEATHER:
+                    allowTint = false;
+                    drawableId = R.drawable.logo_partly_cloudy_light;
+                    break;
+                case SuggestionIcon.EVENT:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_event_round;
+                    break;
+                case SuggestionIcon.CURRENCY:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_loop_round;
+                    break;
+                case SuggestionIcon.SPORTS:
+                    allowTint = false;
+                    drawableId = R.drawable.ic_google_round;
+                    break;
                 default:
                     break;
             }
+
             view.setSuggestionIconDrawable(
-                    drawableId, model.get(SuggestionCommonProperties.USE_DARK_COLORS));
+                    drawableId, model.get(SuggestionCommonProperties.USE_DARK_COLORS), allowTint);
         } else if (SuggestionViewProperties.TEXT_LINE_1_SIZING.equals(propertyKey)) {
             Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_1_SIZING);
             view.getTextLine1().setTextSize(sizing.first, sizing.second);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
index 7f2f6209..596f0ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
@@ -15,6 +15,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -50,6 +51,7 @@
     private static final String PREF_SYNC_AND_SERVICES_LINK_DIVIDER =
             "sync_and_services_link_divider";
     private static final String PREF_SYNC_AND_SERVICES_LINK = "sync_and_services_link";
+    private static final String PREF_USAGE_STATS = "usage_stats_reporting";
 
     private ManagedPreferenceDelegate mManagedPreferenceDelegate;
 
@@ -127,6 +129,15 @@
         safeBrowsingPref.setOnPreferenceChangeListener(this);
         safeBrowsingPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
 
+        if (BuildInfo.isAtLeastQ()) {
+            ChromeBaseCheckBoxPreference usageStatsPref =
+                    (ChromeBaseCheckBoxPreference) findPreference(PREF_USAGE_STATS);
+            usageStatsPref.setOnPreferenceChangeListener(this);
+            usageStatsPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+        } else {
+            preferenceScreen.removePreference(findPreference(PREF_USAGE_STATS));
+        }
+
         updateSummaries();
     }
 
@@ -148,6 +159,9 @@
         } else if (PREF_CAN_MAKE_PAYMENT.equals(key)) {
             PrefServiceBridge.getInstance().setBoolean(
                     Pref.CAN_MAKE_PAYMENT_ENABLED, (boolean) newValue);
+        } else if (PREF_USAGE_STATS.equals(key)) {
+            PrefServiceBridge.getInstance().setBoolean(
+                    Pref.USAGE_STATS_ENABLED, (boolean) newValue);
         }
 
         return true;
@@ -225,6 +239,11 @@
                     privacyPrefManager.isUsageAndCrashReportingPermittedByUser() ? textOn
                                                                                  : textOff);
         }
+
+        CheckBoxPreference usageStatsPref = (CheckBoxPreference) findPreference(PREF_USAGE_STATS);
+        if (usageStatsPref != null) {
+            usageStatsPref.setChecked(prefServiceBridge.getBoolean(Pref.USAGE_STATS_ENABLED));
+        }
     }
 
     private ManagedPreferenceDelegate createManagedPreferenceDelegate() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
index 36dd8e3..43cc6b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
@@ -139,7 +139,7 @@
     public static DurationTracker getSpinnerVisibilityReporter() {
         return new DurationTracker((duration) -> {
             RecordHistogram.recordTimesHistogram(
-                    "ContentSuggestions.FetchPendingSpinner.VisibleDuration", duration,
+                    "ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration", duration,
                     TimeUnit.MILLISECONDS);
         });
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
index 877f03c..e4ff56c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
@@ -99,7 +99,11 @@
 
     @Override
     public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame,
-            boolean isSameDocument, boolean isErrorPage) {}
+            boolean isSameDocument, long navigationHandleProxy) {}
+
+    @Override
+    public void onDidRedirectNavigation(
+            Tab tab, String url, boolean isInMainFrame, long navigationHandleProxy) {}
 
     @Override
     public void onDidFinishNavigation(Tab tab, String url, boolean isInMainFrame,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index cc2e00df..3ed5c35 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -392,7 +392,7 @@
 
             String url = tab.getUrl();
             // Simulate the PAGE_LOAD_STARTED notification that we did not get.
-            didStartPageLoad(url, false);
+            didStartPageLoad(url);
 
             if (didFinishLoad) {
                 // Simulate the PAGE_LOAD_FINISHED notification that we did not get.
@@ -1201,7 +1201,7 @@
             initWebContents(webContents);
 
             if (!creatingWebContents && webContents.isLoadingToDifferentDocument()) {
-                didStartPageLoad(webContents.getVisibleUrl(), false);
+                didStartPageLoad(webContents.getVisibleUrl());
             }
 
         } finally {
@@ -1441,9 +1441,8 @@
     /**
      * Called when a page has started loading.
      * @param validatedUrl URL being loaded.
-     * @param showingErrorPage Whether an error page is being shown.
      */
-    protected void didStartPageLoad(String validatedUrl, boolean showingErrorPage) {
+    protected void didStartPageLoad(String validatedUrl) {
         updateTitle();
         if (mIsRendererUnresponsive) handleRendererResponsiveStateChanged(true);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index 36d94498..49b8b87 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -226,10 +226,24 @@
      * @param isInMainFrame Whether the navigation is for the main frame.
      * @param isSameDocument Whether the main frame navigation did not cause changes to the
      *                   document (for example scrolling to a named anchor or PopState).
-     * @param isErrorPage Whether the navigation shows an error page.
+     * @param navigationHandleProxy Pointer to a NavigationHandleProxy representing the navigation.
+     *                              Its lifetime is bound to this function. Do not store it. It can
+     *                              be used to modify headers.
      */
     public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame,
-            boolean isSameDocument, boolean isErrorPage);
+            boolean isSameDocument, long navigationHandleProxy);
+
+    /**
+     * Called when a navigation is redirected in the WebContents.
+     * @param tab The notifying {@link Tab}.
+     * @param url The validated URL for the loading page.
+     * @param isInMainFrame Whether the navigation is for the main frame.
+     * @param navigationHandleProxy Pointer to a NavigationHandleProxy representing the navigation.
+     *                              Its lifetime is bound to this function. Do not store it. It can
+     *                              be used to modify headers.
+     */
+    public void onDidRedirectNavigation(
+            Tab tab, String url, boolean isInMainFrame, long navigationHandleProxy);
 
     /**
      * Called when a navigation is finished i.e. committed, aborted or replaced by a new one.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index 15db351..46a3034 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -216,16 +216,26 @@
         }
 
         @Override
-        public void didStartNavigation(
-                String url, boolean isInMainFrame, boolean isSameDocument, boolean isErrorPage) {
+        public void didStartNavigation(String url, boolean isInMainFrame, boolean isSameDocument,
+                long navigationHandleProxy) {
             if (isInMainFrame && !isSameDocument) {
-                mTab.didStartPageLoad(url, isErrorPage);
+                mTab.didStartPageLoad(url);
             }
 
             RewindableIterator<TabObserver> observers = mTab.getTabObservers();
             while (observers.hasNext()) {
                 observers.next().onDidStartNavigation(
-                        mTab, url, isInMainFrame, isSameDocument, isErrorPage);
+                        mTab, url, isInMainFrame, isSameDocument, navigationHandleProxy);
+            }
+        }
+
+        @Override
+        public void didRedirectNavigation(
+                String url, boolean isInMainFrame, long navigationHandleProxy) {
+            RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+            while (observers.hasNext()) {
+                observers.next().onDidRedirectNavigation(
+                        mTab, url, isInMainFrame, navigationHandleProxy);
             }
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index b190ab5..d2f0130 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -9,7 +9,6 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
@@ -17,6 +16,7 @@
 import org.chromium.chrome.browser.toolbar.ThemeColorProvider.ThemeColorObserver;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * The controller for the tab switcher button. This class handles all interactions that the tab
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
index 49d9450..d7c91e08 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.toolbar;
 
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * This class is responsible for pushing updates to the Android view of the tab switcher. These
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index e7468ea..6196797 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -559,7 +559,7 @@
 
             @Override
             public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame,
-                    boolean isSameDocument, boolean isErrorPage) {
+                    boolean isSameDocument, long navigationHandleProxy) {
                 if (!isInMainFrame) return;
                 // Update URL as soon as it becomes available when it's a new tab.
                 // But we want to update only when it's a new tab. So we check whether the current
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
index bc6988f2..9088647 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
@@ -16,7 +16,6 @@
 import org.chromium.chrome.browser.compositor.layouts.ToolbarSwipeLayout;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.HomeButton;
@@ -27,6 +26,7 @@
 import org.chromium.chrome.browser.toolbar.bottom.BrowsingModeBottomToolbarViewBinder.ViewHolder;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.resources.ResourceManager;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
index 6f9ec09..abccabc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
@@ -9,8 +9,8 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.scene_layer.ScrollingBottomViewSceneLayer;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * This class is responsible for pushing updates to both the Android view and the compositor
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
index 96248aa..a1e4ce7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarCoordinator.java
@@ -11,12 +11,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
 import org.chromium.chrome.browser.toolbar.MenuButton;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
 import org.chromium.chrome.browser.toolbar.ThemeColorProvider;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * The coordinator for the tab switcher mode bottom toolbar. This class handles all interactions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
index 7696f96..ee635ca9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java
@@ -7,8 +7,8 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * This class is responsible for pushing updates the view of the tab switcher bottom toolbar. These
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentActivity.java
new file mode 100644
index 0000000..aa926107
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentActivity.java
@@ -0,0 +1,81 @@
+// 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.
+
+package org.chromium.chrome.browser.usage_stats;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.SynchronousInitializationActivity;
+import org.chromium.chrome.browser.widget.PromoDialog;
+
+/**
+ * Activity that prompts the user for consent to share browsing activity with Digital Wellbeing.
+ */
+public class UsageStatsConsentActivity extends SynchronousInitializationActivity {
+    private static final String DIGITAL_WELLBEING_PACKAGE_NAME =
+            "com.google.android.apps.wellbeing";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // TODO(pnoland): handle revocation as well as authorization.
+        ComponentName caller = getCallingActivity();
+        if (caller == null
+                || !TextUtils.equals(DIGITAL_WELLBEING_PACKAGE_NAME, caller.getPackageName())) {
+            finish();
+            return;
+        }
+
+        PromoDialog promoScreen = makePromoScreen();
+        promoScreen.show();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    private PromoDialog makePromoScreen() {
+        return new PromoDialog(this) {
+            @Override
+            protected DialogParams getDialogParams() {
+                PromoDialog.DialogParams params = new PromoDialog.DialogParams();
+                params.headerStringResource = R.string.usage_stats_consent_title;
+                params.subheaderStringResource = R.string.usage_stats_consent_prompt;
+                params.primaryButtonStringResource = R.string.ok;
+                params.secondaryButtonStringResource = R.string.cancel;
+                return params;
+            }
+
+            @Override
+            public void onDismiss(DialogInterface dialog) {
+                UsageStatsConsentActivity.this.finish();
+            }
+
+            @Override
+            public void onClick(View v) {
+                int id = v.getId();
+                UsageStatsService service = UsageStatsService.getInstance();
+
+                if (id == R.id.button_primary) {
+                    service.setOptInState(true);
+                    UsageStatsConsentActivity.this.setResult(Activity.RESULT_OK);
+                    UsageStatsConsentActivity.this.finish();
+                } else if (id == R.id.button_secondary) {
+                    service.setOptInState(false);
+                    UsageStatsConsentActivity.this.setResult(Activity.RESULT_CANCELED);
+                    UsageStatsConsentActivity.this.finish();
+                } else {
+                    assert false : "Unhandled onClick event";
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
index c09d885..f97eb1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
@@ -8,6 +8,9 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+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.tabmodel.TabModelSelector;
 
 import java.util.List;
@@ -22,6 +25,7 @@
     private EventTracker mEventTracker;
     private SuspensionTracker mSuspensionTracker;
     private TokenTracker mTokenTracker;
+    private UsageStatsBridge mBridge;
     private boolean mOptInState;
 
     /** Get the global instance of UsageStatsService */
@@ -38,6 +42,9 @@
         mEventTracker = new EventTracker();
         mSuspensionTracker = new SuspensionTracker();
         mTokenTracker = new TokenTracker();
+        Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
+        mBridge = new UsageStatsBridge(profile);
+        // TODO(pnoland): listen for preference changes so we can notify DW.
     }
 
     /**
@@ -55,15 +62,15 @@
     /** @return Whether the user has authorized DW to access usage stats data. */
     public boolean getOptInState() {
         ThreadUtils.assertOnUiThread();
-        // TODO(pnoland): return the value of the pref that controls opt in state.
-        return mOptInState;
+        PrefServiceBridge prefServiceBridge = PrefServiceBridge.getInstance();
+        return prefServiceBridge.getBoolean(Pref.USAGE_STATS_ENABLED);
     }
 
     /** Sets the user's opt in state. */
     public void setOptInState(boolean state) {
         ThreadUtils.assertOnUiThread();
-        // TODO(pnoland): set the value of the pref that controls opt in state.
-        mOptInState = state;
+        PrefServiceBridge prefServiceBridge = PrefServiceBridge.getInstance();
+        prefServiceBridge.setBoolean(Pref.USAGE_STATS_ENABLED, state);
     }
 
     /** Query for all events that occurred in the half-open range [start, end) */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
index ec5d2a2..14064522 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/VrModalPresenter.java
@@ -14,11 +14,11 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.modaldialog.ModalDialogView;
 import org.chromium.chrome.browser.modaldialog.ModalDialogViewBinder;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
 public class VrModalPresenter extends ModalDialogManager.Presenter {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
index b7589c7..e8a60fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
@@ -8,6 +8,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.support.annotation.IntDef;
+import android.util.DisplayMetrics;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -101,12 +102,6 @@
         startSplashscreenTraceEvents();
 
         notifySplashscreenVisible();
-        mWebappUma.recordSplashscreenBackgroundColor(webappInfo.hasValidBackgroundColor()
-                        ? WebappUma.SplashColorStatus.CUSTOM
-                        : WebappUma.SplashColorStatus.DEFAULT);
-        mWebappUma.recordSplashscreenThemeColor(webappInfo.hasValidThemeColor()
-                        ? WebappUma.SplashColorStatus.CUSTOM
-                        : WebappUma.SplashColorStatus.DEFAULT);
 
         if (mIsForWebApk) {
             initializeLayout(webappInfo, backgroundColor, ((WebApkInfo) webappInfo).splashIcon());
@@ -245,45 +240,55 @@
         Context context = ContextUtils.getApplicationContext();
         Resources resources = context.getResources();
 
-        Bitmap displayIcon = splashImage;
-        boolean displayIconGenerated = false;
-        boolean displayIconAdaptive = false;
-        if (displayIcon == null) {
-            displayIcon = webappInfo.icon();
-            displayIconGenerated = webappInfo.isIconGenerated();
-            displayIconAdaptive = webappInfo.isIconAdaptive();
+        Bitmap selectedIcon = splashImage;
+        boolean selectedIconGenerated = false;
+        boolean selectedIconAdaptive = false;
+        if (selectedIcon == null) {
+            selectedIcon = webappInfo.icon();
+            selectedIconGenerated = webappInfo.isIconGenerated();
+            selectedIconAdaptive = webappInfo.isIconAdaptive();
         }
         @SplashLayout.IconClassification
-        int displayIconClassification =
-                SplashLayout.classifyIcon(resources, displayIcon, displayIconGenerated);
+        int selectedIconClassification =
+                SplashLayout.classifyIcon(resources, selectedIcon, selectedIconGenerated);
 
-        if (displayIconClassification == SplashLayout.IconClassification.INVALID) {
-            mWebappUma.recordSplashscreenIconType(WebappUma.SplashIconType.NONE);
-        } else {
-            // Record stats about the splash screen.
-            @WebappUma.SplashIconType
-            int splashScreenIconType;
-            if (splashImage == null) {
-                splashScreenIconType = WebappUma.SplashIconType.FALLBACK;
-            } else if (displayIconClassification == SplashLayout.IconClassification.SMALL) {
-                splashScreenIconType = WebappUma.SplashIconType.CUSTOM_SMALL;
-            } else {
-                splashScreenIconType = WebappUma.SplashIconType.CUSTOM;
-            }
-            mWebappUma.recordSplashscreenIconType(splashScreenIconType);
-            mWebappUma.recordSplashscreenIconSize(
-                    Math.round(displayIcon.getScaledWidth(resources.getDisplayMetrics())
-                            / resources.getDisplayMetrics().density));
-        }
-
-        SplashLayout.createLayout(context, mSplashScreen, displayIcon, displayIconAdaptive,
-                displayIconClassification, webappInfo.name(),
+        SplashLayout.createLayout(context, mSplashScreen, selectedIcon, selectedIconAdaptive,
+                selectedIconClassification, webappInfo.name(),
                 ColorUtils.shouldUseLightForegroundOnBackground(backgroundColor));
 
+        recordUma(resources, webappInfo, selectedIconClassification, selectedIcon,
+                (splashImage != null));
         if (mNativeLoaded) mWebappUma.commitMetrics();
     }
 
     /**
+     * Records splash screen UMA metrics.
+     * @param resources
+     * @param webappInfo
+     * @param selectedIconClassification.
+     * @param selectedIcon The icon used on the splash screen.
+     * @param usingDedicatedIcon Whether the PWA provides different icons for the splash screen and
+     *                           for the app icon.
+     */
+    private void recordUma(Resources resources, WebappInfo webappInfo,
+            @SplashLayout.IconClassification int selectedIconClassification, Bitmap selectedIcon,
+            boolean usingDedicatedIcon) {
+        mWebappUma.recordSplashscreenBackgroundColor(webappInfo.hasValidBackgroundColor()
+                        ? WebappUma.SplashColorStatus.CUSTOM
+                        : WebappUma.SplashColorStatus.DEFAULT);
+        mWebappUma.recordSplashscreenThemeColor(webappInfo.hasValidThemeColor()
+                        ? WebappUma.SplashColorStatus.CUSTOM
+                        : WebappUma.SplashColorStatus.DEFAULT);
+
+        mWebappUma.recordSplashscreenIconType(selectedIconClassification, usingDedicatedIcon);
+        if (selectedIconClassification != SplashLayout.IconClassification.INVALID) {
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            mWebappUma.recordSplashscreenIconSize(Math.round(
+                    selectedIcon.getScaledWidth(displayMetrics) / displayMetrics.density));
+        }
+    }
+
+    /**
      * Schedules the splash screen hiding once the compositor has finished drawing a frame.
      *
      * Without this callback we were seeing a short flash of white between the splash screen and
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index f45748e5..6dc14ad6 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3977,6 +3977,17 @@
       <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_URL" desc="URL for Google Autofill Assistant Terms of Service" translateable="false">
         http://support.google.com/assistant?p=fast_checkout
       </message>
+
+      <!-- Usage Stats strings -->
+      <message name="IDS_USAGE_STATS_CONSENT_TITLE" desc="Title for activity authorizing Digital Wellbeing to access Chrome usage data">
+        Connect Chrome to Digital Wellbeing?
+      </message>
+      <message name="IDS_USAGE_STATS_CONSENT_PROMPT" desc="Consent prompt when authorizing Digital Wellbeing to access Chrome usage data">
+        Allow Digital Wellbeing to show websites you visited
+      </message>
+      <message name="IDS_USAGE_STATS_SETTING_TITLE" desc="Title for setting toggling Digital Wellbeing to access Chrome usage data">
+        Connect to Digital Wellbeing
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_PROMPT.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_PROMPT.png.sha1
new file mode 100644
index 0000000..5e187082
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_PROMPT.png.sha1
@@ -0,0 +1 @@
+80616bf5a8556aa427e879651a52a4a13ac93206
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_TITLE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_TITLE.png.sha1
new file mode 100644
index 0000000..5e187082
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_CONSENT_TITLE.png.sha1
@@ -0,0 +1 @@
+80616bf5a8556aa427e879651a52a4a13ac93206
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTING_TITLE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTING_TITLE.png.sha1
new file mode 100644
index 0000000..c17b8e0
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTING_TITLE.png.sha1
@@ -0,0 +1 @@
+cb952ebac04dfca80271fd7a62c536db99235acf
\ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 13d323f..ec06c5f2 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -963,20 +963,6 @@
   "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java",
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java",
-  "java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ListModel.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ListModelChangeProcessor.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ListObservable.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java",
-  "java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java",
-  "java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java",
-  "java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java",
-  "java/src/org/chromium/chrome/browser/modelutil/SimpleList.java",
-  "java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java",
-  "java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java",
   "java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceChromeTabbedActivity.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java",
@@ -1650,6 +1636,15 @@
   "java/src/org/chromium/chrome/browser/upgrade/PackageReplacedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/upgrade/UpgradeActivity.java",
   "java/src/org/chromium/chrome/browser/upgrade/UpgradeIntentService.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/EventTracker.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/TokenGenerator.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/TokenTracker.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentActivity.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/WebsiteEvent.java",
   "java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java",
   "java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
   "java/src/org/chromium/chrome/browser/util/ChromeFileProvider.java",
@@ -1847,19 +1842,6 @@
   ]
 }
 
-if (enable_usage_stats) {
-  chrome_java_sources += [
-    "java/src/org/chromium/chrome/browser/usage_stats/EventTracker.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/TokenGenerator.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/TokenTracker.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java",
-    "java/src/org/chromium/chrome/browser/usage_stats/WebsiteEvent.java",
-  ]
-}
-
 chrome_test_java_sources = [
   "javatests/src/org/chromium/chrome/browser/ActivityTabProviderTest.java",
   "javatests/src/org/chromium/chrome/browser/AudioTest.java",
@@ -2488,9 +2470,6 @@
   "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java",
   "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTitleUpdatedTest.java",
   "junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java",
-  "junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java",
-  "junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java",
-  "junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java",
   "junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeUnitTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationSystemStatusUtilUnitTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java
index 0cf07e4..ffe561d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java
@@ -29,13 +29,13 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece.Type;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
-import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.ui.DeferredViewStubInflationProvider;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 import org.chromium.ui.widget.TextViewWithLeading;
 
 import java.util.concurrent.ExecutionException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
index 1b203e4..ebf7c45 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java
@@ -44,13 +44,13 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
-import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
-import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ViewUtils;
 import org.chromium.ui.DeferredViewStubInflationProvider;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.concurrent.ArrayBlockingQueue;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
index bbab0c2..78b57b4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java
@@ -39,9 +39,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
index bbeb85ea..47b67fc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java
@@ -44,12 +44,12 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
-import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
-import org.chromium.chrome.browser.modelutil.ListModel;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.ui.DeferredViewStubInflationProvider;
 import org.chromium.ui.ViewProvider;
+import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.concurrent.ArrayBlockingQueue;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
index 4f67dcc..7c23497 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
@@ -41,8 +41,6 @@
 import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
@@ -71,6 +69,8 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestWebContentsObserver;
 import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.Locale;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
index c107433..f4d4c7b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
@@ -4,89 +4,108 @@
 
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
-import android.content.Context;
 import android.content.Intent;
 import android.support.customtabs.CustomTabsCallback;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.AppHooksModule;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.CustomTabsDynamicModuleTestUtils.FakeCCTActivityDelegate;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.CustomTabsDynamicModuleTestUtils.IntentBuilder;
 import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
-import org.chromium.chrome.browser.firstrun.FirstRunStatus;
-import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.content_public.browser.test.util.JavaScriptUtils;
+import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.net.test.ServerCertificate;
 import org.chromium.ui.base.PageTransition;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
  * Instrumentation tests for the CustomTabsDynamicModuleNavigationObserver.
  */
-@RunWith(Parameterized.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1", "ignore-certificate-errors"})
 public class CustomTabsDynamicModuleNavigationTest {
     @Rule
     public CustomTabActivityTestRule mActivityRule = new CustomTabActivityTestRule();
 
-    /**
-     * Test against different module versions i.e. before and after API was introduced.
-     */
-    @Parameterized.Parameters(name = "moduleVersion={0};")
-    public static Object[] values() {
-        return new Object[]{1, 4};
-    }
-
-    @Parameterized.Parameter
-    public int moduleVersion;
-
     private String mTestPage;
     private String mTestPage2;
     private String mTestPage3;
-    private EmbeddedTestServer mServer;
+    private EmbeddedTestServer mTestServer;
+
+    /**
+     * Test against different module versions i.e. before and after API was introduced.
+     */
+    @ClassParameter
+    private static List<ParameterSet> sModuleVersionsList = Arrays.asList(
+            new ParameterSet().value(1).name("API_1"), new ParameterSet().value(4).name("API_4"));
+
+    public CustomTabsDynamicModuleNavigationTest(int moduleVersion) {
+        CustomTabsDynamicModuleTestUtils.setModuleVersion(moduleVersion);
+    }
 
     @Before
     public void setUp() throws Exception {
-        ThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true));
         LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
 
         ModuleFactoryOverrides.setOverride(AppHooksModule.Factory.class,
                 CustomTabsDynamicModuleTestUtils.AppHooksModuleForTest::new);
 
-        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext()
-                .getApplicationContext();
+        // Module managed hosts only work with HTTPS.
+        mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                ServerCertificate.CERT_OK);
 
-        mServer = EmbeddedTestServer.createAndStartServer(appContext);
-        mTestPage = mServer.getURL("/chrome/test/data/android/google.html");
-        mTestPage2 = mServer.getURL("/chrome/test/data/android/simple.html");
-        mTestPage3 = mServer.getURL("/chrome/test/data/android/about.html");
+        mTestPage = mTestServer.getURLWithHostName(
+                "google.com", "/chrome/test/data/android/google.html");
+        mTestPage2 = mTestServer.getURLWithHostName(
+                "google.com", "/chrome/test/data/android/simple.html");
+        mTestPage3 = mTestServer.getURLWithHostName(
+                "google.com", "/chrome/test/data/android/about.html");
+
+        // The EmbeddedTestServer uses a non standard port.
+        DynamicModuleCoordinator.setAllowNonStandardPortNumber(true);
     }
 
     @After
     public void tearDown() {
+        mTestServer.stopAndDestroyServer();
         ModuleFactoryOverrides.clearOverrides();
-        ThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true));
+        DynamicModuleCoordinator.setAllowNonStandardPortNumber(false);
     }
 
     @Test
     @SmallTest
-    @Features.EnableFeatures(ChromeFeatureList.CCT_MODULE)
-    public void testModuleNavigationNotification()
-            throws TimeoutException, InterruptedException {
-        CustomTabsDynamicModuleTestUtils.setModuleVersion(moduleVersion);
+    @EnableFeatures(ChromeFeatureList.CCT_MODULE)
+    public void testModuleNavigationNotification() throws TimeoutException, InterruptedException {
         Intent intent = new IntentBuilder(mTestPage).build();
 
         mActivityRule.startCustomTabActivityWithIntent(intent);
@@ -112,4 +131,129 @@
     private DynamicModuleCoordinator getModuleCoordinator() {
         return getActivity().getComponent().resolveDynamicModuleCoordinator();
     }
-}
\ No newline at end of file
+
+    /**
+     * Returns the text content of the document body.
+     */
+    private String getDocumentContent() throws Exception {
+        Tab tab = getActivity().getActivityTab();
+        return JavaScriptUtils.executeJavaScriptAndWaitForResult(
+                tab.getWebContents(), "document.body.textContent");
+    }
+
+    private static final String HEADER_VALUE = "HEADER_VALUE";
+    private static final String HEADER_VALUE_QUOTED = "\"" + HEADER_VALUE + "\"";
+    private static final String NONE_QUOTED = "\"None\"";
+
+    // The managed url regex matches the URL. The custom header is added.
+    @Test
+    @SmallTest
+    @EnableFeatures(
+            {ChromeFeatureList.CCT_MODULE, ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER})
+    public void
+    testHeaderShown() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        Intent intent = new IntentBuilder(finalURL)
+                                .setModuleManagedUrlRegex(".*")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+        Assert.assertEquals(HEADER_VALUE_QUOTED, getDocumentContent());
+    }
+
+    // The managed url regex matches the URL, but CCT_MODULE_CUSTOM_REQUEST_HEADER is disabled. The
+    // custom header is not added.
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.CCT_MODULE)
+    @DisableFeatures(ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER)
+    public void testHeaderFeatureDisabled() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        Intent intent = new IntentBuilder(finalURL)
+                                .setModuleManagedUrlRegex(".*")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+        Assert.assertEquals(NONE_QUOTED, getDocumentContent());
+    }
+
+    // The managed url regex doesn't match the URL. No custom header is added.
+    @Test
+    @SmallTest
+    @EnableFeatures(
+            {ChromeFeatureList.CCT_MODULE, ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER})
+    public void
+    testHeaderNotShown() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        Intent intent = new IntentBuilder(finalURL)
+                                .setModuleManagedUrlRegex("no-match")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+        Assert.assertEquals(NONE_QUOTED, getDocumentContent());
+    }
+
+    // The managed url regex doesn't match the initial URL, but match the final
+    // URL after a redirect. A custom header is added.
+    @Test
+    @SmallTest
+    @EnableFeatures(
+            {ChromeFeatureList.CCT_MODULE, ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER})
+    public void
+    testHeaderMatchFinalURLOnly() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        String redirectURL = mTestServer.getURL("/server-redirect?" + finalURL);
+        Intent intent = new IntentBuilder(redirectURL)
+                                .setModuleManagedUrlRegex("^((?!redirect).)*$")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+
+        Assert.assertEquals(HEADER_VALUE_QUOTED, getDocumentContent());
+    }
+
+    // The managed url regex matches the initial URL, but doesn't match the
+    // final URL after a redirect. The custom header must have been removed from
+    // the second request.
+    @Test
+    @SmallTest
+    @EnableFeatures(
+            {ChromeFeatureList.CCT_MODULE, ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER})
+    public void
+    testHeaderMatchRedirectURLOnly() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        String redirectURL = mTestServer.getURL("/server-redirect?" + finalURL);
+        Intent intent = new IntentBuilder(redirectURL)
+                                .setModuleManagedUrlRegex(".*redirect.*")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+
+        Assert.assertEquals(NONE_QUOTED, getDocumentContent());
+    }
+
+    // The managed url regex matches the initial URL and the final URL after a
+    // redirect. The custom header is added to both requests.
+    @Test
+    @SmallTest
+    @EnableFeatures(
+            {ChromeFeatureList.CCT_MODULE, ChromeFeatureList.CCT_MODULE_CUSTOM_REQUEST_HEADER})
+    public void
+    testHeaderMatchBoth() throws Exception {
+        String finalURL = mTestServer.getURLWithHostName(
+                "google.com", "/echoheader?" + DynamicModuleConstants.MANAGED_URL_HEADER);
+        String redirectURL = mTestServer.getURL("/server-redirect?" + finalURL);
+        Intent intent = new IntentBuilder(redirectURL)
+                                .setModuleManagedUrlRegex(".*")
+                                .setModuleManagedUrlHeaderValue(HEADER_VALUE)
+                                .build();
+        mActivityRule.startCustomTabActivityWithIntent(intent);
+
+        Assert.assertEquals(HEADER_VALUE_QUOTED, getDocumentContent());
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
index e67888d..98194cb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS;
 import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_CLASS_NAME;
+import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE;
 import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_MANAGED_URLS_REGEX;
 import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_PACKAGE_NAME;
 import static org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleNavigationEventObserver.PENDING_URL_KEY;
@@ -233,6 +234,11 @@
             return this;
         }
 
+        IntentBuilder setModuleManagedUrlHeaderValue(String headerValue) {
+            mIntent.putExtra(EXTRA_MODULE_MANAGED_URLS_HEADER_VALUE, headerValue);
+            return this;
+        }
+
         IntentBuilder setHideCCTHeader(boolean isEnabled) {
             mIntent.putExtra(EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS, isEnabled);
             return this;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java
index 5723505..c942710a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java
@@ -103,7 +103,7 @@
         EmptyTabObserver navigationWaiter = new EmptyTabObserver() {
             @Override
             public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame,
-                    boolean isSameDocument, boolean isErrorPage) {
+                    boolean isSameDocument, long navigationHandleProxy) {
                 callbackHelper.notifyCalled();
             }
         };
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
index 74d9b21..0b5d47a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java
@@ -38,12 +38,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.io.IOException;
 import java.util.Collections;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
index 503f26c..61797bec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
@@ -6,7 +6,6 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -56,8 +55,9 @@
     // Builds a simple notification used in tests.
     private Notification buildSimpleNotification(String title) {
         ChromeNotificationBuilder builder =
-                NotificationBuilderFactory.createChromeNotificationBuilder(
-                        true /* preferCompat */, ChannelDefinitions.ChannelId.DOWNLOADS);
+                NotificationBuilderFactory.createChromeNotificationBuilder(true /* preferCompat */,
+                        ChannelDefinitions.ChannelId.DOWNLOADS, null /* remoteAppPackageName */,
+                        NotificationTestUtil.getTestNotificationMetadata());
 
         // Set content intent. UI automator may tap the notification and expand the action buttons,
         // in order to reduce flakiness, don't add action button.
@@ -66,11 +66,10 @@
         Uri uri = Uri.parse("www.example.com");
         contentIntent.setData(uri);
         contentIntent.setAction(Intent.ACTION_VIEW);
-        PendingIntent contentPendingIntent =
-                PendingIntent.getActivity(context, 0, contentIntent, 0);
+        PendingIntentProvider contentPendingIntent =
+                PendingIntentProvider.getActivity(context, 0, contentIntent, 0);
         assert contentPendingIntent != null;
-        builder.setContentIntent(NotificationIntentInterceptor.createInterceptPendingIntent(
-                NotificationIntentInterceptor.IntentType.CONTENT_INTENT, contentPendingIntent));
+        builder.setContentIntent(contentPendingIntent);
         builder.setContentTitle(title);
         builder.setSmallIcon(R.drawable.offline_pin);
         return builder.build();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationTestUtil.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationTestUtil.java
index 21aa94e..d463554 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationTestUtil.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationTestUtil.java
@@ -43,6 +43,11 @@
         return ((BitmapDrawable) icon.loadDrawable(context)).getBitmap();
     }
 
+    public static NotificationMetadata getTestNotificationMetadata() {
+        return new NotificationMetadata(
+                NotificationUmaTracker.SystemNotificationType.UNKNOWN, null, 0);
+    }
+
     @SuppressLint("NewApi") // Notification.actions is hidden in Jellybean
     static Notification.Action[] getActions(Notification notification) {
         return notification.actions;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
index 8eee4b2..dc67441 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
@@ -38,7 +38,6 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
@@ -58,6 +57,7 @@
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.net.test.EmbeddedTestServerRule;
+import org.chromium.ui.modelutil.ListObservable;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
index c61f0ae..12114a6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java
@@ -34,11 +34,11 @@
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
+import org.chromium.ui.test.util.modelutil.FakeViewProvider;
 
 /**
  * Controller tests for the keyboard accessory bottom sheet component.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
index ab1f1246..f6c3165 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
@@ -33,11 +33,11 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
+import org.chromium.ui.test.util.modelutil.FakeViewProvider;
 
 import java.util.HashMap;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
index fc8c314..66ae971 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java
@@ -26,7 +26,7 @@
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
index bf69a7a..05856127 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -54,19 +54,19 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BarItem;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
-import org.chromium.chrome.browser.modelutil.ListModel;
-import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
-import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.display.DisplayAndroid;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListObservable;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.test.util.modelutil.FakeViewProvider;
 
 import java.lang.ref.WeakReference;
 import java.util.Map;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
index 828af9b..74041ea 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
@@ -36,7 +36,7 @@
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.FooterCommand;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.PropertyProvider;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
-import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable;
 
 /**
  * Controller tests for the password accessory sheet.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
index 188633b1..13576e7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMutatorTest.java
@@ -28,10 +28,10 @@
 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
 import org.chromium.chrome.browser.download.home.list.ListItem.OfflineItemListItem;
 import org.chromium.chrome.browser.download.home.list.ListItem.SectionHeaderListItem;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemFilter;
 import org.chromium.components.offline_items_collection.OfflineItemState;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 
 import java.util.Calendar;
 import java.util.Collections;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProviderTest.java
index be61129..5dddd80 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafBaseMediaRouteProviderTest.java
@@ -384,6 +384,7 @@
         MediaSource mockSource = mock(MediaSource.class);
         doReturn(mockSource).when(mProvider).getSourceFromId("source-id");
         doReturn("source-id").when(mockSource).getSourceId();
+        doReturn(mCastSession).when(mSessionManager).getCurrentCastSession();
 
         // Request to create a session.
         mProvider.createRoute("source-id", "cast-route", "presentation-id", "origin", 1, false, 1);
@@ -403,6 +404,65 @@
     }
 
     @Test
+    public void testOnSessionStarted_nonCurrentSession() {
+        CastSession otherCastSession = mock(CastSession.class);
+
+        InOrder inOrder = inOrder(mSessionController);
+        MediaSource mockSource = mock(MediaSource.class);
+        doReturn(mockSource).when(mProvider).getSourceFromId("source-id");
+        doReturn("source-id").when(mockSource).getSourceId();
+        doReturn(otherCastSession).when(mSessionManager).getCurrentCastSession();
+
+        // Request to create a session.
+        mProvider.createRoute("source-id", "cast-route", "presentation-id", "origin", 1, false, 1);
+
+        // Session started.
+        mProvider.onSessionStarted(mCastSession, "session-id");
+
+        inOrder.verify(mSessionController, never()).attachToCastSession(mCastSession);
+        inOrder.verify(mSessionController, never()).onSessionStarted();
+        assertTrue(mProvider.mRoutes.isEmpty());
+    }
+
+    @Test
+    public void testOnSessionStarted_twiceForSameSession() {
+        InOrder inOrder = inOrder(mSessionController);
+        MediaSource mockSource = mock(MediaSource.class);
+        doReturn(mockSource).when(mProvider).getSourceFromId("source-id");
+        doReturn("source-id").when(mockSource).getSourceId();
+        doReturn(mCastSession).when(mSessionManager).getCurrentCastSession();
+
+        // Request to create a session.
+        mProvider.createRoute("source-id", "cast-route", "presentation-id", "origin", 1, false, 1);
+
+        // Session started.
+        mProvider.onSessionStarted(mCastSession, "session-id");
+
+        inOrder.verify(mSessionController).attachToCastSession(mCastSession);
+        inOrder.verify(mSessionController).onSessionStarted();
+        assertEquals(mProvider.mRoutes.size(), 1);
+
+        MediaRoute route = (MediaRoute) (mProvider.mRoutes.values().toArray()[0]);
+        assertEquals(route.sinkId, "cast-route");
+        assertEquals(route.sourceId, "source-id");
+        assertEquals(route.presentationId, "presentation-id");
+        assertNull(mProvider.getPendingCreateRouteRequestInfo());
+
+        // Same session started for the second time.
+        mProvider.onSessionStarted(mCastSession, "session-id");
+
+        inOrder.verify(mSessionController, never()).attachToCastSession(mCastSession);
+        inOrder.verify(mSessionController, never()).onSessionStarted();
+        assertEquals(mProvider.mRoutes.size(), 1);
+
+        route = (MediaRoute) (mProvider.mRoutes.values().toArray()[0]);
+        assertEquals(route.sinkId, "cast-route");
+        assertEquals(route.sourceId, "source-id");
+        assertEquals(route.presentationId, "presentation-id");
+        assertNull(mProvider.getPendingCreateRouteRequestInfo());
+    }
+
+    @Test
     public void testOnSessionResumed() {
         mProvider.onSessionResumed(mCastSession, true);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java
index 329de164..532dd83 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java
@@ -11,6 +11,7 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
@@ -34,6 +35,8 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
@@ -259,6 +262,17 @@
         InOrder inOrder = inOrder(mSessionController, mRemoteMediaClient);
 
         doReturn(mSink).when(mSessionController).getSink();
+        doReturn(mCastSession).when(mSessionManager).getCurrentCastSession();
+        doReturn(null).when(mSessionController).getSession();
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                doReturn(invocation.getArguments()[0]).when(mSessionController).getSession();
+                return null;
+            }
+        })
+                .when(mSessionController)
+                .attachToCastSession(any(CastSession.class));
 
         // Prepare the pending create route request so super.onSessionStarted() behaves correctly.
         mProvider.createRoute(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/OWNERS
deleted file mode 100644
index 17a573e..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://chrome/android/java/src/org/chromium/chrome/browser/modelutil/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
index 6bba9a9..5d44b887 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
@@ -26,10 +26,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index f6d8f85..69728c08b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -54,7 +54,6 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.cards.SignInPromo.SigninObserver;
@@ -80,6 +79,7 @@
 import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.net.NetworkChangeNotifier;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
index b7f61c5..6b9a8ed0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -48,8 +48,6 @@
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.modelutil.ListObservable;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
@@ -69,6 +67,8 @@
 import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge;
 import org.chromium.chrome.test.util.browser.suggestions.ContentSuggestionsTestUtils.CategoryInfoBuilder;
 import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index a6f5bcc..24b7e1f4 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-73.0.3671.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-73.0.3674.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/android/chrome_main_delegate_android.cc b/chrome/app/android/chrome_main_delegate_android.cc
index c7d31192..213c8d37 100644
--- a/chrome/app/android/chrome_main_delegate_android.cc
+++ b/chrome/app/android/chrome_main_delegate_android.cc
@@ -92,7 +92,7 @@
     if (!browser_runner_.get()) {
       startup_metric_utils::RecordMainEntryPointTime(
           chrome::android::GetMainEntryPointTimeTicks());
-      browser_runner_.reset(content::BrowserMainRunner::Create());
+      browser_runner_ = content::BrowserMainRunner::Create();
     }
     return browser_runner_->Initialize(main_function_params);
   }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index f9e6fce..175bb33 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1293,6 +1293,14 @@
     Certificates not loaded
   </message>
 
+  <!-- Sync/sign-in error messages -->
+  <message name="IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_TITLE" desc="Title in the sign-in error bubble view/notification for Chrome OS Secondary Accounts.">
+    Sign-in again
+  </message>
+  <message name="IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_MESSAGE" desc="Message in the sign-in error bubble view for Chrome OS Secondary Accounts.">
+    Your Google Account(s) need attention
+  </message>
+
   <!-- Strings used in <cr-network-list>, used in Settings and OOBE -->
   <message name="IDS_NETWORK_LIST_INITIALIZING" desc="Text in the network list element when a Cellular network is initiailizing.">
     Initializing...
@@ -3562,9 +3570,6 @@
   <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PASSWORD_REAUTH" desc="Tooltip for an icon on user's lock screen pod shown by EasyUnlock when the user must enter their password instead of using EasyUnlock, because too much time has passed since their last password entry.">
     For added security, Smart Lock will ask you to enter your password after 20 hours.
   </message>
-  <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PASSWORD_REQUIRED_FOR_LOGIN" desc="Tooltip for an icon on user's lock screen pod shown by EasyUnlock when a password is required to login to the Chromebook.">
-    To start Smart Lock, enter your password. Next time, you can use your phone to unlock your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>.
-  </message>
   <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_UNSUPPORTED_ANDROID_VERSION" desc="Tooltip for the icon on user's lock screen pod shown by Easy Unlock when a phone set up to unlock the Chromebook is detected, but has an unsupported Android version. The user is asked to update the phone.">
     Please update your phone to a newer version of Android to unlock this <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>.
   </message>
@@ -3577,9 +3582,12 @@
   <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED" desc="Tooltip text shown on a user's lock screen pod when Easy Unlock pairing data is synced on a new Chromebook.">
     Enter your password to enable Smart Lock. Next time, your phone will unlock your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. Turn off Smart Lock in Settings.
   </message>
-  <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE" desc="Tooltip text shown on a user's lock screen when Smart Lock signin attempt fails.">
+  <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE" desc="Tooltip text shown on a user's sign-in screen when Smart Lock sign-in attempt fails.">
     Smart Lock couldn’t verify your account. Type your password to enter.
   </message>
+  <message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_DISABLED" desc="Tooltip for an icon on user's sign-in screen pod shown by EasyUnlock when the feature is only enabled for unlock, not sign-in.">
+    To use Smart Lock to sign in to your Google Account, go to Settings > Connected devices > Your phone > Smart Lock.
+  </message>
   <message name="IDS_SMART_LOCK_SCREENLOCK_TOOLTIP_HARDLOCK_REAUTH_USER" desc="Tooltip text shown on a user's lock screen pod to reauthenticate the user before setting up Smart Lock. A password has to be entered to unlock the device.">
     To set up Smart Lock for Chromebook, Google needs to make sure it’s you—type your password to get started.
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_MESSAGE.png.sha1
new file mode 100644
index 0000000..e85f1b4
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_MESSAGE.png.sha1
@@ -0,0 +1 @@
+3382aecab164ba4f9cb5348a5eb2b2afd4364d72
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_TITLE.png.sha1
new file mode 100644
index 0000000..e85f1b4
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_TITLE.png.sha1
@@ -0,0 +1 @@
+3382aecab164ba4f9cb5348a5eb2b2afd4364d72
\ No newline at end of file
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 6abbc20..30555aa 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -717,6 +717,9 @@
         </message>
       </if>
       <if expr="chromeos">
+        <message name="IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE" desc="Context title shown in the notification header of sign-in error notification for Chromium OS Secondary Accounts.">
+          Chromium OS System
+        </message>
         <message name="IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE" desc="Message in the sync error notification when the user needs to update their sync passphrase.">
           Chromium OS could not sync your data. Please update your Sync passphrase.
         </message>
diff --git a/chrome/app/chromium_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1
new file mode 100644
index 0000000..083d3abb
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1
@@ -0,0 +1 @@
+588b2e6b80268b422425396516d8ad3e0cb681b6
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 683f83a..b3b6844 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5755,9 +5755,12 @@
         <message name="IDS_TAB_CXMENU_SEND_TO_MY_DEVICES" desc="The label of the tab context menu item for share this tab to other devices.">
           Send to my devices
         </message>
-        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_NEW_GROUP" desc="The label of the tab context menu item for creating a new tab group and adding a single tab to it.">
+        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_NEW_GROUP" desc="The label of the tab context menu item for creating a new tab group and adding one or more tabs to it.">
           Add to new group
         </message>
+        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP" desc="The label of the tab context menu submenu for adding one or more tabs to an existing tab group.">
+          Add to existing group
+        </message>
       </if>
       <if expr="use_titlecase">
         <message name="IDS_TAB_CXMENU_NEWTAB" desc="In Title Case: The label of the 'New Tab' Tab context menu item.">
@@ -5799,8 +5802,11 @@
         <message name="IDS_TAB_CXMENU_BOOKMARK_ALL_TABS" desc="In Title Case: The label of the tab context menu item for creating a bookmark folder containing an entry for each open tab.">
           Bookmark All Tabs...
         </message>
-        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_NEW_GROUP" desc="In Title Case: The label of the tab context menu item for creating a new tab group and adding a single tab to it.">
-          Add Tab to New Group
+        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_NEW_GROUP" desc="In Title Case: The label of the tab context menu item for creating a new tab group and adding one or more tabs to it.">
+          Add to New Group
+        </message>
+        <message name="IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP" desc="In Title Case: The label of the tab context menu submenu for adding one or more tabs to an existing tab group.">
+          Add to Existing Group
         </message>
         <message name="IDS_TAB_CXMENU_SEND_TO_MY_DEVICES" desc="In Title Case: The label of the tab context menu item for share this tab to other devices.">
           Send To My Devices
diff --git a/chrome/app/generated_resources_grd/IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP.png.sha1 b/chrome/app/generated_resources_grd/IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP.png.sha1
new file mode 100644
index 0000000..c3607b4
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP.png.sha1
@@ -0,0 +1 @@
+19feaf7abe591f018878302c0ac3c2958a07c430
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 7d446d72..2317c30 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -726,6 +726,9 @@
         </message>
       </if>
       <if expr="chromeos">
+        <message name="IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE" desc="Context title shown in the notification header of sign-in error notification for Chrome OS Secondary Accounts.">
+          Chrome OS System
+        </message>
         <message name="IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE" desc="Message in the sync error bubble view when the user needs to update their sync passphrase.">
           Chrome OS could not sync your data. Please update your Sync passphrase.
         </message>
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1
new file mode 100644
index 0000000..e85f1b4
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE.png.sha1
@@ -0,0 +1 @@
+3382aecab164ba4f9cb5348a5eb2b2afd4364d72
\ No newline at end of file
diff --git a/chrome/app/md_extensions_strings.grdp b/chrome/app/md_extensions_strings.grdp
index 93a88f01..aa875dd 100644
--- a/chrome/app/md_extensions_strings.grdp
+++ b/chrome/app/md_extensions_strings.grdp
@@ -131,7 +131,7 @@
     Activity Log
   </message>
   <message name="IDS_MD_EXTENSIONS_ACTIVITY_LOG_SEARCH_LABEL" desc="The placeholder label to display in the search bar for the activity log page.">
-    Search activities
+    Search by API call/URL
   </message>
   <message name="IDS_MD_EXTENSIONS_ITEM_ID" desc="The text for the label next to the extension id.">
     &lt;span&gt;ID: &lt;/span&gt;<ph name="EXTENSION_ID">$1<ex>cfhdojbkjhnklbpkdaibdccddilifddb</ex></ph>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index bd13c847..88740db1 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2571,11 +2571,8 @@
   <message name="IDS_SETTINGS_TITLE_AND_COUNT" desc="The title of a section in the settings page with a count of the total number of items in the section">
     <ph name="TITLE">$1<ex>Block</ex></ph> - <ph name="COUNT">$2<ex>42</ex></ph>
   </message>
-  <message name="IDS_SETTINGS_PRIVACY_MORE_SETTINGS" desc="The text with a link to point users to more sync and personalization settings">
-    For more settings that relate to privacy, security, and data collection, see <ph name="BEGIN_LINK">&lt;a href=&quot;chrome://settings/syncSetup&quot;&gt;<ex>&lt;a href=&quot;chrome://settings/syncSetup&quot;&gt;</ex></ph>Sync and personalization<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
-  </message>
-  <message name="IDS_SETTINGS_PRIVACY_MORE_SETTINGS_UNIFIED_CONSENT" desc="The text with a link to point users to Sync and Google services settings">
-    For more settings that relate to privacy, security, and data collection, see <ph name="BEGIN_LINK">&lt;a href=&quot;chrome://settings/syncSetup&quot;&gt;<ex>&lt;a href=&quot;chrome://settings/syncSetup&quot;&gt;</ex></ph>Sync and Google services<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
+  <message name="IDS_SETTINGS_SYNC_AND_GOOGLE_SERVICES_PRIVACY_DESC_UNIFIED_CONSENT" desc="The description of the 'Sync and Google services' row in the privacy section">
+    More settings that relate to privacy, security, and data collection
   </message>
 
   <!-- Reset Settings Page -->
diff --git a/chrome/app/theme/chromium/BRANDING b/chrome/app/theme/chromium/BRANDING
index 17a31784..26918a1 100644
--- a/chrome/app/theme/chromium/BRANDING
+++ b/chrome/app/theme/chromium/BRANDING
@@ -4,7 +4,7 @@
 PRODUCT_SHORTNAME=Chromium
 PRODUCT_INSTALLER_FULLNAME=Chromium Installer
 PRODUCT_INSTALLER_SHORTNAME=Chromium Installer
-COPYRIGHT=Copyright 2018 The Chromium Authors. All rights reserved.
+COPYRIGHT=Copyright 2019 The Chromium Authors. All rights reserved.
 MAC_BUNDLE_ID=org.chromium.Chromium
 MAC_CREATOR_CODE=Cr24
 MAC_TEAM_ID=
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a983bbf..b213cb4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -300,6 +300,8 @@
     "command_updater_delegate.h",
     "command_updater_impl.cc",
     "command_updater_impl.h",
+    "complex_tasks/task_tab_helper.cc",
+    "complex_tasks/task_tab_helper.h",
     "component_updater/chrome_component_updater_configurator.cc",
     "component_updater/chrome_component_updater_configurator.h",
     "component_updater/component_updater_prefs.cc",
@@ -1939,8 +1941,9 @@
     "//services/device/public/mojom",
     "//services/identity:lib",
     "//services/identity/public/cpp",
-    "//services/image_annotation/public/cpp:cpp",
-    "//services/image_annotation/public/mojom:mojom",
+    "//services/image_annotation:lib",
+    "//services/image_annotation/public/cpp",
+    "//services/image_annotation/public/mojom",
     "//services/metrics/public/cpp:ukm_builders",
     "//services/network:network_service",
     "//services/network/public/cpp",
@@ -2179,6 +2182,8 @@
       "android/explore_sites/blacklist_site_task.h",
       "android/explore_sites/catalog.cc",
       "android/explore_sites/catalog.h",
+      "android/explore_sites/clear_activities_task.cc",
+      "android/explore_sites/clear_activities_task.h",
       "android/explore_sites/clear_catalog_task.cc",
       "android/explore_sites/clear_catalog_task.h",
       "android/explore_sites/explore_sites_bridge.cc",
@@ -2391,8 +2396,6 @@
       "android/tab_state.h",
       "android/tab_web_contents_delegate_android.cc",
       "android/tab_web_contents_delegate_android.h",
-      "android/tasks/task_tab_helper.cc",
-      "android/tasks/task_tab_helper.h",
       "android/thumbnail/scoped_ptr_expiring_cache.h",
       "android/thumbnail/thumbnail.cc",
       "android/thumbnail/thumbnail.h",
@@ -2401,6 +2404,10 @@
       "android/trusted_cdn.cc",
       "android/trusted_cdn.h",
       "android/url_utilities.cc",
+      "android/usage_stats/usage_stats_bridge.cc",
+      "android/usage_stats/usage_stats_bridge.h",
+      "android/usage_stats/usage_stats_database.cc",
+      "android/usage_stats/usage_stats_database.h",
       "android/usb/web_usb_chooser_android.cc",
       "android/usb/web_usb_chooser_android.h",
       "android/warmup_manager.cc",
@@ -2605,15 +2612,6 @@
         "supervised_user/child_accounts/child_account_service_android.h",
       ]
     }
-
-    if (enable_usage_stats) {
-      sources += [
-        "android/usage_stats/usage_stats_bridge.cc",
-        "android/usage_stats/usage_stats_bridge.h",
-        "android/usage_stats/usage_stats_database.cc",
-        "android/usage_stats/usage_stats_database.h",
-      ]
-    }
   } else {  # !is_android
     sources += [
       "accessibility/invert_bubble_prefs.cc",
@@ -5523,8 +5521,6 @@
   sources = [
     "signin/token_revoker_test_utils.cc",
     "signin/token_revoker_test_utils.h",
-    "ui/webui/signin/login_ui_test_utils.cc",
-    "ui/webui/signin/login_ui_test_utils.h",
   ]
 
   deps = [
@@ -5546,6 +5542,8 @@
     sources += [
       "password_manager/password_manager_test_base.cc",
       "password_manager/password_manager_test_base.h",
+      "ui/webui/signin/login_ui_test_utils.cc",
+      "ui/webui/signin/login_ui_test_utils.h",
       "ui/webui/web_ui_test_handler.cc",
       "ui/webui/web_ui_test_handler.h",
     ]
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS
index e3d8f02d..2188813 100644
--- a/chrome/browser/OWNERS
+++ b/chrome/browser/OWNERS
@@ -18,7 +18,6 @@
 
 per-file browser_close_manager_browsertest.cc=creis@chromium.org
 
-per-file browser_navigator_browsertest.*=alexmos@chromium.org
 per-file browser_navigator_browsertest.*=file://content/OWNERS
 
 per-file certificate_manager_model*=mattm@chromium.org
@@ -33,10 +32,8 @@
 per-file chrome_device_client*=reillyg@chromium.org
 per-file chrome_device_client*=rockot@google.com
 
-per-file chrome_navigation_browsertest.cc=alexmos@chromium.org
 per-file chrome_navigation_browsertest.cc=file://content/OWNERS
 
-per-file chrome_security_exploit_browsertest.cc=alexmos@chromium.org
 per-file chrome_security_exploit_browsertest.cc=file://content/OWNERS
 
 per-file chrome_service_worker_browsertest.cc=file://content/browser/service_worker/OWNERS
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8ff0a56e..88e0b4a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1324,9 +1324,6 @@
      flag_descriptions::kExtensionsOnChromeUrlsDescription, kOsAll,
      SINGLE_VALUE_TYPE(extensions::switches::kExtensionsOnChromeURLs)},
 #endif  // ENABLE_EXTENSIONS
-    {"enable-fast-unload", flag_descriptions::kFastUnloadName,
-     flag_descriptions::kFastUnloadDescription, kOsAll,
-     SINGLE_VALUE_TYPE(switches::kEnableFastUnload)},
     {"enable-history-entry-requires-user-gesture",
      flag_descriptions::kHistoryRequiresUserGestureName,
      flag_descriptions::kHistoryRequiresUserGestureDescription, kOsAll,
@@ -3196,6 +3193,9 @@
      flag_descriptions::kEnableVirtualKeyboardUkmName,
      flag_descriptions::kEnableVirtualKeyboardUkmDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kEnableVirtualKeyboardUkm)},
+    {"handwriting-gesture", flag_descriptions::kHandwritingGestureName,
+     flag_descriptions::kHandwritingGestureDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kHandwritingGesture)},
 #endif  // OS_CHROMEOS
 
 #if !defined(OS_ANDROID)
@@ -3547,13 +3547,6 @@
      flag_descriptions::kBundledConnectionHelpDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kBundledConnectionHelpFeature)},
 
-#if defined(OS_CHROMEOS)
-    {"enable-experimental-crostini-ui",
-     flag_descriptions::kExperimentalCrostiniUIName,
-     flag_descriptions::kExperimentalCrostiniUIDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kExperimentalCrostiniUI)},
-#endif  // OS_CHROMEOS
-
 #if defined(OS_ANDROID)
     {"enable-omnibox-voice-search-always-visible",
      flag_descriptions::kOmniboxVoiceSearchAlwaysVisibleName,
@@ -4340,13 +4333,6 @@
       channel == version_info::Channel::STABLE) {
     return true;
   }
-
-  // enable-experimental-crostini-ui is only available for boards that have
-  // VM support, which is controlled by the Crostini feature.
-  if (!strcmp("enable-experimental-crostini-ui", entry.internal_name) &&
-      !base::FeatureList::IsEnabled(features::kCrostini)) {
-    return true;
-  }
 #endif  // defined(OS_CHROMEOS)
 
   // data-reduction-proxy-lo-fi and enable-data-reduction-proxy-lite-page
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index e1375279..8e21440 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -69,6 +69,7 @@
     &features::kIncognitoStrings,
     &features::kMaterialDesignIncognitoNTP,
     &features::kPermissionDelegation,
+    &features::kPredictivePrefetchingAllowedOnAllConnectionTypes,
     &features::kServiceWorkerPaymentApps,
     &features::kShowTrustedPublisherURL,
     &features::kSoundContentSetting,
@@ -93,6 +94,7 @@
     &kCCTModule,
     &kCCTModuleCache,
     &kCCTModuleCustomHeader,
+    &kCCTModuleCustomRequestHeader,
     &kCCTModulePostMessage,
     &kCCTPostMessageAPI,
     &kCCTRedirectPreconnect,
@@ -241,6 +243,9 @@
 const base::Feature kCCTModuleCustomHeader{"CCTModuleCustomHeader",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kCCTModuleCustomRequestHeader{
+    "CCTModuleCustomRequestHeader", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCCTModulePostMessage{"CCTModulePostMessage",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 9525997..bd4d5bf3 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -27,6 +27,7 @@
 extern const base::Feature kCCTModule;
 extern const base::Feature kCCTModuleCache;
 extern const base::Feature kCCTModuleCustomHeader;
+extern const base::Feature kCCTModuleCustomRequestHeader;
 extern const base::Feature kCCTModulePostMessage;
 extern const base::Feature kCCTPostMessageAPI;
 extern const base::Feature kCCTRedirectPreconnect;
diff --git a/chrome/browser/android/explore_sites/clear_activities_task.cc b/chrome/browser/android/explore_sites/clear_activities_task.cc
new file mode 100644
index 0000000..8ab0364
--- /dev/null
+++ b/chrome/browser/android/explore_sites/clear_activities_task.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/explore_sites/clear_activities_task.h"
+
+#include "chrome/browser/android/explore_sites/explore_sites_schema.h"
+#include "components/offline_pages/core/offline_store_utils.h"
+#include "sql/database.h"
+#include "sql/statement.h"
+
+namespace explore_sites {
+
+namespace {
+
+static const char kClearActivitiesSql[] =
+    "DELETE FROM activity WHERE time >= ? AND time < ?";
+
+bool ClearActivitiesTaskSync(base::Time begin,
+                             base::Time end,
+                             sql::Database* db) {
+  if (!db)
+    return false;
+
+  sql::Statement statement(
+      db->GetCachedStatement(SQL_FROM_HERE, kClearActivitiesSql));
+  statement.BindInt64(0, offline_pages::store_utils::ToDatabaseTime(begin));
+  statement.BindInt64(1, offline_pages::store_utils::ToDatabaseTime(end));
+  return statement.Run();
+}
+
+}  // namespace
+
+ClearActivitiesTask::ClearActivitiesTask(ExploreSitesStore* store,
+                                         base::Time begin,
+                                         base::Time end,
+                                         BooleanCallback callback)
+    : store_(store),
+      begin_(begin),
+      end_(end),
+      callback_(std::move(callback)),
+      weak_factory_(this) {}
+
+ClearActivitiesTask::~ClearActivitiesTask() = default;
+
+void ClearActivitiesTask::Run() {
+  store_->Execute(base::BindOnce(&ClearActivitiesTaskSync, begin_, end_),
+                  base::BindOnce(&ClearActivitiesTask::DoneExecuting,
+                                 weak_factory_.GetWeakPtr()),
+                  false);
+}
+
+void ClearActivitiesTask::DoneExecuting(bool result) {
+  std::move(callback_).Run(result);
+  TaskComplete();
+}
+
+}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/clear_activities_task.h b/chrome/browser/android/explore_sites/clear_activities_task.h
new file mode 100644
index 0000000..090e9e5
--- /dev/null
+++ b/chrome/browser/android/explore_sites/clear_activities_task.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROME_BROWSER_ANDROID_EXPLORE_SITES_CLEAR_ACTIVITIES_TASK_H_
+#define CHROME_BROWSER_ANDROID_EXPLORE_SITES_CLEAR_ACTIVITIES_TASK_H_
+
+#include "base/time/time.h"
+#include "chrome/browser/android/explore_sites/explore_sites_store.h"
+#include "chrome/browser/android/explore_sites/explore_sites_types.h"
+#include "components/offline_pages/task/task.h"
+
+using offline_pages::Task;
+
+namespace explore_sites {
+
+// Takes a URL that the user has asked us to remove, and adds it to a blacklist
+// of sites we will stop showing in Explore on Sites.
+class ClearActivitiesTask : public Task {
+ public:
+  ClearActivitiesTask(ExploreSitesStore* store,
+                      base::Time begin,
+                      base::Time end,
+                      BooleanCallback callback);
+  ~ClearActivitiesTask() override;
+
+ private:
+  // Task implementation:
+  void Run() override;
+
+  void DoneExecuting(bool result);
+
+  ExploreSitesStore* store_;  // outlives this class.
+  base::Time begin_;
+  base::Time end_;
+  BooleanCallback callback_;
+  base::WeakPtrFactory<ClearActivitiesTask> weak_factory_;
+};
+
+}  // namespace explore_sites
+
+#endif  // CHROME_BROWSER_ANDROID_EXPLORE_SITES_CLEAR_ACTIVITIES_TASK_H_
diff --git a/chrome/browser/android/explore_sites/clear_activities_task_unittest.cc b/chrome/browser/android/explore_sites/clear_activities_task_unittest.cc
new file mode 100644
index 0000000..c79a24a
--- /dev/null
+++ b/chrome/browser/android/explore_sites/clear_activities_task_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/explore_sites/clear_activities_task.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/android/explore_sites/explore_sites_schema.h"
+#include "components/offline_pages/core/offline_store_utils.h"
+#include "components/offline_pages/task/task.h"
+#include "components/offline_pages/task/task_test_base.h"
+#include "sql/database.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using offline_pages::TaskTestBase;
+
+namespace explore_sites {
+
+namespace {
+
+const char kInsertActivitySql[] =
+    "INSERT INTO activity (time, category_type, url) VALUES (?, ?, ?);";
+const char kGetAllActivitiesSql[] =
+    "SELECT time, category_type, url FROM activity";
+const base::Time kJanuary2017 = base::Time::FromDoubleT(1484505871);
+const base::Time kBetweenJanuaryAndJune2017 =
+    base::Time::FromDoubleT(1490000000);
+const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
+const char kUrl1[] = "https://www.google.com";
+const char kUrl2[] = "https://www.example.com/1";
+const char kUrl3[] = "https://www.example.com/here";
+
+struct ActivityInfo {
+  base::Time time;
+  int category_type;
+  std::string url;
+};
+
+}  // namespace
+
+std::vector<ActivityInfo> GetAllActivitiesSync(sql::Database* db) {
+  std::vector<ActivityInfo> result;
+  sql::Statement statement(
+      db->GetCachedStatement(SQL_FROM_HERE, kGetAllActivitiesSql));
+  while (statement.Step()) {
+    base::Time time =
+        offline_pages::store_utils::FromDatabaseTime(statement.ColumnInt64(0));
+    int category_type = statement.ColumnInt(1);
+    std::string url = statement.ColumnString(2);
+    result.push_back({time, category_type, url});
+  }
+  if (!statement.Succeeded())
+    result.clear();
+  return result;
+}
+
+class ClearActivitiesTaskTest : public TaskTestBase {
+ public:
+  ClearActivitiesTaskTest() = default;
+  ~ClearActivitiesTaskTest() override = default;
+
+  void SetUp() override {
+    store_ = std::make_unique<ExploreSitesStore>(task_runner());
+  }
+
+  ExploreSitesStore* store() { return store_.get(); }
+
+  void ExecuteSync(base::RepeatingCallback<bool(sql::Database*)> query) {
+    store()->Execute(base::OnceCallback<bool(sql::Database*)>(query),
+                     base::BindOnce([](bool result) { ASSERT_TRUE(result); }),
+                     false);
+    RunUntilIdle();
+  }
+
+  void PopulateActivities();
+  void ClearActivities(base::Time begin, base::Time end);
+  std::vector<ActivityInfo> GetAllActivities();
+
+  bool callback_called() const { return callback_called_; }
+  bool success() const { return success_; }
+
+ private:
+  void InsertActivity(base::Time time,
+                      int category_type,
+                      const std::string& url);
+  void OnClearActivitiesDone(bool success);
+  void GetAllActivitiesDone(std::vector<ActivityInfo> activities);
+
+  std::unique_ptr<ExploreSitesStore> store_;
+  bool callback_called_ = false;
+  bool success_ = false;
+  std::vector<ActivityInfo> activities_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClearActivitiesTaskTest);
+};
+
+void ClearActivitiesTaskTest::PopulateActivities() {
+  InsertActivity(kJanuary2017, 1, kUrl1);
+  InsertActivity(kBetweenJanuaryAndJune2017, 2, kUrl2);
+  InsertActivity(kJune2017, 3, kUrl3);
+}
+
+void ClearActivitiesTaskTest::InsertActivity(base::Time time,
+                                             int category_type,
+                                             const std::string& url) {
+  ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
+    sql::Statement statement(
+        db->GetCachedStatement(SQL_FROM_HERE, kInsertActivitySql));
+    statement.BindInt64(0, offline_pages::store_utils::ToDatabaseTime(time));
+    statement.BindInt(1, category_type);
+    statement.BindString(2, url);
+    return statement.Run();
+  }));
+}
+
+void ClearActivitiesTaskTest::ClearActivities(base::Time begin,
+                                              base::Time end) {
+  ClearActivitiesTask task(
+      store(), begin, end,
+      base::BindOnce(&ClearActivitiesTaskTest::OnClearActivitiesDone,
+                     base::Unretained(this)));
+  RunTask(&task);
+}
+
+void ClearActivitiesTaskTest::OnClearActivitiesDone(bool success) {
+  success_ = success;
+  callback_called_ = true;
+}
+
+std::vector<ActivityInfo> ClearActivitiesTaskTest::GetAllActivities() {
+  activities_.clear();
+  store()->Execute<std::vector<ActivityInfo>>(
+      base::BindOnce(&GetAllActivitiesSync),
+      base::BindOnce(&ClearActivitiesTaskTest::GetAllActivitiesDone,
+                     base::Unretained(this)),
+      {});
+  RunUntilIdle();
+  return activities_;
+}
+
+void ClearActivitiesTaskTest::GetAllActivitiesDone(
+    std::vector<ActivityInfo> activities) {
+  activities_ = activities;
+}
+
+TEST_F(ClearActivitiesTaskTest, StoreFailure) {
+  store()->SetInitializationStatusForTest(InitializationStatus::FAILURE);
+  ClearActivities(kJanuary2017, kJune2017);
+
+  // A database failure should be completed but return with an error.
+  EXPECT_TRUE(callback_called());
+  EXPECT_FALSE(success());
+}
+
+TEST_F(ClearActivitiesTaskTest, EmptyTime) {
+  PopulateActivities();
+  ClearActivities(base::Time(), base::Time());
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(success());
+
+  // Nothing should be removed.
+  std::vector<ActivityInfo> activities = GetAllActivities();
+  ASSERT_EQ(3u, activities.size());
+}
+
+TEST_F(ClearActivitiesTaskTest, ClearSome) {
+  PopulateActivities();
+  ClearActivities(kJanuary2017, kJune2017);
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(success());
+
+  // Two matching activities are removed and one is left.
+  std::vector<ActivityInfo> activities = GetAllActivities();
+  ASSERT_EQ(1u, activities.size());
+  EXPECT_EQ(kJune2017, activities[0].time);
+  EXPECT_EQ(3, activities[0].category_type);
+  EXPECT_EQ(kUrl3, activities[0].url);
+}
+
+}  // namespace explore_sites
diff --git a/chrome/browser/android/explore_sites/explore_sites_service.h b/chrome/browser/android/explore_sites/explore_sites_service.h
index a346946..8ff60a8 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service.h
+++ b/chrome/browser/android/explore_sites/explore_sites_service.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ANDROID_EXPLORE_SITES_EXPLORE_SITES_SERVICE_H_
 #define CHROME_BROWSER_ANDROID_EXPLORE_SITES_EXPLORE_SITES_SERVICE_H_
 
+#include "base/time/time.h"
 #include "chrome/browser/android/explore_sites/explore_sites_types.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -43,6 +44,11 @@
   // Add the url to the blacklist.
   virtual void BlacklistSite(const std::string& url) = 0;
 
+  // Remove the activity history from the specified time range.
+  virtual void ClearActivities(base::Time begin,
+                               base::Time end,
+                               base::OnceClosure callback) = 0;
+
   // Controls for use by chrome://explore-sites-internals.
   virtual void ClearCachedCatalogsForDebugging() = 0;
   virtual void OverrideCountryCodeForDebugging(
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
index 4fd021f..843ae79 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/explore_sites/blacklist_site_task.h"
 #include "chrome/browser/android/explore_sites/catalog.pb.h"
+#include "chrome/browser/android/explore_sites/clear_activities_task.h"
 #include "chrome/browser/android/explore_sites/clear_catalog_task.h"
 #include "chrome/browser/android/explore_sites/explore_sites_bridge.h"
 #include "chrome/browser/android/explore_sites/explore_sites_feature.h"
@@ -133,6 +134,16 @@
   // TODO(https://crbug.com/893845): Remove cached category icon if affected.
 }
 
+void ExploreSitesServiceImpl::ClearActivities(base::Time begin,
+                                              base::Time end,
+                                              base::OnceClosure callback) {
+  task_queue_.AddTask(std::make_unique<ClearActivitiesTask>(
+      explore_sites_store_.get(), begin, end,
+      base::BindOnce(
+          [](base::OnceClosure callback, bool) { std::move(callback).Run(); },
+          std::move(callback))));
+}
+
 void ExploreSitesServiceImpl::ClearCachedCatalogsForDebugging() {
   task_queue_.AddTask(std::make_unique<ClearCatalogTask>(
       explore_sites_store_.get(), base::BindOnce([](bool result) {})));
diff --git a/chrome/browser/android/explore_sites/explore_sites_service_impl.h b/chrome/browser/android/explore_sites/explore_sites_service_impl.h
index c35c338f..7484ca5 100644
--- a/chrome/browser/android/explore_sites/explore_sites_service_impl.h
+++ b/chrome/browser/android/explore_sites/explore_sites_service_impl.h
@@ -51,6 +51,9 @@
                                 const std::string& accept_languages,
                                 BooleanCallback callback) override;
   void BlacklistSite(const std::string& url) override;
+  void ClearActivities(base::Time begin,
+                       base::Time end,
+                       base::OnceClosure callback) override;
   void ClearCachedCatalogsForDebugging() override;
   void OverrideCountryCodeForDebugging(
       const std::string& country_code) override;
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_config.h b/chrome/browser/android/oom_intervention/oom_intervention_config.h
index 0e61b06..bc7aa0f 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_config.h
+++ b/chrome/browser/android/oom_intervention/oom_intervention_config.h
@@ -29,6 +29,11 @@
   // navigated.
   bool is_navigate_ads_enabled() const { return is_navigate_ads_enabled_; }
 
+  // True if on detection of near OOM condition V8 memory should be purged.
+  bool is_purge_v8_memory_enabled() const {
+    return is_purge_v8_memory_enabled_;
+  }
+
   // True if detection should be enabled on renderers.
   bool should_detect_in_renderer() const { return should_detect_in_renderer_; }
 
@@ -49,6 +54,7 @@
 
   bool is_renderer_pause_enabled_ = false;
   bool is_navigate_ads_enabled_ = false;
+  bool is_purge_v8_memory_enabled_ = false;
   bool should_detect_in_renderer_ = false;
 
   uint64_t swapfree_threshold_ = 0;
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
index 1cf0cc5..9fed7827 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
@@ -78,7 +78,8 @@
 void OomInterventionTabHelper::OnHighMemoryUsage() {
   auto* config = OomInterventionConfig::GetInstance();
   if (config->is_renderer_pause_enabled() ||
-      config->is_navigate_ads_enabled()) {
+      config->is_navigate_ads_enabled() ||
+      config->is_purge_v8_memory_enabled()) {
     NearOomReductionInfoBar::Show(web_contents(), this);
     intervention_state_ = InterventionState::UI_SHOWN;
     if (!last_navigation_timestamp_.is_null()) {
@@ -290,8 +291,11 @@
   auto* config = OomInterventionConfig::GetInstance();
   bool renderer_pause_enabled = config->is_renderer_pause_enabled();
   bool navigate_ads_enabled = config->is_navigate_ads_enabled();
+  bool purge_v8_memory_enabled = config->is_purge_v8_memory_enabled();
 
-  if ((renderer_pause_enabled || navigate_ads_enabled) && decider_) {
+  if ((renderer_pause_enabled || navigate_ads_enabled ||
+       purge_v8_memory_enabled) &&
+      decider_) {
     DCHECK(!web_contents()->GetBrowserContext()->IsOffTheRecord());
     const std::string& host = web_contents()->GetVisibleURL().host();
     if (!decider_->CanTriggerIntervention(host)) {
@@ -311,7 +315,8 @@
   blink::mojom::DetectionArgsPtr detection_args =
       config->GetRendererOomDetectionArgs();
   intervention_->StartDetection(std::move(host), std::move(detection_args),
-                                renderer_pause_enabled, navigate_ads_enabled);
+                                renderer_pause_enabled, navigate_ads_enabled,
+                                purge_v8_memory_enabled);
 }
 
 void OomInterventionTabHelper::OnNearOomDetected() {
diff --git a/chrome/browser/android/preferences/prefs.h b/chrome/browser/android/preferences/prefs.h
index 3c60031..315ad6f 100644
--- a/chrome/browser/android/preferences/prefs.h
+++ b/chrome/browser/android/preferences/prefs.h
@@ -35,6 +35,7 @@
   CONTEXTUAL_SEARCH_ENABLED,
   AUTOFILL_PROFILE_ENABLED,
   AUTOFILL_CREDIT_CARD_ENABLED,
+  USAGE_STATS_ENABLED,
   // PREF_NUM_PREFS must be the last entry.
   PREF_NUM_PREFS
 };
@@ -60,6 +61,8 @@
     payments::kCanMakePaymentEnabled,
     prefs::kContextualSearchEnabled,
     autofill::prefs::kAutofillProfileEnabled,
-    autofill::prefs::kAutofillCreditCardEnabled};
+    autofill::prefs::kAutofillCreditCardEnabled,
+    prefs::kUsageStatsEnabled,
+};
 
 #endif  // CHROME_BROWSER_ANDROID_PREFERENCES_PREFS_H_
diff --git a/chrome/browser/android/preferences/prefs_unittest.cc b/chrome/browser/android/preferences/prefs_unittest.cc
index 8a9064e..2cb2f459 100644
--- a/chrome/browser/android/preferences/prefs_unittest.cc
+++ b/chrome/browser/android/preferences/prefs_unittest.cc
@@ -61,6 +61,7 @@
             GetPrefName(AUTOFILL_PROFILE_ENABLED));
   EXPECT_EQ(autofill::prefs::kAutofillCreditCardEnabled,
             GetPrefName(AUTOFILL_CREDIT_CARD_ENABLED));
+  EXPECT_EQ(prefs::kUsageStatsEnabled, GetPrefName(USAGE_STATS_ENABLED));
 
   // If this check fails, a pref is missing a test case above.
   EXPECT_EQ(Pref::PREF_NUM_PREFS, pref_count_);
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index b7578ad..d86a4d2a 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -563,10 +563,10 @@
       jembedder = ConvertUTF8ToJavaString(env, embedder);
 
     ScopedJavaLocalRef<jstring> jname =
-        ConvertUTF8ToJavaString(env, context->GetObjectName(object->object));
+        ConvertUTF8ToJavaString(env, context->GetObjectName(object->value));
 
     std::string serialized;
-    bool written = base::JSONWriter::Write(object->object, &serialized);
+    bool written = base::JSONWriter::Write(object->value, &serialized);
     DCHECK(written);
     ScopedJavaLocalRef<jstring> jserialized =
         ConvertUTF8ToJavaString(env, serialized);
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.cc b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
index 977c569..8f916eba 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
@@ -9,8 +9,13 @@
 #include "chrome/browser/android/usage_stats/usage_stats_database.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "jni/UsageStatsBridge_jni.h"
 
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+
 namespace usage_stats {
 
 static jlong JNI_UsageStatsBridge_Init(JNIEnv* env,
@@ -88,4 +93,11 @@
                                         const JavaRef<jobject>& j_this,
                                         const JavaRef<jobject>& j_mappings,
                                         const JavaRef<jobject>& j_callback) {}
+
+// static
+void UsageStatsBridge::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterBooleanPref(prefs::kUsageStatsEnabled, false);
+}
+
 }  // namespace usage_stats
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.h b/chrome/browser/android/usage_stats/usage_stats_bridge.h
index 5fa0417..70a12ac7 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.h
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.h
@@ -10,6 +10,10 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/weak_ptr.h"
 
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 namespace usage_stats {
 
 using base::android::JavaParamRef;
@@ -77,6 +81,8 @@
                         const JavaRef<jobject>& j_mappings,
                         const JavaRef<jobject>& j_callback);
 
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
  private:
   std::unique_ptr<UsageStatsDatabase> usage_stats_database_;
 
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.cc b/chrome/browser/android/usage_stats/usage_stats_database.cc
index db9b2e5..ec93d864 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_database.cc
@@ -6,7 +6,7 @@
 
 #include "base/task/post_task.h"
 #include "components/leveldb_proto/content/proto_database_provider_factory.h"
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace usage_stats {
 
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.h b/chrome/browser/android/usage_stats/usage_stats_database.h
index 101538d..9faf652 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.h
+++ b/chrome/browser/android/usage_stats/usage_stats_database.h
@@ -15,7 +15,7 @@
 #include "base/macros.h"
 #include "chrome/browser/android/usage_stats/website_event.pb.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace usage_stats {
 
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index e767e54..26d3bd7 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -53,7 +53,6 @@
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/sync/sync_ui_util.h"
@@ -100,7 +99,6 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
 #include "components/sessions/core/tab_restore_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
diff --git a/chrome/browser/app_controller_mac_unittest.mm b/chrome/browser/app_controller_mac_unittest.mm
index 3c9ba28..565c194a 100644
--- a/chrome/browser/app_controller_mac_unittest.mm
+++ b/chrome/browser/app_controller_mac_unittest.mm
@@ -14,7 +14,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/pref_names.h"
@@ -24,7 +23,6 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_sync/profile_sync_service_mock.h"
 #include "components/signin/core/browser/signin_error_controller.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/platform_test.h"
 
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
index a75fd9c..9b8c7da 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
@@ -792,16 +792,23 @@
 void ExtensionAppShimHandler::OnBrowserAdded(Browser* browser) {}
 
 void ExtensionAppShimHandler::OnBrowserRemoved(Browser* browser) {
-  const Extension* extension = MaybeGetAppForBrowser(browser);
-  if (!extension)
-    return;
-
-  AppBrowserMap::iterator it = app_browser_windows_.find(extension->id());
-  if (it != app_browser_windows_.end()) {
+  // Note that |browser| may no longer have an extension, if it was unloaded
+  // before |browser| was closed. Search for |browser| in all extensions in
+  // |app_browser_windows_|.
+  for (auto it = app_browser_windows_.begin(); it != app_browser_windows_.end();
+       ++it) {
+    const std::string& extension_id = it->first;
     BrowserSet& browsers = it->second;
-    browsers.erase(browser);
-    if (browsers.empty())
-      OnAppDeactivated(browser->profile(), extension->id());
+    auto found = browsers.find(browser);
+    if (found == browsers.end())
+      continue;
+
+    browsers.erase(found);
+    if (browsers.empty()) {
+      OnAppDeactivated(browser->profile(), extension_id);
+      app_browser_windows_.erase(it);
+    }
+    return;
   }
 }
 
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 6137b4cf..2b5629f8 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -259,7 +259,6 @@
       base::StringPrintf(
           "MAP *:80 127.0.0.1:%d,"
           "MAP *:443 127.0.0.1:%d,"
-
           // Uncomment to use the live autofill prediction server.
           // "EXCLUDE clients1.google.com,"
           "EXCLUDE localhost",
diff --git a/chrome/browser/autofill/captured_sites_test_utils.h b/chrome/browser/autofill/captured_sites_test_utils.h
index 9ea6f64..a7c964c9 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.h
+++ b/chrome/browser/autofill/captured_sites_test_utils.h
@@ -31,7 +31,7 @@
 //    user action.
 const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30);
 // The amount of time to wait for a page to trigger a paint in response to a
-// an ation. The Captured Site Automation Framework uses this timeout to
+// an action. The Captured Site Automation Framework uses this timeout to
 // break out of a wait loop after a hover action.
 const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(20);
 
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
index a8b5da1..3f10e01 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.cc
@@ -124,12 +124,15 @@
 
   if (ShouldReportProgressBySize()) {
     offline_item.progress.value = GetProcessedBytes();
-    // If we have completed all downloads, update progress max to
-    // |downloaded_bytes| in case |download_total_bytes| was set too high. This
-    // avoid unnecessary jumping in the progress bar.
-    offline_item.progress.max = job_state == State::kDownloadsComplete
-                                    ? fetch_description->downloaded_bytes
-                                    : fetch_description->download_total_bytes;
+    // If we have completed all downloads, update progress max to the processed
+    // bytes in case the provided totals were set too high. This avoids
+    // unnecessary jumping in the progress bar.
+    uint64_t completed_bytes =
+        fetch_description->downloaded_bytes + fetch_description->uploaded_bytes;
+    uint64_t total_bytes = fetch_description->download_total_bytes +
+                           fetch_description->upload_total_bytes;
+    offline_item.progress.max =
+        job_state == State::kDownloadsComplete ? completed_bytes : total_bytes;
   } else {
     offline_item.progress.value = fetch_description->completed_requests;
     offline_item.progress.max = fetch_description->total_requests;
@@ -168,13 +171,23 @@
 }
 
 uint64_t BackgroundFetchDelegateImpl::JobDetails::GetProcessedBytes() const {
-  return fetch_description->downloaded_bytes + GetInProgressBytes();
+  return fetch_description->downloaded_bytes +
+         fetch_description->uploaded_bytes + GetInProgressBytes();
+}
+
+uint64_t BackgroundFetchDelegateImpl::JobDetails::GetDownloadedBytes() const {
+  uint64_t bytes = fetch_description->downloaded_bytes;
+  for (const auto& current_fetch : current_fetch_guids)
+    bytes += current_fetch.second.in_progress_downloaded_bytes;
+  return bytes;
 }
 
 uint64_t BackgroundFetchDelegateImpl::JobDetails::GetInProgressBytes() const {
   uint64_t bytes = 0u;
-  for (const auto& current_fetch : current_fetch_guids)
-    bytes += current_fetch.second.in_progress_downloaded_bytes;
+  for (const auto& current_fetch : current_fetch_guids) {
+    bytes += current_fetch.second.in_progress_downloaded_bytes +
+             current_fetch.second.in_progress_uploaded_bytes;
+  }
   return bytes;
 }
 
@@ -203,7 +216,7 @@
 
   if (fetch_description->completed_requests <
           fetch_description->total_requests &&
-      GetProcessedBytes() > fetch_description->download_total_bytes) {
+      GetDownloadedBytes() > fetch_description->download_total_bytes) {
     // |download_total_bytes| was set too low.
     return false;
   }
@@ -476,7 +489,7 @@
                                     bytes_downloaded);
   if (job_details.fetch_description->download_total_bytes &&
       job_details.fetch_description->download_total_bytes <
-          job_details.GetProcessedBytes()) {
+          job_details.GetDownloadedBytes()) {
     // Fail the fetch if total download size was set too low.
     // We only do this if total download size is specified. If not specified,
     // this check is skipped. This is to allow for situations when the
diff --git a/chrome/browser/background_fetch/background_fetch_delegate_impl.h b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
index b29c7e5..8fe434c 100644
--- a/chrome/browser/background_fetch/background_fetch_delegate_impl.h
+++ b/chrome/browser/background_fetch/background_fetch_delegate_impl.h
@@ -166,6 +166,10 @@
     // far.
     uint64_t GetProcessedBytes() const;
 
+    // Returns the number of downloaded bytes, including for the in progress
+    // requests.
+    uint64_t GetDownloadedBytes() const;
+
     void UpdateInProgressBytes(const std::string& download_guid,
                                uint64_t bytes_uploaded,
                                uint64_t bytes_downloaded);
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 900f913..dfe53a4 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -100,6 +100,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/customtabs/origin_verifier.h"
+#include "chrome/browser/android/explore_sites/explore_sites_service_factory.h"
 #include "chrome/browser/android/feed/feed_lifecycle_bridge.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h"
 #include "chrome/browser/android/search_permissions/search_permissions_service.h"
@@ -590,6 +591,16 @@
     }
 
     device_event_log::Clear(delete_begin_, delete_end_);
+
+#if defined(OS_ANDROID)
+    explore_sites::ExploreSitesService* explore_sites_service =
+        explore_sites::ExploreSitesServiceFactory::GetForBrowserContext(
+            profile_);
+    if (explore_sites_service) {
+      explore_sites_service->ClearActivities(
+          delete_begin_, delete_end_, CreatePendingTaskCompletionClosure());
+    }
+#endif
   }
 
   //////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc b/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc
index 52901d8..43686091 100644
--- a/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/counters/sync_aware_counter_browsertest.cc
@@ -39,6 +39,7 @@
     fake_web_history_service_ =
         std::make_unique<history::FakeWebHistoryService>();
     run_loop_.reset(new base::RunLoop());
+    SyncTest::SetUpOnMainThread();
   }
 
   history::WebHistoryService* GetFakeWebHistoryService(Profile* profile) {
diff --git a/chrome/browser/chrome_browser_main_android.cc b/chrome/browser/chrome_browser_main_android.cc
index d92ad08e..0195759 100644
--- a/chrome/browser/chrome_browser_main_android.cc
+++ b/chrome/browser/chrome_browser_main_android.cc
@@ -13,11 +13,9 @@
 #include "chrome/browser/android/preferences/clipboard_android.h"
 #include "chrome/browser/android/seccomp_support_detector.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "components/crash/content/browser/child_exit_observer_android.h"
 #include "components/crash/content/browser/child_process_crash_observer_android.h"
 #include "components/metrics/stability_metrics_helper.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/main_function_params.h"
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index c60af7c0..ea96e17 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -219,11 +219,44 @@
   UMA_HISTOGRAM_COUNTS_100("OSX.OtherInstances.OtherUser", other_user_count);
 }
 
+// Used for UMA; never alter existing values.
+enum class FastUserSwitchEvent {
+  kUserDidBecomeActiveEvent,
+  kUserDidBecomeInactiveEvent,
+  kMaxValue = kUserDidBecomeInactiveEvent,
+};
+
+void LogFastUserSwitchStat(FastUserSwitchEvent event) {
+  UMA_HISTOGRAM_ENUMERATION("OSX.FastUserSwitch", event);
+}
+
+void InstallFastUserSwitchStatRecorder() {
+  NSNotificationCenter* notification_center =
+      [[NSWorkspace sharedWorkspace] notificationCenter];
+  [notification_center
+      addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
+                  object:nil
+                   queue:nil
+              usingBlock:^(NSNotification* note) {
+                LogFastUserSwitchStat(
+                    FastUserSwitchEvent::kUserDidBecomeActiveEvent);
+              }];
+  [notification_center
+      addObserverForName:NSWorkspaceSessionDidResignActiveNotification
+                  object:nil
+                   queue:nil
+              usingBlock:^(NSNotification* note) {
+                LogFastUserSwitchStat(
+                    FastUserSwitchEvent::kUserDidBecomeInactiveEvent);
+              }];
+}
+
 // Records various bits of information about the local Chromium installation in
 // UMA.
 void RecordInstallationStats() {
   RecordFilesystemStats();
   RecordInstanceStats();
+  InstallFastUserSwitchStatRecorder();
 }
 
 }  // namespace
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 24aa453..7e944ee 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5146,6 +5146,7 @@
     content::NavigationHandle* navigation_handle,
     const GURL& current_navigation_url) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!navigation_handle->HasCommitted());
 
   // If this is not a main frame, return the initial state. If there are no
   // previews in the state, return the state as is.
@@ -5415,3 +5416,14 @@
 std::string ChromeContentBrowserClient::GetUserAgent() const {
   return ::GetUserAgent();
 }
+
+bool ChromeContentBrowserClient::IsBuiltinComponent(
+    content::BrowserContext* browser_context,
+    const url::Origin& origin) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  return ChromeContentBrowserClientExtensionsPart::IsBuiltinComponent(
+      browser_context, origin);
+#else
+  return false;
+#endif
+}
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index c52eed2f..ef44018 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -553,6 +553,9 @@
   std::string GetProduct() const override;
   std::string GetUserAgent() const override;
 
+  bool IsBuiltinComponent(content::BrowserContext* browser_context,
+                          const url::Origin& origin) override;
+
   // Determines the committed previews state for the passed in params.
   static content::PreviewsState DetermineCommittedPreviewsForURL(
       const GURL& url,
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index 82c4fa6..af7cfecb 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -226,6 +226,31 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
+                       StartServiceWorkerAndDispatchMessage) {
+  base::RunLoop run_loop;
+  blink::TransferableMessage msg;
+  const base::string16 message_data = base::UTF8ToUTF16("testMessage");
+
+  WriteFile(FILE_PATH_LITERAL("sw.js"), "self.onfetch = function(e) {};");
+  WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
+  InitializeServer();
+  NavigateToPageAndWaitForReadyTitle("/test.html");
+  msg.owned_encoded_message = blink::EncodeStringMessage(message_data);
+  msg.encoded_message = msg.owned_encoded_message;
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(
+          &content::ServiceWorkerContext::StartServiceWorkerAndDispatchMessage,
+          base::Unretained(GetServiceWorkerContext()),
+          embedded_test_server()->GetURL("/scope/"), std::move(msg),
+          base::BindRepeating(&ExpectResultAndRun<bool>, true,
+                              run_loop.QuitClosure())));
+
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                        StartServiceWorkerForLongRunningMessage) {
   base::RunLoop run_loop;
   blink::TransferableMessage msg;
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 28edeee..0005edb 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -531,12 +531,6 @@
   EXPECT_TRUE(PerformAcceleratorAction(ash::TOGGLE_OVERVIEW));
   while (true) {
     std::string utterance = speech_monitor_.GetNextUtterance();
-    if (base::MatchPattern(utterance, "Edit text"))
-      break;
-  }
-
-  while (true) {
-    std::string utterance = speech_monitor_.GetNextUtterance();
     if (utterance == "Entered window overview mode")
       break;
   }
diff --git a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.cc b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.cc
index bb43c57..7916d14 100644
--- a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.cc
+++ b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h"
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/session_manager/core/session_manager.h"
 
 namespace chromeos {
 
@@ -16,9 +17,9 @@
     : context_(context) {
   ArcAppListPrefs* arc_app_prefs = ArcAppListPrefs::Get(context_);
   // arc_app_prefs may not available in some browser tests.
-  if (!arc_app_prefs)
-    return;
-  arc_app_prefs->AddObserver(this);
+  if (arc_app_prefs)
+    arc_app_prefs->AddObserver(this);
+  session_manager::SessionManager::Get()->AddObserver(this);
 }
 
 EventBasedStatusReportingService::~EventBasedStatusReportingService() = default;
@@ -37,12 +38,36 @@
       ->RequestImmediateStatusReport();
 }
 
+void EventBasedStatusReportingService::OnSessionStateChanged() {
+  session_manager::SessionState session_state =
+      session_manager::SessionManager::Get()->session_state();
+
+  // Do not request status report when the device has just started the session
+  // because not all components may be ready yet, which may cause an incomplete
+  // status report.
+  if (session_just_started_ &&
+      session_state == session_manager::SessionState::ACTIVE) {
+    session_just_started_ = false;
+    return;
+  }
+
+  if (session_state == session_manager::SessionState::ACTIVE) {
+    VLOG(1) << "Request status report due to a unlock screen.";
+    ConsumerStatusReportingServiceFactory::GetForBrowserContext(context_)
+        ->RequestImmediateStatusReport();
+  } else if (session_state == session_manager::SessionState::LOCKED) {
+    VLOG(1) << "Request status report due to a lock screen.";
+    ConsumerStatusReportingServiceFactory::GetForBrowserContext(context_)
+        ->RequestImmediateStatusReport();
+  }
+}
+
 void EventBasedStatusReportingService::Shutdown() {
   ArcAppListPrefs* arc_app_prefs = ArcAppListPrefs::Get(context_);
   // arc_app_prefs may not available in some browser tests.
-  if (!arc_app_prefs)
-    return;
-  arc_app_prefs->RemoveObserver(this);
+  if (arc_app_prefs)
+    arc_app_prefs->RemoveObserver(this);
+  session_manager::SessionManager::Get()->RemoveObserver(this);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.h b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.h
index 653f726e..f4a3f22 100644
--- a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.h
+++ b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/session_manager/core/session_manager_observer.h"
 
 namespace content {
 class BrowserContext;
@@ -16,8 +17,10 @@
 namespace chromeos {
 
 // Requests status report when events relevant to supervision features happen.
-class EventBasedStatusReportingService : public KeyedService,
-                                         public ArcAppListPrefs::Observer {
+class EventBasedStatusReportingService
+    : public KeyedService,
+      public ArcAppListPrefs::Observer,
+      public session_manager::SessionManagerObserver {
  public:
   explicit EventBasedStatusReportingService(content::BrowserContext* context);
   ~EventBasedStatusReportingService() override;
@@ -28,11 +31,15 @@
   void OnPackageModified(
       const arc::mojom::ArcPackageInfo& package_info) override;
 
+  // session_manager::SessionManagerObserver:
+  void OnSessionStateChanged() override;
+
  private:
   // KeyedService:
   void Shutdown() override;
 
   content::BrowserContext* const context_;
+  bool session_just_started_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(EventBasedStatusReportingService);
 };
diff --git a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
index ffd6e63..4913682 100644
--- a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
@@ -9,11 +9,14 @@
 #include "base/macros.h"
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h"
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/account_id/account_id.h"
 #include "components/arc/common/app.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/session_manager/core/session_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -54,6 +57,15 @@
   void SetUp() override {
     profile_.SetSupervisedUserId(supervised_users::kChildAccountSUID);
     arc_test_.SetUp(profile());
+
+    session_manager_.CreateSession(
+        account_id(),
+        chromeos::ProfileHelper::GetUserIdHashByUserIdForTesting(
+            account_id().GetUserEmail()),
+        true);
+    session_manager_.SetSessionState(
+        session_manager::SessionState::LOGIN_PRIMARY);
+
     ConsumerStatusReportingServiceFactory::GetInstance()->SetTestingFactory(
         profile(),
         base::BindRepeating(&CreateTestingConsumerStatusReportingService));
@@ -73,12 +85,23 @@
     return test_consumer_status_reporting_service_;
   }
 
+  session_manager::SessionManager* session_manager() {
+    return &session_manager_;
+  }
+
+  AccountId account_id() {
+    return chromeos::ProfileHelper::Get()
+        ->GetUserByProfile(profile())
+        ->GetAccountId();
+  }
+
  private:
   content::TestBrowserThreadBundle thread_bundle_;
   ArcAppTest arc_test_;
   TestingProfile profile_;
   TestingConsumerStatusReportingService*
       test_consumer_status_reporting_service_;
+  session_manager::SessionManager session_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(EventBasedStatusReportingServiceTest);
 };
@@ -103,17 +126,65 @@
       1, test_consumer_status_reporting_service()->performed_status_reports());
 }
 
+TEST_F(EventBasedStatusReportingServiceTest, DoNotReportWhenUserJustSignIn) {
+  EventBasedStatusReportingService service(profile());
+
+  ASSERT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+  EXPECT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+}
+
+TEST_F(EventBasedStatusReportingServiceTest, ReportWhenSessionIsLocked) {
+  EventBasedStatusReportingService service(profile());
+
+  ASSERT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+  EXPECT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
+  EXPECT_EQ(
+      1, test_consumer_status_reporting_service()->performed_status_reports());
+}
+
+TEST_F(EventBasedStatusReportingServiceTest, ReportWhenSessionIsActive) {
+  EventBasedStatusReportingService service(profile());
+
+  ASSERT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+  EXPECT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
+  EXPECT_EQ(
+      1, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+  EXPECT_EQ(
+      2, test_consumer_status_reporting_service()->performed_status_reports());
+}
+
 TEST_F(EventBasedStatusReportingServiceTest, ReportForMultipleEvents) {
   EventBasedStatusReportingService service(profile());
 
   ASSERT_EQ(
       0, test_consumer_status_reporting_service()->performed_status_reports());
-  app_host()->OnPackageAdded(arc::mojom::ArcPackageInfo::New());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
+  EXPECT_EQ(
+      0, test_consumer_status_reporting_service()->performed_status_reports());
+  session_manager()->SetSessionState(session_manager::SessionState::LOCKED);
   EXPECT_EQ(
       1, test_consumer_status_reporting_service()->performed_status_reports());
-  app_host()->OnPackageModified(arc::mojom::ArcPackageInfo::New());
+  session_manager()->SetSessionState(session_manager::SessionState::ACTIVE);
   EXPECT_EQ(
       2, test_consumer_status_reporting_service()->performed_status_reports());
+  app_host()->OnPackageAdded(arc::mojom::ArcPackageInfo::New());
+  EXPECT_EQ(
+      3, test_consumer_status_reporting_service()->performed_status_reports());
+  app_host()->OnPackageModified(arc::mojom::ArcPackageInfo::New());
+  EXPECT_EQ(
+      4, test_consumer_status_reporting_service()->performed_status_reports());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
index 2a0dded1..df7a83d 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/chromeos/child_accounts/screen_time_controller.h"
 
+#include <algorithm>
+#include <string>
+
 #include "ash/public/interfaces/login_screen.mojom.h"
 #include "base/feature_list.h"
 #include "base/optional.h"
@@ -111,9 +114,13 @@
   if (state.is_locked) {
     DCHECK(!state.next_unlock_time.is_null());
     if (!session_manager::SessionManager::Get()->IsScreenLocked()) {
-      VLOG(1) << "Request status report before locking screen.";
-      ConsumerStatusReportingServiceFactory::GetForBrowserContext(context_)
-          ->RequestImmediateStatusReport();
+      // This status report are going to be done in EventBasedStatusReporting if
+      // this feature is enabled.
+      if (!base::FeatureList::IsEnabled(features::kEventBasedStatusReporting)) {
+        VLOG(1) << "Request status report before locking screen.";
+        ConsumerStatusReportingServiceFactory::GetForBrowserContext(context_)
+            ->RequestImmediateStatusReport();
+      }
       ForceScreenLockByPolicy(state.next_unlock_time);
     }
   } else {
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 684f40e..f19a7c7 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/concierge_client.h"
 #include "chromeos/dbus/cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -918,6 +919,13 @@
   request.set_container_name(std::move(container_name));
   request.set_owner_id(owner_id_);
   request.set_async(true);
+  if (base::FeatureList::IsEnabled(chromeos::features::kDriveFs)) {
+    if (auto* integration_service =
+            drive::DriveIntegrationServiceFactory::GetForProfile(profile_)) {
+      request.set_drivefs_mount_path(
+          integration_service->GetMountPointPath().value());
+    }
+  }
   GetCiceroneClient()->StartLxdContainer(
       std::move(request),
       base::BindOnce(&CrostiniManager::OnStartLxdContainer,
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index 181b15e..510b869 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cicerone_client.h"
@@ -180,6 +181,10 @@
     profile_ = std::make_unique<TestingProfile>();
     crostini_manager_ = std::make_unique<CrostiniManager>(profile_.get());
 
+    // Link gaia user for DriveFS.
+    user_manager_.AddUser(AccountId::FromUserEmailGaiaId(
+        profile_->GetProfileUserName(), "12345"));
+
     device::mojom::UsbDeviceManagerPtr fake_usb_manager_ptr_;
     fake_usb_manager_.AddBinding(mojo::MakeRequest(&fake_usb_manager_ptr_));
     crostini_manager_->SetUsbManagerForTesting(
@@ -210,6 +215,7 @@
 
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
+  chromeos::FakeChromeUserManager user_manager_;
   DISALLOW_COPY_AND_ASSIGN(CrostiniManagerTest);
 };
 
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/crostini/crostini_share_path.cc
index 969e0085..7b44725 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.cc
+++ b/chrome/browser/chromeos/crostini/crostini_share_path.cc
@@ -43,7 +43,6 @@
 void OnVmRestartedForSeneschal(
     Profile* profile,
     std::string vm_name,
-    const base::FilePath path,
     base::OnceCallback<void(bool, std::string)> callback,
     vm_tools::seneschal::SharePathRequest request,
     crostini::CrostiniResult result) {
@@ -72,11 +71,12 @@
 }
 
 void LogErrorResult(const std::string& operation,
-                    const base::FilePath& path,
+                    const base::FilePath& cros_path,
+                    const base::FilePath& container_path,
                     bool result,
                     std::string failure_reason) {
   if (!result) {
-    LOG(WARNING) << "Error " << operation << " " << path << ": "
+    LOG(WARNING) << "Error " << operation << " " << cros_path << ": "
                  << failure_reason;
   }
 }
@@ -93,9 +93,12 @@
       std::move(callback_).Run(true, "");
   }
 
-  void Run(base::FilePath path, bool success, std::string failure_reason) {
+  void Run(const base::FilePath& cros_path,
+           const base::FilePath& container_path,
+           bool success,
+           std::string failure_reason) {
     if (!success) {
-      LOG(WARNING) << "Error SharePath=" << path.value()
+      LOG(WARNING) << "Error SharePath=" << cros_path.value()
                    << ", FailureReason=" << failure_reason;
       if (success_) {
         success_ = false;
@@ -135,15 +138,16 @@
   observers_.AddObserver(obs);
 }
 
-void CrostiniSharePath::CallSeneschalSharePath(
-    std::string vm_name,
-    const base::FilePath& path,
-    bool persist,
-    base::OnceCallback<void(bool, std::string)> callback) {
+void CrostiniSharePath::CallSeneschalSharePath(std::string vm_name,
+                                               const base::FilePath& path,
+                                               bool persist,
+                                               SharePathCallback callback) {
+  base::FilePath container_path;
+
   // Verify path is in one of the allowable mount points.
   // This logic is similar to DownloadPrefs::SanitizeDownloadTargetPath().
   if (!path.IsAbsolute() || path.ReferencesParent()) {
-    std::move(callback).Run(false, "Path must be absolute");
+    std::move(callback).Run(container_path, false, "Path must be absolute");
     return;
   }
 
@@ -166,10 +170,15 @@
   base::FilePath removable_media(file_manager::util::kRemovableMediaPath);
   if (my_files == path || my_files.AppendRelativePath(path, &relative_path)) {
     allowed_path = true;
-    request.set_storage_location(
-        base::FeatureList::IsEnabled(chromeos::features::kMyFilesVolume)
-            ? vm_tools::seneschal::SharePathRequest::MY_FILES
-            : vm_tools::seneschal::SharePathRequest::DOWNLOADS);
+    if (base::FeatureList::IsEnabled(chromeos::features::kMyFilesVolume)) {
+      request.set_storage_location(
+          vm_tools::seneschal::SharePathRequest::MY_FILES);
+      container_path = container_path.Append("MyFiles");
+    } else {
+      request.set_storage_location(
+          vm_tools::seneschal::SharePathRequest::DOWNLOADS);
+      container_path = container_path.Append("MyFiles/Downloads");
+    }
     request.set_owner_id(crostini::CryptohomeIdForProfile(profile_));
   } else if (base::FeatureList::IsEnabled(chromeos::features::kDriveFs) &&
              integration_service &&
@@ -191,18 +200,21 @@
       allowed_path = true;
       request.set_storage_location(
           vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE);
+      container_path = container_path.Append("GoogleDrive/MyDrive");
     } else if (team_drives == drivefs_path ||
                team_drives.AppendRelativePath(drivefs_path, &relative_path)) {
       // Team Drives and subdirs.
       allowed_path = true;
       request.set_storage_location(
           vm_tools::seneschal::SharePathRequest::DRIVEFS_TEAM_DRIVES);
+      container_path = container_path.Append("GoogleDrive/TeamDrives");
     } else if (computers == drivefs_path ||
                computers.AppendRelativePath(drivefs_path, &relative_path)) {
       // Computers and subdirs.
       allowed_path = true;
       request.set_storage_location(
           vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS);
+      container_path = container_path.Append("GoogleDrive/Computers");
 
       // TODO(crbug.com/917920): Do not allow Computers Grand Root, or single
       // Computer Root to be shared until DriveFS enforces allowed write paths.
@@ -223,15 +235,17 @@
     allowed_path = true;
     request.set_storage_location(
         vm_tools::seneschal::SharePathRequest::PLAY_FILES);
+    container_path = container_path.Append("PlayFiles");
   } else if (removable_media.AppendRelativePath(path, &relative_path)) {
     // Allow subdirs of /media/removable.
     allowed_path = true;
     request.set_storage_location(
         vm_tools::seneschal::SharePathRequest::REMOVABLE);
+    container_path = container_path.Append("removable");
   }
 
   if (!allowed_path) {
-    std::move(callback).Run(false, "Path is not allowed");
+    std::move(callback).Run(container_path, false, "Path is not allowed");
     return;
   }
 
@@ -245,6 +259,7 @@
 
   request.mutable_shared_path()->set_path(relative_path.value());
   request.mutable_shared_path()->set_writable(true);
+  container_path = container_path.Append(relative_path);
 
   // Restart VM if not currently running.
   auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_);
@@ -254,7 +269,7 @@
     crostini_manager->RestartCrostini(
         vm_name, crostini::kCrostiniDefaultContainerName,
         base::BindOnce(&OnVmRestartedForSeneschal, profile_, std::move(vm_name),
-                       std::move(path), std::move(callback),
+                       base::BindOnce(std::move(callback), container_path),
                        std::move(request)));
     return;
   }
@@ -262,7 +277,8 @@
   request.set_handle(vm_info->info.seneschal_server_handle());
   chromeos::DBusThreadManager::Get()->GetSeneschalClient()->SharePath(
       request,
-      base::BindOnce(&OnSeneschalSharePathResponse, std::move(callback)));
+      base::BindOnce(&OnSeneschalSharePathResponse,
+                     base::BindOnce(std::move(callback), container_path)));
 }
 
 void CrostiniSharePath::CallSeneschalUnsharePath(
@@ -307,17 +323,17 @@
       base::BindOnce(&OnSeneschalUnsharePathResponse, std::move(callback)));
 }
 
-void CrostiniSharePath::SharePath(
-    std::string vm_name,
-    const base::FilePath& path,
-    bool persist,
-    base::OnceCallback<void(bool, std::string)> callback) {
+void CrostiniSharePath::SharePath(std::string vm_name,
+                                  const base::FilePath& path,
+                                  bool persist,
+                                  SharePathCallback callback) {
   DCHECK(callback);
   if (!base::FeatureList::IsEnabled(chromeos::features::kCrostiniFiles)) {
-    std::move(callback).Run(false, "Flag crostini-files not enabled");
+    std::move(callback).Run(path, false, "Flag crostini-files not enabled");
     return;
   }
-  CallSeneschalSharePath(vm_name, path, persist, std::move(callback));
+  CallSeneschalSharePath(std::move(vm_name), path, persist,
+                         std::move(callback));
 }
 
 void CrostiniSharePath::SharePaths(
@@ -325,8 +341,9 @@
     std::vector<base::FilePath> paths,
     bool persist,
     base::OnceCallback<void(bool, std::string)> callback) {
-  base::RepeatingCallback<void(base::FilePath, bool, std::string)> barrier =
-      base::BindRepeating(
+  base::RepeatingCallback<void(const base::FilePath&, const base::FilePath&,
+                               bool, std::string)>
+      barrier = base::BindRepeating(
           &ErrorCapture::Run,
           base::Owned(new ErrorCapture(paths.size(), std::move(callback))));
   for (const auto& path : paths) {
@@ -344,7 +361,7 @@
   base::ListValue* shared_paths = update.Get();
   if (!shared_paths->Remove(base::Value(path.value()), nullptr))
     LOG(WARNING) << "Unshared path not in prefs: " << path.value();
-  CallSeneschalUnsharePath(vm_name, path, std::move(callback));
+  CallSeneschalUnsharePath(std::move(vm_name), path, std::move(callback));
   for (Observer& observer : observers_) {
     observer.OnUnshare(path);
   }
@@ -445,9 +462,10 @@
   auto paths = GetPersistedSharedPaths();
   for (const auto& path : paths) {
     if (path == volume.mount_path() || volume.mount_path().IsParent(path)) {
-      CallSeneschalUnsharePath(kCrostiniDefaultVmName, path,
-                               base::BindOnce(mount_event_seneschal_callback_,
-                                              "unshare-on-unmount", path));
+      CallSeneschalUnsharePath(
+          kCrostiniDefaultVmName, path,
+          base::BindOnce(mount_event_seneschal_callback_, "unshare-on-unmount",
+                         path, path));
     }
   }
 }
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.h b/chrome/browser/chromeos/crostini/crostini_share_path.h
index ed625a0..2599f0c9 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.h
+++ b/chrome/browser/chromeos/crostini/crostini_share_path.h
@@ -24,9 +24,12 @@
 class CrostiniSharePath : public KeyedService,
                           public file_manager::VolumeManagerObserver {
  public:
+  using SharePathCallback =
+      base::OnceCallback<void(const base::FilePath&, bool, std::string)>;
   using MountEventSeneschalCallback =
       base::RepeatingCallback<void(const std::string& operation,
-                                   const base::FilePath& path,
+                                   const base::FilePath& cros_path,
+                                   const base::FilePath& container_path,
                                    bool result,
                                    std::string failure_reason)>;
   class Observer {
@@ -42,12 +45,12 @@
   void AddObserver(Observer* obs);
 
   // Share specified absolute |path| with vm. If |persist| is set, the path will
-  // be automatically shared at container startup. Callback receives success
-  // bool and failure reason string.
+  // be automatically shared at container startup. Callback receives path mapped
+  // in container, success bool and failure reason string.
   void SharePath(std::string vm_name,
                  const base::FilePath& path,
                  bool persist,
-                 base::OnceCallback<void(bool, std::string)> callback);
+                 SharePathCallback callback);
 
   // Share specified absolute |paths| with vm. If |persist| is set, the paths
   // will be automatically shared at container startup. Callback receives
@@ -90,11 +93,10 @@
   }
 
  private:
-  void CallSeneschalSharePath(
-      std::string vm_name,
-      const base::FilePath& path,
-      bool persist,
-      base::OnceCallback<void(bool, std::string)> callback);
+  void CallSeneschalSharePath(std::string vm_name,
+                              const base::FilePath& path,
+                              bool persist,
+                              SharePathCallback callback);
 
   void CallSeneschalUnsharePath(
       std::string vm_name,
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
index f471e99a..01a4123 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_share_path_unittest.cc
@@ -45,8 +45,10 @@
       const vm_tools::seneschal::SharePathRequest::StorageLocation*
           expected_seneschal_storage_location,
       const std::string& expected_seneschal_path,
+      const std::string& expected_container_path,
       Success expected_success,
       const std::string& expected_failure_reason,
+      const base::FilePath& container_path,
       bool success,
       std::string failure_reason) {
     const base::ListValue* prefs =
@@ -73,6 +75,7 @@
                     .shared_path()
                     .path(),
                 expected_seneschal_path);
+      EXPECT_EQ(container_path.value(), expected_container_path);
     }
     EXPECT_EQ(success, expected_success == Success::YES);
     EXPECT_EQ(failure_reason, expected_failure_reason);
@@ -87,18 +90,21 @@
       const vm_tools::seneschal::SharePathRequest::StorageLocation*
           expected_seneschal_storage_location,
       const std::string& expected_seneschal_path,
+      const std::string& expected_container_path,
       Success expected_success,
       const std::string& expected_failure_reason,
       const std::string& operation,
-      const base::FilePath& path,
+      const base::FilePath& cros_path,
+      const base::FilePath& container_path,
       bool success,
       std::string failure_reason) {
     EXPECT_EQ(expected_operation, operation);
-    EXPECT_EQ(expected_path, path);
+    EXPECT_EQ(expected_path, cros_path);
     SharePathCallback(expected_persist, expected_seneschal_client_called,
                       expected_seneschal_storage_location,
-                      expected_seneschal_path, expected_success,
-                      expected_failure_reason, success, failure_reason);
+                      expected_seneschal_path, expected_container_path,
+                      expected_success, expected_failure_reason, container_path,
+                      success, failure_reason);
   }
 
   void SharePersistedPathsCallback(bool success, std::string failure_reason) {
@@ -122,9 +128,9 @@
       const base::FilePath& path,
       Persist expected_persist,
       SeneschalClientCalled expected_seneschal_client_called,
-      std::string expected_seneschal_path,
+      const std::string& expected_seneschal_path,
       Success expected_success,
-      std::string expected_failure_reason,
+      const std::string& expected_failure_reason,
       bool success,
       std::string failure_reason) {
     const base::ListValue* prefs =
@@ -150,16 +156,17 @@
       const base::FilePath& expected_path,
       Persist expected_persist,
       SeneschalClientCalled expected_seneschal_client_called,
-      std::string expected_seneschal_path,
+      const std::string& expected_seneschal_path,
       Success expected_success,
-      std::string expected_failure_reason,
+      const std::string& expected_failure_reason,
       const std::string& operation,
-      const base::FilePath& path,
+      const base::FilePath& cros_path,
+      const base::FilePath& container_path,
       bool success,
       std::string failure_reason) {
     EXPECT_EQ(expected_operation, operation);
-    EXPECT_EQ(expected_path, path);
-    UnsharePathCallback(path, expected_persist,
+    EXPECT_EQ(expected_path, cros_path);
+    UnsharePathCallback(cros_path, expected_persist,
                         expected_seneschal_client_called,
                         expected_seneschal_path, expected_success,
                         expected_failure_reason, success, failure_reason);
@@ -253,7 +260,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "",
-                     Success::YES, ""));
+                     "MyFiles/Downloads", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -269,7 +276,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::MY_FILES, "",
-                     Success::YES, ""));
+                     "MyFiles", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -277,11 +284,11 @@
   features_.InitAndEnableFeature(chromeos::features::kCrostiniFiles);
   crostini_share_path()->SharePath(
       "vm-running", share_path_, PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
-                     base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::YES,
-                     &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                     "path-to-share", Success::YES, ""));
+      base::BindOnce(
+          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          Persist::NO, SeneschalClientCalled::YES,
+          &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "path-to-share",
+          "MyFiles/Downloads/path-to-share", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -289,11 +296,11 @@
   features_.InitAndEnableFeature(chromeos::features::kCrostiniFiles);
   crostini_share_path()->SharePath(
       "vm-running", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
-                     base::Unretained(this), Persist::YES,
-                     SeneschalClientCalled::YES,
-                     &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                     "path-to-share", Success::YES, ""));
+      base::BindOnce(
+          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          Persist::YES, SeneschalClientCalled::YES,
+          &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "path-to-share",
+          "MyFiles/Downloads/path-to-share", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -306,7 +313,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
-                     "my", Success::YES, ""));
+                     "my", "GoogleDrive/MyDrive/my", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -315,10 +322,10 @@
                              {chromeos::features::kDriveFs});
   crostini_share_path()->SharePath(
       "vm-running", drivefs_.Append("root").Append("my"), PERSIST_NO,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
-                     base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "my", Success::NO,
-                     "Path is not allowed"));
+      base::BindOnce(
+          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          Persist::NO, SeneschalClientCalled::NO, nullptr, "my",
+          "GoogleDrive/MyDrive/my", Success::NO, "Path is not allowed"));
   run_loop()->Run();
 }
 
@@ -331,7 +338,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_MY_DRIVE,
-                     "", Success::YES, ""));
+                     "", "GoogleDrive/MyDrive", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -342,7 +349,7 @@
       "vm-running", drivefs_, PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -356,7 +363,7 @@
           &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
           Persist::NO, SeneschalClientCalled::YES,
           &vm_tools::seneschal::SharePathRequest::DRIVEFS_TEAM_DRIVES, "team",
-          Success::YES, ""));
+          "GoogleDrive/TeamDrives/team", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -370,7 +377,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
-                     "pc", Success::YES, ""));
+                     "pc", "GoogleDrive/Computers/pc", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -382,7 +389,7 @@
       "vm-running", drivefs_.Append("Computers"), PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -397,7 +404,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
-                     "pc", Success::YES, ""));
+                     "pc", "GoogleDrive/Computers/pc", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -409,7 +416,7 @@
       "vm-running", drivefs_.Append("Computers").Append("pc"), PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -425,7 +432,8 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::DRIVEFS_COMPUTERS,
-                     "pc/SyncFolder", Success::YES, ""));
+                     "pc/SyncFolder", "GoogleDrive/Computers/pc/SyncFolder",
+                     Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -437,7 +445,7 @@
       PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -450,7 +458,7 @@
                      base::Unretained(this), Persist::NO,
                      SeneschalClientCalled::YES,
                      &vm_tools::seneschal::SharePathRequest::REMOVABLE, "MyUSB",
-                     Success::YES, ""));
+                     "removable/MyUSB", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -460,7 +468,7 @@
       "vm-running", base::FilePath("/media/removable"), PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -479,11 +487,11 @@
 
   crostini_share_path()->SharePath(
       "error-seneschal", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
-                     base::Unretained(this), Persist::YES,
-                     SeneschalClientCalled::YES,
-                     &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                     "path-to-share", Success::NO, "test failure"));
+      base::BindOnce(
+          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          Persist::YES, SeneschalClientCalled::YES,
+          &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "path-to-share",
+          "MyFiles/Downloads/path-to-share", Success::NO, "test failure"));
   run_loop()->Run();
 }
 
@@ -494,7 +502,7 @@
       "vm-running", path, PERSIST_YES,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path must be absolute"));
   run_loop()->Run();
 }
@@ -506,7 +514,7 @@
       "vm-running", path, PERSIST_NO,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path must be absolute"));
   run_loop()->Run();
 }
@@ -518,7 +526,7 @@
       "vm-running", path, PERSIST_YES,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::NO,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "Path is not allowed"));
   run_loop()->Run();
 }
@@ -527,11 +535,11 @@
   features_.InitAndEnableFeature(chromeos::features::kCrostiniFiles);
   crostini_share_path()->SharePath(
       "vm-to-be-started", share_path_, PERSIST_YES,
-      base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
-                     base::Unretained(this), Persist::YES,
-                     SeneschalClientCalled::YES,
-                     &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                     "path-to-share", Success::YES, ""));
+      base::BindOnce(
+          &CrostiniSharePathTest::SharePathCallback, base::Unretained(this),
+          Persist::YES, SeneschalClientCalled::YES,
+          &vm_tools::seneschal::SharePathRequest::DOWNLOADS, "path-to-share",
+          "MyFiles/Downloads/path-to-share", Success::YES, ""));
   run_loop()->Run();
 }
 
@@ -545,7 +553,7 @@
       "error-vm-could-not-be-started", share_path_, PERSIST_YES,
       base::BindOnce(&CrostiniSharePathTest::SharePathCallback,
                      base::Unretained(this), Persist::YES,
-                     SeneschalClientCalled::NO, nullptr, "", Success::NO,
+                     SeneschalClientCalled::NO, nullptr, "", "", Success::NO,
                      "VM could not be started"));
   run_loop()->Run();
 }
@@ -756,7 +764,8 @@
                           base::Unretained(this), "share-on-mount",
                           shared_path_, Persist::NO, SeneschalClientCalled::YES,
                           &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                          "already-shared", Success::YES, ""));
+                          "already-shared", "MyFiles/Downloads/already-shared",
+                          Success::YES, ""));
   crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_downloads_);
   run_loop()->Run();
@@ -773,7 +782,8 @@
                           base::Unretained(this), "share-on-mount",
                           shared_path_, Persist::NO, SeneschalClientCalled::YES,
                           &vm_tools::seneschal::SharePathRequest::DOWNLOADS,
-                          "already-shared", Success::YES, ""));
+                          "already-shared", "MyFiles/Downloads/already-shared",
+                          Success::YES, ""));
   crostini_share_path_->OnVolumeMounted(chromeos::MountError::MOUNT_ERROR_NONE,
                                         *volume_shared_path);
   run_loop()->Run();
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index 03a1671..96c7ff8e 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -256,10 +256,6 @@
   if (!chromeos::ProfileHelper::IsPrimaryProfile(profile)) {
     return false;
   }
-
-  if (!base::FeatureList::IsEnabled(features::kExperimentalCrostiniUI)) {
-    return false;
-  }
   if (check_policy) {
     return IsCrostiniAllowedForProfile(profile);
   }
diff --git a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
index f144c443..7e37be2 100644
--- a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
@@ -113,7 +113,8 @@
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
   static const base::Feature constexpr* kFeatureLookup[] = {
-      &features::kUsbguard, &features::kShillSandboxing};
+      &features::kUsbbouncer, &features::kUsbguard,
+      &features::kShillSandboxing};
 
   dbus::MessageReader reader(method_call);
   std::string feature_name;
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index d2e1b3f..3da98dc0 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -360,9 +360,7 @@
     browser()->profile()->GetPrefs()->SetBoolean(
         crostini::prefs::kCrostiniEnabled, true);
     scoped_feature_list->InitWithFeatures(
-        {features::kCrostini, features::kExperimentalCrostiniUI,
-         chromeos::features::kCrostiniFiles},
-        {});
+        {features::kCrostini, chromeos::features::kCrostiniFiles}, {});
     // Profile must be signed in with email for crostini.
     identity::SetPrimaryAccount(
         IdentityManagerFactory::GetForProfileIfExists(browser()->profile()),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 64624750..b2039eb 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1265,7 +1265,6 @@
   std::vector<base::Feature> disabled_features;
   if (!IsGuestModeTest()) {
     enabled_features.emplace_back(features::kCrostini);
-    enabled_features.emplace_back(features::kExperimentalCrostiniUI);
     enabled_features.emplace_back(chromeos::features::kCrostiniFiles);
   }
   if (IsDriveFsTest()) {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index 0066d824..f780e696 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -193,3 +193,7 @@
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, Crostini) {
   RunGeneratedTest("/background/js/crostini_unittest.html");
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ListContainer) {
+  RunGeneratedTest("/foreground/js/ui/list_container_unittest.html");
+}
diff --git a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
index fb29019e..9e04b2d 100644
--- a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
@@ -73,4 +73,9 @@
   StartTest();
 }
 
+IN_PROC_BROWSER_TEST_F(VideoPlayerBrowserTest, NativeMediaKey) {
+  set_test_case_name("mediaKeyNative");
+  StartTest();
+}
+
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_metrics.h b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_metrics.h
index 5403e69..cb1588b 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_metrics.h
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_metrics.h
@@ -68,8 +68,8 @@
   // their password every 20 hours).
   PASSWORD_ENTRY_FORCED_REAUTH = 20,
 
-  // Password entry was forced because it is required to login.
-  PASSWORD_ENTRY_REQUIRED_FOR_LOGIN = 21,
+  // Password entry was forced because sign-in with Smart Lock is disabled.
+  PASSWORD_ENTRY_LOGIN_DISABLED = 21,
 
   EASY_UNLOCK_AUTH_EVENT_COUNT  // Must be the last entry.
 };
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.cc
index b063a24..dab95f0 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.cc
@@ -283,26 +283,34 @@
 
   base::string16 device_name = GetDeviceName();
   base::string16 tooltip;
-  if (hardlock_state_ == USER_HARDLOCK) {
-    tooltip = l10n_util::GetStringFUTF16(
-        IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_USER, device_name);
-  } else if (hardlock_state_ == PAIRING_CHANGED) {
-    tooltip = l10n_util::GetStringFUTF16(
-        IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_CHANGED,
-        device_name);
-  } else if (hardlock_state_ == PAIRING_ADDED) {
-    tooltip = l10n_util::GetStringFUTF16(
-        IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED, device_name);
-  } else if (hardlock_state_ == LOGIN_FAILED) {
-    tooltip = l10n_util::GetStringUTF16(
-        IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE);
-  } else if (hardlock_state_ == PASSWORD_REQUIRED_FOR_LOGIN) {
-    tooltip = l10n_util::GetStringFUTF16(
-        IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PASSWORD_REQUIRED_FOR_LOGIN,
-        device_name);
-  } else {
-    LOG(ERROR) << "Unknown hardlock state " << hardlock_state_;
+  switch (hardlock_state_) {
+    case USER_HARDLOCK:
+      tooltip = l10n_util::GetStringFUTF16(
+          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_USER, device_name);
+      break;
+    case PAIRING_CHANGED:
+      tooltip = l10n_util::GetStringFUTF16(
+          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_CHANGED,
+          device_name);
+      break;
+    case PAIRING_ADDED:
+      tooltip = l10n_util::GetStringFUTF16(
+          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED,
+          device_name);
+      break;
+    case LOGIN_FAILED:
+      tooltip = l10n_util::GetStringUTF16(
+          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE);
+      break;
+    case LOGIN_DISABLED:
+      tooltip = l10n_util::GetStringUTF16(
+          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_DISABLED);
+      break;
+    default:
+      LOG(ERROR) << "Unknown hardlock state: " << hardlock_state_;
+      NOTREACHED();
   }
+
   icon_options.SetTooltip(tooltip, true /* autoshow */);
 
   screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(account_id_,
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.h b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.h
index 4398b65..f5370f4a 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.h
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_screenlock_state_handler.h
@@ -30,7 +30,8 @@
                                // failure. Reset when screen is unlocked.
     PAIRING_ADDED = 1 << 4,    // Similar to PAIRING_CHANGED when it happens
                                // on a new Chromebook.
-    PASSWORD_REQUIRED_FOR_LOGIN = 1 << 5,  // Login must be done with a password
+    LOGIN_DISABLED = 1 << 5,   // Sign-in via Smart Lock is disabled in
+                               // Settings.
   };
 
   // |account_id|: The account id of the user associated with the profile to
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
index 29676a4..170ba29 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -594,9 +594,9 @@
       case EasyUnlockScreenlockStateHandler::PAIRING_ADDED:
         return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
             kPairingAdded;
-      case EasyUnlockScreenlockStateHandler::PASSWORD_REQUIRED_FOR_LOGIN:
+      case EasyUnlockScreenlockStateHandler::LOGIN_DISABLED:
         return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
-            kRequiredForLogin;
+            kLoginWithSmartLockDisabled;
       default:
         NOTREACHED();
         return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState::
@@ -670,8 +670,8 @@
         return PASSWORD_ENTRY_LOGIN_FAILED;
       case EasyUnlockScreenlockStateHandler::PAIRING_ADDED:
         return PASSWORD_ENTRY_PAIRING_ADDED;
-      case EasyUnlockScreenlockStateHandler::PASSWORD_REQUIRED_FOR_LOGIN:
-        return PASSWORD_ENTRY_REQUIRED_FOR_LOGIN;
+      case EasyUnlockScreenlockStateHandler::LOGIN_DISABLED:
+        return PASSWORD_ENTRY_LOGIN_DISABLED;
     }
   } else if (!screenlock_state_handler()) {
     return PASSWORD_ENTRY_NO_SCREENLOCK_STATE_HANDLER;
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index 9dff063..9d6c0e71 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -650,10 +650,10 @@
     return;
 
   if (!pref_manager_->IsChromeOSLoginEnabled()) {
-    // Show a hardlock state if the user has not enabled the login flow.
-    SetHardlockStateForUser(
-        account_id_,
-        EasyUnlockScreenlockStateHandler::PASSWORD_REQUIRED_FOR_LOGIN);
+    // Show a hardlock state if the user has not enabled Smart Lock to the log
+    // in to the user's Google account.
+    SetHardlockStateForUser(account_id_,
+                            EasyUnlockScreenlockStateHandler::LOGIN_DISABLED);
   } else {
     // This UI is simply a placeholder until the RemoteDevices are loaded from
     // cryptohome and the ProximityAuthSystem is started. Hardlock states are
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
index bcc79fb..1095d17d 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
@@ -84,7 +84,6 @@
   const std::string primary_account_id = GetPrimaryAccountId();
   if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
     VLOG(1) << "OAuth2 refresh token is already loaded.";
-    FireRefreshTokensLoaded();
     VerifySessionCookies();
   } else {
     VLOG(1) << "Waiting for OAuth2 refresh token being loaded from database.";
@@ -164,17 +163,11 @@
   DCHECK(!refresh_token_.empty());
   // |account_id| is assumed to be already canonicalized if it's an email.
   GetTokenService()->UpdateCredentials(account_id, refresh_token_);
-  FireRefreshTokensLoaded();
 
   for (auto& observer : observer_list_)
     observer.OnNewRefreshTokenAvaiable(user_profile_);
 }
 
-void OAuth2LoginManager::FireRefreshTokensLoaded() {
-  // TODO(570218): Figure out the right way to plumb this.
-  GetTokenService()->LoadCredentials(std::string());
-}
-
 void OAuth2LoginManager::VerifySessionCookies() {
   DCHECK(!login_verifier_.get());
   login_verifier_.reset(new OAuth2LoginVerifier(
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
index d9763d1..3c08c11 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
@@ -175,12 +175,6 @@
   // Update the token service and inform listeners of a new refresh token.
   void UpdateCredentials(const std::string& account_id);
 
-  // Notify that the refresh tokens are loaded and ready to use.
-  void FireRefreshTokensLoaded();
-
-  // Reports when all tokens are loaded.
-  void ReportOAuth2TokensLoaded();
-
   // Checks if primary account sessions cookies are stale and restores them
   // if needed.
   void VerifySessionCookies();
diff --git a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
index 2377f83..46180fcf 100644
--- a/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
+++ b/chrome/browser/chromeos/login/ui/captive_portal_window_proxy.cc
@@ -26,7 +26,6 @@
   params.delegate = delegate;
   params.child = true;
   params.parent = parent;
-  params.remove_standard_frame = true;
   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
 
   widget->Init(params);
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
index 5ab2bf6..51b405c8 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.cc
@@ -17,9 +17,13 @@
 //   "hash": "842841a4c75a55ad050d686f4ea5f77e83ae059877fe9b6946aa63d3d057ed32"
 // }
 const char kPluginVmImage[] = "plugin_vm.image";
+// A boolean preference representing whether there is a PluginVm image for
+// this user on this device.
+const char kPluginVmImageExists[] = "plugin_vm.image_exists";
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(kPluginVmImage);
+  registry->RegisterBooleanPref(kPluginVmImageExists, false);
 }
 
 }  // namespace prefs
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
index 057fea5..2611438 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h
@@ -11,6 +11,7 @@
 namespace prefs {
 
 extern const char kPluginVmImage[];
+extern const char kPluginVmImageExists[];
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
index 409f4ee..359bf823 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.cc
@@ -54,4 +54,12 @@
   return true;
 }
 
+bool IsPluginVmConfigured(Profile* profile) {
+  if (!profile->GetPrefs()->GetBoolean(
+          plugin_vm::prefs::kPluginVmImageExists)) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace plugin_vm
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
index 991813e..71cb3d23 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util.h
@@ -11,6 +11,8 @@
 
 // Checks if PluginVm is allowed for the current profile.
 bool IsPluginVmAllowedForProfile(Profile* profile);
+// Checks if PluginVm is configured for the current profile.
+bool IsPluginVmConfigured(Profile* profile);
 
 }  // namespace plugin_vm
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
index c6a4bfd..630b124 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
@@ -77,4 +77,13 @@
   EXPECT_TRUE(IsPluginVmAllowedForProfile(testing_profile_.get()));
 }
 
+TEST_F(PluginVmUtilTest, IsPluginVmConfiguredOnceAllConditionsAreMet) {
+  EXPECT_FALSE(IsPluginVmConfigured(testing_profile_.get()));
+
+  testing_profile_->GetPrefs()->SetBoolean(
+      plugin_vm::prefs::kPluginVmImageExists, true);
+
+  EXPECT_TRUE(IsPluginVmConfigured(testing_profile_.get()));
+}
+
 }  // namespace plugin_vm
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
index 880c4f4..6e7ec211 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
@@ -94,7 +94,7 @@
   // even if this method is invoked multiple times. The |callback|s passed are
   // enqueued and all invoked once the data has been successfully retrieved.
   void Fetch(const std::string& policy,
-             const ExternalDataFetcher::FetchCallback& callback);
+             ExternalDataFetcher::FetchCallback callback);
 
   // Try to download and cache all external data referenced by |metadata_|.
   void FetchAll();
@@ -112,7 +112,7 @@
 
   // Invokes |callback| via the |callback_task_runner_|, passing |data| as a
   // parameter.
-  void RunCallback(const ExternalDataFetcher::FetchCallback& callback,
+  void RunCallback(ExternalDataFetcher::FetchCallback callback,
                    std::unique_ptr<std::string> data) const;
 
   // Tells the |updater_| to download the external data referenced by |policy|.
@@ -180,10 +180,8 @@
   updater_.reset(new ExternalPolicyDataUpdater(
       task_runner_, std::move(external_policy_data_fetcher),
       kMaxParallelFetches));
-  for (FetchCallbackMap::const_iterator it = pending_downloads_.begin();
-       it != pending_downloads_.end(); ++it) {
-    StartDownload(it->first);
-  }
+  for (const auto& it : pending_downloads_)
+    StartDownload(it.first);
 }
 
 void CloudExternalDataManagerBase::Backend::Disconnect() {
@@ -213,10 +211,9 @@
         // Cancel the external data download.
         updater_->CancelExternalDataFetch(policy);
       }
-      for (FetchCallbackList::const_iterator callback = it->second.begin();
-           callback != it->second.end(); ++callback) {
+      for (ExternalDataFetcher::FetchCallback& callback : it->second) {
         // Invoke all callbacks for |policy|, indicating permanent failure.
-        RunCallback(*callback, std::unique_ptr<std::string>());
+        RunCallback(std::move(callback), std::unique_ptr<std::string>());
       }
       pending_downloads_.erase(it++);
       continue;
@@ -242,25 +239,23 @@
   if (external_data_store_)
     external_data_store_->Store(policy, hash, data);
 
-  const FetchCallbackList& pending_callbacks = pending_downloads_[policy];
-  for (FetchCallbackList::const_iterator it = pending_callbacks.begin();
-       it != pending_callbacks.end(); ++it) {
-    RunCallback(*it, std::make_unique<std::string>(data));
-  }
+  FetchCallbackList& pending_callbacks = pending_downloads_[policy];
+  for (ExternalDataFetcher::FetchCallback& callback : pending_callbacks)
+    RunCallback(std::move(callback), std::make_unique<std::string>(data));
   pending_downloads_.erase(policy);
   return true;
 }
 
 void CloudExternalDataManagerBase::Backend::Fetch(
     const std::string& policy,
-    const ExternalDataFetcher::FetchCallback& callback) {
+    ExternalDataFetcher::FetchCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   Metadata::const_iterator metadata = metadata_.find(policy);
   if (metadata == metadata_.end()) {
     // If |policy| does not reference any external data, indicate permanent
     // failure.
-    RunCallback(callback, std::unique_ptr<std::string>());
+    RunCallback(std::move(callback), std::unique_ptr<std::string>());
     return;
   }
 
@@ -268,7 +263,7 @@
     // If a download of the external data referenced by |policy| has already
     // been requested, add |callback| to the list of callbacks for |policy| and
     // return.
-    pending_downloads_[policy].push_back(callback);
+    pending_downloads_[policy].push_back(std::move(callback));
     return;
   }
 
@@ -278,27 +273,27 @@
           data.get())) {
     // If the external data referenced by |policy| exists in the cache and
     // matches the expected hash, pass it to the callback.
-    RunCallback(callback, std::move(data));
+    RunCallback(std::move(callback), std::move(data));
     return;
   }
 
   // Request a download of the the external data referenced by |policy| and
   // initialize the list of callbacks by adding |callback|.
-  pending_downloads_[policy].push_back(callback);
+  pending_downloads_[policy].push_back(std::move(callback));
   StartDownload(policy);
 }
 
 void CloudExternalDataManagerBase::Backend::FetchAll() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Loop through all external data references.
-  for (Metadata::const_iterator it = metadata_.begin(); it != metadata_.end();
-       ++it) {
-    const std::string& policy = it->first;
+  for (const auto& it : metadata_) {
+    const std::string& policy = it.first;
     std::unique_ptr<std::string> data(new std::string);
     if (pending_downloads_.find(policy) != pending_downloads_.end() ||
-        (external_data_store_ && external_data_store_->Load(
-             policy, it->second.hash, GetMaxExternalDataSize(policy),
-             data.get()))) {
+        (external_data_store_ &&
+         external_data_store_->Load(policy, it.second.hash,
+                                    GetMaxExternalDataSize(policy),
+                                    data.get()))) {
       // If a download of the external data referenced by |policy| has already
       // been requested or the data exists in the cache and matches the expected
       // hash, there is nothing to be done.
@@ -330,11 +325,11 @@
 }
 
 void CloudExternalDataManagerBase::Backend::RunCallback(
-    const ExternalDataFetcher::FetchCallback& callback,
+    ExternalDataFetcher::FetchCallback callback,
     std::unique_ptr<std::string> data) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   callback_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(callback, base::Passed(&data)));
+      FROM_HERE, base::BindOnce(std::move(callback), base::Passed(&data)));
 }
 
 void CloudExternalDataManagerBase::Backend::StartDownload(
@@ -398,9 +393,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<Metadata> metadata(new Metadata);
   const PolicyMap& policy_map = policy_store_->policy_map();
-  for (PolicyMap::const_iterator it = policy_map.begin();
-       it != policy_map.end(); ++it) {
-    if (!it->second.external_data_fetcher) {
+  for (const auto& it : policy_map) {
+    if (!it.second.external_data_fetcher) {
       // Skip policies that do not reference external data.
       continue;
     }
@@ -408,14 +402,14 @@
     std::string url;
     std::string hex_hash;
     std::vector<uint8_t> hash;
-    if (it->second.value && it->second.value->GetAsDictionary(&dict) &&
+    if (it.second.value && it.second.value->GetAsDictionary(&dict) &&
         dict->GetStringWithoutPathExpansion("url", &url) &&
         dict->GetStringWithoutPathExpansion("hash", &hex_hash) &&
         !url.empty() && !hex_hash.empty() &&
         base::HexStringToBytes(hex_hash, &hash)) {
       // Add the external data reference to |metadata| if it is valid (URL and
       // hash are not empty, hash can be decoded as a hex string).
-      (*metadata)[it->first] =
+      (*metadata)[it.first] =
           MetadataEntry(url, std::string(hash.begin(), hash.end()));
     }
   }
@@ -451,12 +445,12 @@
 
 void CloudExternalDataManagerBase::Fetch(
     const std::string& policy,
-    const ExternalDataFetcher::FetchCallback& callback) {
+    ExternalDataFetcher::FetchCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   backend_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&Backend::Fetch, base::Unretained(backend_.get()), policy,
-                     callback));
+                     std::move(callback)));
 }
 
 // static
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
index 3057b0e01..82606619 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
+++ b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
@@ -53,7 +53,7 @@
                    url_loader_factory) override;
   void Disconnect() override;
   void Fetch(const std::string& policy,
-             const ExternalDataFetcher::FetchCallback& callback) override;
+             ExternalDataFetcher::FetchCallback callback) override;
 
   // Allows policies to reference |max_size| bytes of external data even if no
   // |max_size| was specified in policy_templates.json.
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
index c855ffcc..a2cdd48b 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
@@ -284,10 +284,9 @@
   std::unique_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id];
   weak_ptr_factory.reset(new WeakPtrFactory(this));
   if (entry->external_data_fetcher) {
-    entry->external_data_fetcher->Fetch(base::Bind(
-        &CloudExternalDataPolicyObserver::OnExternalDataFetched,
-        weak_ptr_factory->GetWeakPtr(),
-        user_id));
+    entry->external_data_fetcher->Fetch(
+        base::BindOnce(&CloudExternalDataPolicyObserver::OnExternalDataFetched,
+                       weak_ptr_factory->GetWeakPtr(), user_id));
   } else {
     NOTREACHED();
   }
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc
index 115491c..0e8a75c 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc
@@ -59,7 +59,6 @@
 
 using ::testing::Mock;
 using ::testing::Return;
-using ::testing::SaveArg;
 using ::testing::_;
 
 namespace policy {
@@ -745,7 +744,10 @@
 
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
       .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+      .WillOnce([&](const std::string& policy,
+                    ExternalDataFetcher::FetchCallback callback) {
+        fetch_callback_ = std::move(callback);
+      });
 
   LogInAsRegularUser();
 
@@ -758,7 +760,8 @@
   Mock::VerifyAndClear(&external_data_manager_);
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(0);
 
-  fetch_callback_.Run(base::WrapUnique(new std::string(avatar_policy_1_data_)));
+  std::move(fetch_callback_)
+      .Run(std::make_unique<std::string>(avatar_policy_1_data_));
 
   EXPECT_TRUE(set_calls_.empty());
   EXPECT_TRUE(cleared_calls_.empty());
@@ -805,9 +808,7 @@
 
   CreateObserver();
 
-  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
-      .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(1);
 
   LogInAsRegularUser();
 
@@ -850,7 +851,10 @@
   Mock::VerifyAndClear(&external_data_manager_);
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
       .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+      .WillOnce([&](const std::string& policy,
+                    ExternalDataFetcher::FetchCallback callback) {
+        fetch_callback_ = std::move(callback);
+      });
 
   SetRegularUserAvatarPolicy(avatar_policy_1_);
 
@@ -863,7 +867,8 @@
   Mock::VerifyAndClear(&external_data_manager_);
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(0);
 
-  fetch_callback_.Run(base::WrapUnique(new std::string(avatar_policy_1_data_)));
+  std::move(fetch_callback_)
+      .Run(std::make_unique<std::string>(avatar_policy_1_data_));
 
   EXPECT_TRUE(set_calls_.empty());
   EXPECT_TRUE(cleared_calls_.empty());
@@ -884,9 +889,7 @@
 
   CreateObserver();
 
-  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
-      .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(1);
 
   LogInAsRegularUser();
 
@@ -899,7 +902,10 @@
   Mock::VerifyAndClear(&external_data_manager_);
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
       .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+      .WillOnce([&](const std::string& policy,
+                    ExternalDataFetcher::FetchCallback callback) {
+        fetch_callback_ = std::move(callback);
+      });
 
   SetRegularUserAvatarPolicy(avatar_policy_2_);
 
@@ -912,7 +918,8 @@
   Mock::VerifyAndClear(&external_data_manager_);
   EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(0);
 
-  fetch_callback_.Run(base::WrapUnique(new std::string(avatar_policy_2_data_)));
+  std::move(fetch_callback_)
+      .Run(std::make_unique<std::string>(avatar_policy_2_data_));
 
   EXPECT_TRUE(set_calls_.empty());
   EXPECT_TRUE(cleared_calls_.empty());
@@ -929,9 +936,7 @@
   SetRegularUserAvatarPolicy(avatar_policy_1_);
   CreateObserver();
 
-  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _))
-      .Times(1)
-      .WillOnce(SaveArg<1>(&fetch_callback_));
+  EXPECT_CALL(external_data_manager_, Fetch(key::kUserAvatarImage, _)).Times(1);
 
   LogInAsRegularUser();
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_external_data_policy_observer.cc b/chrome/browser/chromeos/policy/device_cloud_external_data_policy_observer.cc
index c825a496..381a59d 100644
--- a/chrome/browser/chromeos/policy/device_cloud_external_data_policy_observer.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_external_data_policy_observer.cc
@@ -67,7 +67,7 @@
   // Invalidate any pending callbacks. They are fetching outdated data.
   weak_factory_.InvalidateWeakPtrs();
   if (entry->external_data_fetcher) {
-    entry->external_data_fetcher->Fetch(base::BindRepeating(
+    entry->external_data_fetcher->Fetch(base::BindOnce(
         &DeviceCloudExternalDataPolicyObserver::OnDeviceExternalDataFetched,
         weak_factory_.GetWeakPtr()));
   } else {
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 179f699cf..06f8f43 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -1415,10 +1415,9 @@
   // the retrieval should succeed because the data has been cached.
   run_loop.reset(new base::RunLoop);
   std::unique_ptr<std::string> fetched_external_data;
-  policy_entry->external_data_fetcher->Fetch(base::Bind(
-      &test::ExternalDataFetchCallback,
-      &fetched_external_data,
-      run_loop->QuitClosure()));
+  policy_entry->external_data_fetcher->Fetch(
+      base::BindOnce(&test::ExternalDataFetchCallback, &fetched_external_data,
+                     run_loop->QuitClosure()));
   run_loop->Run();
 
   ASSERT_TRUE(fetched_external_data);
@@ -1443,10 +1442,9 @@
   // should succeed because the data has been cached.
   run_loop.reset(new base::RunLoop);
   fetched_external_data.reset();
-  policy_entry->external_data_fetcher->Fetch(base::Bind(
-      &test::ExternalDataFetchCallback,
-      &fetched_external_data,
-      run_loop->QuitClosure()));
+  policy_entry->external_data_fetcher->Fetch(
+      base::BindOnce(&test::ExternalDataFetchCallback, &fetched_external_data,
+                     run_loop->QuitClosure()));
   run_loop->Run();
 
   ASSERT_TRUE(fetched_external_data);
diff --git a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
index 5015d9f..6f9c697 100644
--- a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
@@ -102,8 +102,8 @@
     base::RunLoop run_loop;
     std::unique_ptr<std::string> fetched_external_data;
     policy_entry->external_data_fetcher->Fetch(
-        base::BindRepeating(&test::ExternalDataFetchCallback,
-                            &fetched_external_data, run_loop.QuitClosure()));
+        base::BindOnce(&test::ExternalDataFetchCallback, &fetched_external_data,
+                       run_loop.QuitClosure()));
     run_loop.Run();
 
     EXPECT_TRUE(fetched_external_data);
diff --git a/chrome/browser/chromeos/policy/status_uploader.cc b/chrome/browser/chromeos/policy/status_uploader.cc
index bdd7560..41ed42e 100644
--- a/chrome/browser/chromeos/policy/status_uploader.cc
+++ b/chrome/browser/chromeos/policy/status_uploader.cc
@@ -47,16 +47,6 @@
       upload_frequency_(default_upload_frequency),
       has_captured_media_(false),
       weak_factory_(this) {
-  // StatusUploader is currently only created for registered clients, and
-  // it is currently safe to assume that the client will not unregister while
-  // StatusUploader is alive.
-  //
-  // If future changes result in StatusUploader's lifetime extending beyond
-  // unregistration events, then this class should be updated
-  // to skip status uploads for unregistered clients, and to observe the client
-  // and kick off an upload when registration happens.
-  DCHECK(client->is_registered());
-
   // Track whether any media capture devices are in use - this changes what
   // type of information we are allowed to upload.
   MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
diff --git a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
index 1ae6551..21b3e74 100644
--- a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
@@ -112,10 +112,9 @@
 
   base::RunLoop run_loop;
   std::unique_ptr<std::string> fetched_external_data;
-  policy_entry->external_data_fetcher->Fetch(base::Bind(
-      &test::ExternalDataFetchCallback,
-      &fetched_external_data,
-      run_loop.QuitClosure()));
+  policy_entry->external_data_fetcher->Fetch(
+      base::BindOnce(&test::ExternalDataFetchCallback, &fetched_external_data,
+                     run_loop.QuitClosure()));
   run_loop.Run();
 
   ASSERT_TRUE(fetched_external_data);
diff --git a/chrome/browser/chromeos/ui/idle_app_name_notification_view.cc b/chrome/browser/chromeos/ui/idle_app_name_notification_view.cc
index 39a3802c..f91bb3f 100644
--- a/chrome/browser/chromeos/ui/idle_app_name_notification_view.cc
+++ b/chrome/browser/chromeos/ui/idle_app_name_notification_view.cc
@@ -66,7 +66,6 @@
   params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
   params.accept_events = false;
   params.keep_on_top = true;
-  params.remove_standard_frame = true;
   params.delegate = delegate;
   params.bounds = bounds;
   ash_util::SetupWidgetInitParamsForContainer(
diff --git a/chrome/browser/chromeos/ui/kiosk_external_update_notification.cc b/chrome/browser/chromeos/ui/kiosk_external_update_notification.cc
index ac5b6e4..e05a253 100644
--- a/chrome/browser/chromeos/ui/kiosk_external_update_notification.cc
+++ b/chrome/browser/chromeos/ui/kiosk_external_update_notification.cc
@@ -134,7 +134,6 @@
   params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
   params.accept_events = false;
   params.keep_on_top = true;
-  params.remove_standard_frame = true;
   params.delegate = view_;
   params.bounds = bounds;
   // The notification is shown on the primary display.
diff --git a/chrome/browser/android/tasks/OWNERS b/chrome/browser/complex_tasks/OWNERS
similarity index 70%
rename from chrome/browser/android/tasks/OWNERS
rename to chrome/browser/complex_tasks/OWNERS
index fe60577..cfeb8c6 100644
--- a/chrome/browser/android/tasks/OWNERS
+++ b/chrome/browser/complex_tasks/OWNERS
@@ -1,2 +1,3 @@
 wychen@chromium.org
 yusufo@chromium.org
+bsep@chromium.org
\ No newline at end of file
diff --git a/chrome/browser/android/tasks/task_tab_helper.cc b/chrome/browser/complex_tasks/task_tab_helper.cc
similarity index 95%
rename from chrome/browser/android/tasks/task_tab_helper.cc
rename to chrome/browser/complex_tasks/task_tab_helper.cc
index 0ebce77..aac07fc4 100644
--- a/chrome/browser/android/tasks/task_tab_helper.cc
+++ b/chrome/browser/complex_tasks/task_tab_helper.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/tasks/task_tab_helper.h"
+#include "chrome/browser/complex_tasks/task_tab_helper.h"
 
 #include <string>
 
@@ -87,7 +87,9 @@
       histogram_name = "Tabs.Tasks.HubAndSpokeNavigationUsage.FromOther";
       break;
     }
-    default: { NOTREACHED() << "Unknown HubType"; }
+    default: {
+      NOTREACHED() << "Unknown HubType";
+    }
   }
 
   base::UmaHistogramExactLinear(histogram_name, spokes, 100);
diff --git a/chrome/browser/android/tasks/task_tab_helper.h b/chrome/browser/complex_tasks/task_tab_helper.h
similarity index 84%
rename from chrome/browser/android/tasks/task_tab_helper.h
rename to chrome/browser/complex_tasks/task_tab_helper.h
index ae2221a..c681148e 100644
--- a/chrome/browser/android/tasks/task_tab_helper.h
+++ b/chrome/browser/complex_tasks/task_tab_helper.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 CHROME_BROWSER_ANDROID_TASKS_TASK_TAB_HELPER_H_
-#define CHROME_BROWSER_ANDROID_TASKS_TASK_TAB_HELPER_H_
+#ifndef CHROME_BROWSER_COMPLEX_TASKS_TASK_TAB_HELPER_H_
+#define CHROME_BROWSER_COMPLEX_TASKS_TASK_TAB_HELPER_H_
 
 #include <map>
 
@@ -14,6 +14,8 @@
 
 namespace tasks {
 
+// This is a tab helper that collects navigation state information of a
+// complex task.
 class TaskTabHelper : public content::WebContentsObserver,
                       public content::WebContentsUserData<TaskTabHelper> {
  public:
@@ -52,4 +54,4 @@
 
 }  // namespace tasks
 
-#endif  // CHROME_BROWSER_ANDROID_TASKS_TASK_TAB_HELPER_H_
+#endif  // CHROME_BROWSER_COMPLEX_TASKS_TASK_TAB_HELPER_H_
diff --git a/chrome/browser/android/tasks/task_tab_helper_unittest.cc b/chrome/browser/complex_tasks/task_tab_helper_unittest.cc
similarity index 97%
rename from chrome/browser/android/tasks/task_tab_helper_unittest.cc
rename to chrome/browser/complex_tasks/task_tab_helper_unittest.cc
index 18da273..47e3fdc5 100644
--- a/chrome/browser/android/tasks/task_tab_helper_unittest.cc
+++ b/chrome/browser/complex_tasks/task_tab_helper_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/tasks/task_tab_helper.h"
+#include "chrome/browser/complex_tasks/task_tab_helper.h"
 
 #include <string>
 
@@ -53,7 +53,7 @@
       MockTaskTabHelper::HubType::OTHER;
 
   void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
     MockTaskTabHelper::CreateForWebContents(web_contents());
     task_tab_helper_ = MockTaskTabHelper::FromWebContents(web_contents());
     NavigateAndCommit(URL);
@@ -62,8 +62,6 @@
         .WillByDefault(testing::Return(DEFAULT_SEARCH_ENGINE_HUB_TYPE));
   }
 
-  void TearDown() override {}
-
   void GoBackNTimes(int times) {
     while (times--) {
       content::NavigationSimulator::GoBack(web_contents());
diff --git a/chrome/browser/conflicts/incompatible_applications_updater_win.cc b/chrome/browser/conflicts/incompatible_applications_updater_win.cc
index a8f89e67..9741add 100644
--- a/chrome/browser/conflicts/incompatible_applications_updater_win.cc
+++ b/chrome/browser/conflicts/incompatible_applications_updater_win.cc
@@ -300,7 +300,7 @@
   // Explicitly whitelist modules whose signing cert's Subject field matches the
   // one in the current executable. No attempt is made to check the validity of
   // module signatures or of signing certs.
-  if (exe_certificate_info_.type != CertificateType::NO_CERTIFICATE &&
+  if (exe_certificate_info_.type != CertificateInfo::Type::NO_CERTIFICATE &&
       exe_certificate_info_.subject ==
           module_data.inspection_result->certificate_info.subject) {
     module_warning_decisions_[module_key.module_id] =
diff --git a/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc b/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc
index 65556d4d..ddc7391b 100644
--- a/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
@@ -83,7 +84,7 @@
 ModuleInfoData CreateLoadedModuleInfoData() {
   ModuleInfoData module_data;
   module_data.module_properties |= ModuleInfoData::kPropertyLoadedModule;
-  module_data.inspection_result = std::make_unique<ModuleInspectionResult>();
+  module_data.inspection_result = base::make_optional<ModuleInspectionResult>();
   return module_data;
 }
 
@@ -93,7 +94,7 @@
   ModuleInfoData module_data = CreateLoadedModuleInfoData();
 
   module_data.inspection_result->certificate_info.type =
-      CertificateType::CERTIFICATE_IN_FILE;
+      CertificateInfo::Type::CERTIFICATE_IN_FILE;
   module_data.inspection_result->certificate_info.path =
       base::FilePath(kCertificatePath);
   module_data.inspection_result->certificate_info.subject = kCertificateSubject;
@@ -111,7 +112,7 @@
         dll2_(kDllPath2),
         scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
         module_list_filter_(base::MakeRefCounted<MockModuleListFilter>()) {
-    exe_certificate_info_.type = CertificateType::CERTIFICATE_IN_FILE;
+    exe_certificate_info_.type = CertificateInfo::Type::CERTIFICATE_IN_FILE;
     exe_certificate_info_.path = base::FilePath(kCertificatePath);
     exe_certificate_info_.subject = kCertificateSubject;
   }
@@ -343,7 +344,7 @@
   // Simulate the module loading into the process.
   ModuleInfoKey module_key(dll1_, 0, 0, 0);
   ModuleInfoData module_data;
-  module_data.inspection_result = std::make_unique<ModuleInspectionResult>();
+  module_data.inspection_result = base::make_optional<ModuleInspectionResult>();
   incompatible_applications_updater->OnNewModuleFound(module_key, module_data);
   incompatible_applications_updater->OnModuleDatabaseIdle();
 
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
index 30a7ce2e..6642ceae 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
@@ -345,7 +345,7 @@
   // Explicitly whitelist modules whose signing cert's Subject field matches the
   // one in the current executable. No attempt is made to check the validity of
   // module signatures or of signing certs.
-  if (exe_certificate_info_.type != CertificateType::NO_CERTIFICATE &&
+  if (exe_certificate_info_.type != CertificateInfo::Type::NO_CERTIFICATE &&
       exe_certificate_info_.subject ==
           module_data.inspection_result->certificate_info.subject) {
     return ModuleBlockingDecision::kAllowedSameCertificate;
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
index d4d7fc03..0b9e2a3 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/files/file_util.h"
 #include "base/i18n/case_conversion.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/sha1.h"
 #include "base/strings/stringprintf.h"
@@ -49,7 +50,7 @@
 ModuleInfoData CreateLoadedModuleInfoData() {
   ModuleInfoData module_data;
   module_data.module_properties |= ModuleInfoData::kPropertyLoadedModule;
-  module_data.inspection_result = std::make_unique<ModuleInspectionResult>();
+  module_data.inspection_result = base::make_optional<ModuleInspectionResult>();
   return module_data;
 }
 
@@ -59,7 +60,7 @@
   ModuleInfoData module_data = CreateLoadedModuleInfoData();
 
   module_data.inspection_result->certificate_info.type =
-      CertificateType::CERTIFICATE_IN_FILE;
+      CertificateInfo::Type::CERTIFICATE_IN_FILE;
   module_data.inspection_result->certificate_info.path =
       base::FilePath(kCertificatePath);
   module_data.inspection_result->certificate_info.subject = kCertificateSubject;
@@ -102,7 +103,7 @@
         module_list_filter_(CreateModuleListFilter()),
         module_blacklist_cache_path_(
             ModuleBlacklistCacheUpdater::GetModuleBlacklistCachePath()) {
-    exe_certificate_info_.type = CertificateType::CERTIFICATE_IN_FILE;
+    exe_certificate_info_.type = CertificateInfo::Type::CERTIFICATE_IN_FILE;
     exe_certificate_info_.path = base::FilePath(kCertificatePath);
     exe_certificate_info_.subject = kCertificateSubject;
   }
@@ -245,7 +246,7 @@
 
   ModuleInfoKey module_key(module_path, module_size, time_date_stamp, 0);
   ModuleInfoData module_data = CreateLoadedModuleInfoData();
-  module_data.inspection_result = InspectModule(StringMapping(), module_key);
+  module_data.inspection_result = InspectModule(module_key.module_path);
 
   module_blacklist_cache_updater->OnNewModuleFound(module_key, module_data);
   module_blacklist_cache_updater->OnModuleDatabaseIdle();
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index 145f969..5ca7132 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -334,7 +334,7 @@
 
 void ModuleDatabase::OnModuleInspected(
     const ModuleInfoKey& module_key,
-    std::unique_ptr<ModuleInspectionResult> inspection_result) {
+    ModuleInspectionResult inspection_result) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   auto it = modules_.find(module_key);
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index 94b87ee5..00f3670a 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -198,9 +198,8 @@
   void OnRegisteredModulesEnumerated();
 
   // Callback for ModuleInspector.
-  void OnModuleInspected(
-      const ModuleInfoKey& module_key,
-      std::unique_ptr<ModuleInspectionResult> inspection_result);
+  void OnModuleInspected(const ModuleInfoKey& module_key,
+                         ModuleInspectionResult inspection_result);
 
   // If the ModuleDatabase is truly idle, calls EnterIdleState().
   void OnDelayExpired();
diff --git a/chrome/browser/conflicts/module_info_util_win.cc b/chrome/browser/conflicts/module_info_util_win.cc
index 9da05a0..45ac107 100644
--- a/chrome/browser/conflicts/module_info_util_win.cc
+++ b/chrome/browser/conflicts/module_info_util_win.cc
@@ -218,7 +218,7 @@
   if (subject.empty())
     return;
 
-  certificate_info->type = CertificateType::CERTIFICATE_IN_CATALOG;
+  certificate_info->type = CertificateInfo::Type::CERTIFICATE_IN_CATALOG;
   certificate_info->path = catalog_path;
   certificate_info->subject = subject;
 }
@@ -229,25 +229,25 @@
 
 // ModuleDatabase::CertificateInfo ---------------------------------------------
 
-CertificateInfo::CertificateInfo() : type(CertificateType::NO_CERTIFICATE) {}
+CertificateInfo::CertificateInfo() : type(Type::NO_CERTIFICATE) {}
 
 // Extracts information about the certificate of the given file, if any is
 // found.
 void GetCertificateInfo(const base::FilePath& filename,
                         CertificateInfo* certificate_info) {
-  DCHECK_EQ(CertificateType::NO_CERTIFICATE, certificate_info->type);
+  DCHECK_EQ(CertificateInfo::Type::NO_CERTIFICATE, certificate_info->type);
   DCHECK(certificate_info->path.empty());
   DCHECK(certificate_info->subject.empty());
 
   GetCatalogCertificateInfo(filename, certificate_info);
-  if (certificate_info->type == CertificateType::CERTIFICATE_IN_CATALOG)
+  if (certificate_info->type == CertificateInfo::Type::CERTIFICATE_IN_CATALOG)
     return;
 
   base::string16 subject = GetSubjectNameInFile(filename);
   if (subject.empty())
     return;
 
-  certificate_info->type = CertificateType::CERTIFICATE_IN_FILE;
+  certificate_info->type = CertificateInfo::Type::CERTIFICATE_IN_FILE;
   certificate_info->path = filename;
   certificate_info->subject = subject;
 }
diff --git a/chrome/browser/conflicts/module_info_util_win.h b/chrome/browser/conflicts/module_info_util_win.h
index 642e4bc..2641cd28 100644
--- a/chrome/browser/conflicts/module_info_util_win.h
+++ b/chrome/browser/conflicts/module_info_util_win.h
@@ -16,22 +16,22 @@
 // HKEY_CLASSES_ROOT.
 extern const wchar_t kClassIdRegistryKeyFormat[];
 
-// The type of certificate found for the module.
-enum class CertificateType {
-  // The module is not signed.
-  NO_CERTIFICATE,
-  // The module is signed and the certificate is in the module.
-  CERTIFICATE_IN_FILE,
-  // The module is signed and the certificate is in an external catalog.
-  CERTIFICATE_IN_CATALOG,
-};
-
 // Information about the certificate of a file.
 struct CertificateInfo {
+  // The type of certificate found for the module.
+  enum class Type {
+    // The module is not signed.
+    NO_CERTIFICATE,
+    // The module is signed and the certificate is in the module.
+    CERTIFICATE_IN_FILE,
+    // The module is signed and the certificate is in an external catalog.
+    CERTIFICATE_IN_CATALOG,
+  };
+
   CertificateInfo();
 
   // The type of signature encountered.
-  CertificateType type;
+  Type type;
 
   // Path to the file containing the certificate. Empty if |type| is
   // NO_CERTIFICATE.
diff --git a/chrome/browser/conflicts/module_info_util_win_unittest.cc b/chrome/browser/conflicts/module_info_util_win_unittest.cc
index 7d596eb..d5ebb45 100644
--- a/chrome/browser/conflicts/module_info_util_win_unittest.cc
+++ b/chrome/browser/conflicts/module_info_util_win_unittest.cc
@@ -58,7 +58,7 @@
   ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &path));
   CertificateInfo cert_info;
   GetCertificateInfo(path, &cert_info);
-  EXPECT_EQ(CertificateType::NO_CERTIFICATE, cert_info.type);
+  EXPECT_EQ(CertificateInfo::Type::NO_CERTIFICATE, cert_info.type);
   EXPECT_TRUE(cert_info.path.empty());
   EXPECT_TRUE(cert_info.subject.empty());
 }
@@ -73,7 +73,7 @@
 
   CertificateInfo cert_info;
   GetCertificateInfo(path, &cert_info);
-  EXPECT_NE(CertificateType::NO_CERTIFICATE, cert_info.type);
+  EXPECT_NE(CertificateInfo::Type::NO_CERTIFICATE, cert_info.type);
   EXPECT_FALSE(cert_info.path.empty());
   EXPECT_FALSE(cert_info.subject.empty());
 }
diff --git a/chrome/browser/conflicts/module_info_win.cc b/chrome/browser/conflicts/module_info_win.cc
index 3e2ed35..0b5fcd9 100644
--- a/chrome/browser/conflicts/module_info_win.cc
+++ b/chrome/browser/conflicts/module_info_win.cc
@@ -22,19 +22,19 @@
 // Using the module path, populates |inspection_result| with information
 // available via the file on disk. For example, this includes the description
 // and the certificate information.
-void PopulateModuleInfoData(const ModuleInfoKey& key,
+void PopulateModuleInfoData(const base::FilePath& module_path,
                             ModuleInspectionResult* inspection_result) {
-  inspection_result->location = key.module_path.value();
+  inspection_result->location = module_path.value();
 
   std::unique_ptr<FileVersionInfo> file_version_info(
-      FileVersionInfo::CreateFileVersionInfo(key.module_path));
+      FileVersionInfo::CreateFileVersionInfo(module_path));
   if (file_version_info) {
     inspection_result->product_name = file_version_info->product_name();
     inspection_result->description = file_version_info->file_description();
     inspection_result->version = file_version_info->product_version();
   }
 
-  GetCertificateInfo(key.module_path, &(inspection_result->certificate_info));
+  GetCertificateInfo(module_path, &(inspection_result->certificate_info));
 }
 
 // Returns the long path name given a short path name. A short path name is a
@@ -78,6 +78,12 @@
 
 ModuleInspectionResult::ModuleInspectionResult() = default;
 
+ModuleInspectionResult::ModuleInspectionResult(
+    ModuleInspectionResult&& other) noexcept = default;
+
+ModuleInspectionResult& ModuleInspectionResult::operator=(
+    ModuleInspectionResult&& other) noexcept = default;
+
 ModuleInspectionResult::~ModuleInspectionResult() = default;
 
 // ModuleInfoData --------------------------------------------------------------
@@ -90,15 +96,11 @@
 
 // -----------------------------------------------------------------------------
 
-std::unique_ptr<ModuleInspectionResult> InspectModule(
-    const StringMapping& env_variable_mapping,
-    const ModuleInfoKey& module_key) {
-  auto inspection_result = std::make_unique<ModuleInspectionResult>();
+ModuleInspectionResult InspectModule(const base::FilePath& module_path) {
+  ModuleInspectionResult inspection_result;
 
-  PopulateModuleInfoData(module_key, inspection_result.get());
-  internal::NormalizeInspectionResult(inspection_result.get());
-  CollapseMatchingPrefixInPath(env_variable_mapping,
-                               &inspection_result->location);
+  PopulateModuleInfoData(module_path, &inspection_result);
+  internal::NormalizeInspectionResult(&inspection_result);
 
   return inspection_result;
 }
diff --git a/chrome/browser/conflicts/module_info_win.h b/chrome/browser/conflicts/module_info_win.h
index 5585fb1..9638392 100644
--- a/chrome/browser/conflicts/module_info_win.h
+++ b/chrome/browser/conflicts/module_info_win.h
@@ -5,10 +5,11 @@
 #ifndef CHROME_BROWSER_CONFLICTS_MODULE_INFO_WIN_H_
 #define CHROME_BROWSER_CONFLICTS_MODULE_INFO_WIN_H_
 
-#include <memory>
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/optional.h"
 #include "chrome/browser/conflicts/module_info_util_win.h"
 
 // ModuleInfoKey and ModuleInfoData are used in pair by the ModuleDatabase to
@@ -49,13 +50,15 @@
 // Holds more detailed information about a given module. Because all of this
 // information is expensive to gather and requires disk access, it should be
 // collected via InspectModule() on a task runner that allow blocking.
+//
+// This struct is move-only to ensure it is not unnecessarily copied.
 struct ModuleInspectionResult {
   ModuleInspectionResult();
+  ModuleInspectionResult(ModuleInspectionResult&& other) noexcept;
+  ModuleInspectionResult& operator=(ModuleInspectionResult&& other) noexcept;
   ~ModuleInspectionResult();
 
-  // The module path, not including the basename. This is cleaned and normalized
-  // so that common paths are converted to their environment variable mappings
-  // (ie, %systemroot%). This makes i18n localized paths easily comparable.
+  // The lowercase module path, not including the basename.
   base::string16 location;
 
   // The basename of the module.
@@ -73,6 +76,9 @@
 
   // The certificate info for the module.
   CertificateInfo certificate_info;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ModuleInspectionResult);
 };
 
 // Contains the inspection result of a module and additional information that is
@@ -108,15 +114,13 @@
   uint32_t module_properties;
 
   // The inspection result obtained via InspectModule().
-  std::unique_ptr<ModuleInspectionResult> inspection_result;
+  base::Optional<ModuleInspectionResult> inspection_result;
 };
 
-// Given a module identified by |module_key|, returns a populated
+// Given a module located at |module_path|, returns a populated
 // ModuleInspectionResult that contains detailed information about the module on
 // disk. This is a blocking task that requires access to disk.
-std::unique_ptr<ModuleInspectionResult> InspectModule(
-    const StringMapping& env_variable_mapping,
-    const ModuleInfoKey& module_key);
+ModuleInspectionResult InspectModule(const base::FilePath& module_path);
 
 // Generate the code id of a module.
 std::string GenerateCodeId(const ModuleInfoKey& module_key);
diff --git a/chrome/browser/conflicts/module_info_win_unittest.cc b/chrome/browser/conflicts/module_info_win_unittest.cc
index e1c4487a..ec7a4f7 100644
--- a/chrome/browser/conflicts/module_info_win_unittest.cc
+++ b/chrome/browser/conflicts/module_info_win_unittest.cc
@@ -27,27 +27,21 @@
 }  // namespace
 
 TEST(ModuleInfoTest, InspectModule) {
-  ModuleInfoKey module_key = {GetKernel32DllFilePath(), 0, 0, 1};
-  StringMapping path_mapping = GetEnvironmentVariablesMapping({
-      L"SystemRoot",
-  });
+  ModuleInspectionResult inspection_result =
+      InspectModule(GetKernel32DllFilePath());
 
-  std::unique_ptr<ModuleInspectionResult> inspection_result =
-      InspectModule(path_mapping, module_key);
-
-  EXPECT_STREQ(L"%systemroot%\\system32\\",
-               inspection_result->location.c_str());
-  EXPECT_STREQ(L"kernel32.dll", inspection_result->basename.c_str());
+  EXPECT_STREQ(L"c:\\windows\\system32\\", inspection_result.location.c_str());
+  EXPECT_STREQ(L"kernel32.dll", inspection_result.basename.c_str());
   EXPECT_STREQ(L"Microsoft\xAE Windows\xAE Operating System",
-               inspection_result->product_name.c_str());
+               inspection_result.product_name.c_str());
   EXPECT_STREQ(L"Windows NT BASE API Client DLL",
-               inspection_result->description.c_str());
-  EXPECT_FALSE(inspection_result->version.empty());
-  EXPECT_EQ(inspection_result->certificate_info.type,
-            CertificateType::CERTIFICATE_IN_CATALOG);
-  EXPECT_FALSE(inspection_result->certificate_info.path.empty());
+               inspection_result.description.c_str());
+  EXPECT_FALSE(inspection_result.version.empty());
+  EXPECT_EQ(inspection_result.certificate_info.type,
+            CertificateInfo::Type::CERTIFICATE_IN_CATALOG);
+  EXPECT_FALSE(inspection_result.certificate_info.path.empty());
   EXPECT_STREQ(L"Microsoft Windows",
-               inspection_result->certificate_info.subject.c_str());
+               inspection_result.certificate_info.subject.c_str());
 }
 
 TEST(ModuleInfoTest, GenerateCodeId) {
diff --git a/chrome/browser/conflicts/module_inspector_win.cc b/chrome/browser/conflicts/module_inspector_win.cc
index c678c457..efa28481 100644
--- a/chrome/browser/conflicts/module_inspector_win.cc
+++ b/chrome/browser/conflicts/module_inspector_win.cc
@@ -7,10 +7,12 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/after_startup_task_utils.h"
+#include "chrome/browser/conflicts/module_info_util_win.h"
 
 namespace {
 
@@ -23,21 +25,17 @@
 
 // Does the inspection of the module and replies with the result by calling
 // |on_inspection_finished_callback| on |reply_task_runner|.
-// The StringMapping is wrapped in a RefCountedData to save a copy per
-// invocation.
 //
 // TODO(pmonette): When the Task Scheduler starts supporting after-startup
 // background sequences, change this to use base::PostTaskAndReplyWithResult().
 void InspectModuleOnBlockingSequenceAndReply(
-    scoped_refptr<base::RefCountedData<StringMapping>> env_variable_mapping,
-    const ModuleInfoKey& module_key,
+    const base::FilePath& module_path,
     scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
-    base::OnceCallback<void(std::unique_ptr<ModuleInspectionResult>)>
+    base::OnceCallback<void(ModuleInspectionResult)>
         on_inspection_finished_callback) {
   reply_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(on_inspection_finished_callback),
-                     InspectModule(env_variable_mapping->data, module_key)));
+      FROM_HERE, base::BindOnce(std::move(on_inspection_finished_callback),
+                                InspectModule(module_path)));
 }
 
 }  // namespace
@@ -48,8 +46,7 @@
       task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
-      path_mapping_(base::MakeRefCounted<base::RefCountedData<StringMapping>>(
-          GetPathMapping())),
+      path_mapping_(GetPathMapping()),
       weak_ptr_factory_(this) {}
 
 ModuleInspector::~ModuleInspector() = default;
@@ -91,7 +88,7 @@
   AfterStartupTaskUtils::PostTask(
       FROM_HERE, task_runner_,
       base::BindOnce(
-          &InspectModuleOnBlockingSequenceAndReply, path_mapping_, module_key,
+          &InspectModuleOnBlockingSequenceAndReply, module_key.module_path,
           base::SequencedTaskRunnerHandle::Get(),
           base::BindOnce(&ModuleInspector::OnInspectionFinished,
                          weak_ptr_factory_.GetWeakPtr(), module_key)));
@@ -99,9 +96,14 @@
 
 void ModuleInspector::OnInspectionFinished(
     const ModuleInfoKey& module_key,
-    std::unique_ptr<ModuleInspectionResult> inspection_result) {
+    ModuleInspectionResult inspection_result) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  // Convert the prefix of known Windows directories to their environment
+  // variable mappings (ie, %systemroot$). This makes i18n localized paths
+  // easily comparable.
+  CollapseMatchingPrefixInPath(path_mapping_, &inspection_result.location);
+
   // Pop first, because the callback may want to know if there is any work left
   // to be done, which is caracterized by a non-empty queue.
   queue_.pop();
diff --git a/chrome/browser/conflicts/module_inspector_win.h b/chrome/browser/conflicts/module_inspector_win.h
index 92ad77b..fad1df92 100644
--- a/chrome/browser/conflicts/module_inspector_win.h
+++ b/chrome/browser/conflicts/module_inspector_win.h
@@ -5,12 +5,10 @@
 #ifndef CHROME_BROWSER_CONFLICTS_MODULE_INSPECTOR_WIN_H_
 #define CHROME_BROWSER_CONFLICTS_MODULE_INSPECTOR_WIN_H_
 
-#include <memory>
-
 #include "base/callback.h"
 #include "base/containers/queue.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/task_traits.h"
 #include "base/threading/thread_checker.h"
@@ -34,7 +32,7 @@
  public:
   using OnModuleInspectedCallback =
       base::Callback<void(const ModuleInfoKey& module_key,
-                          std::unique_ptr<ModuleInspectionResult>)>;
+                          ModuleInspectionResult inspection_result)>;
 
   explicit ModuleInspector(
       const OnModuleInspectedCallback& on_module_inspected_callback);
@@ -58,9 +56,8 @@
   // created when a module has finished being inspected. The callback will be
   // executed and, if the |queue_| is not empty, the next module will be sent
   // for inspection.
-  void OnInspectionFinished(
-      const ModuleInfoKey& module_key,
-      std::unique_ptr<ModuleInspectionResult> inspection_result);
+  void OnInspectionFinished(const ModuleInfoKey& module_key,
+                            ModuleInspectionResult inspection_result);
 
   OnModuleInspectedCallback on_module_inspected_callback_;
 
@@ -75,7 +72,7 @@
   // The vector of paths to %env_var%, used to account for differences in
   // localization and where people keep their files.
   // e.g. c:\windows vs d:\windows
-  scoped_refptr<base::RefCountedData<StringMapping>> path_mapping_;
+  StringMapping path_mapping_;
 
   base::ThreadChecker thread_checker_;
 
diff --git a/chrome/browser/conflicts/module_inspector_win_unittest.cc b/chrome/browser/conflicts/module_inspector_win_unittest.cc
index 426c30d..b44a2ad 100644
--- a/chrome/browser/conflicts/module_inspector_win_unittest.cc
+++ b/chrome/browser/conflicts/module_inspector_win_unittest.cc
@@ -41,14 +41,12 @@
   }
 
   // Callback for ModuleInspector.
-  void OnModuleInspected(
-      const ModuleInfoKey& module_key,
-      std::unique_ptr<ModuleInspectionResult> inspection_result) {
+  void OnModuleInspected(const ModuleInfoKey& module_key,
+                         ModuleInspectionResult inspection_result) {
     inspected_modules_.push_back(std::move(inspection_result));
   }
 
-  const std::vector<std::unique_ptr<ModuleInspectionResult>>&
-  inspected_modules() {
+  const std::vector<ModuleInspectionResult>& inspected_modules() {
     return inspected_modules_;
   }
 
@@ -62,7 +60,7 @@
  private:
   ModuleInspector module_inspector_;
 
-  std::vector<std::unique_ptr<ModuleInspectionResult>> inspected_modules_;
+  std::vector<ModuleInspectionResult> inspected_modules_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleInspectorTest);
 };
@@ -77,7 +75,6 @@
   test_browser_thread_bundle_.RunUntilIdle();
 
   ASSERT_EQ(1u, inspected_modules().size());
-  EXPECT_TRUE(inspected_modules().front());
 }
 
 TEST_F(ModuleInspectorTest, MultipleModules) {
@@ -92,6 +89,4 @@
   test_browser_thread_bundle_.RunUntilIdle();
 
   EXPECT_EQ(5u, inspected_modules().size());
-  for (const auto& inspection_result : inspected_modules())
-    EXPECT_TRUE(inspection_result);
 }
diff --git a/chrome/browser/conflicts/module_list_filter_win_unittest.cc b/chrome/browser/conflicts/module_list_filter_win_unittest.cc
index e7a5de5..f1ef918 100644
--- a/chrome/browser/conflicts/module_list_filter_win_unittest.cc
+++ b/chrome/browser/conflicts/module_list_filter_win_unittest.cc
@@ -127,8 +127,8 @@
                                           module_time_date_stamp, 0),
                     std::forward_as_tuple());
 
-  result.second.inspection_result = std::make_unique<ModuleInspectionResult>();
-
+  result.second.inspection_result =
+      base::make_optional<ModuleInspectionResult>();
   result.second.inspection_result->basename = module_path.BaseName().value();
 
   return result;
diff --git a/chrome/browser/conflicts/third_party_metrics_recorder_win.cc b/chrome/browser/conflicts/third_party_metrics_recorder_win.cc
index f64d5a6e..748e882 100644
--- a/chrome/browser/conflicts/third_party_metrics_recorder_win.cc
+++ b/chrome/browser/conflicts/third_party_metrics_recorder_win.cc
@@ -54,10 +54,10 @@
   const CertificateInfo& certificate_info =
       module_data.inspection_result->certificate_info;
   module_count_++;
-  if (certificate_info.type != CertificateType::NO_CERTIFICATE) {
+  if (certificate_info.type != CertificateInfo::Type::NO_CERTIFICATE) {
     ++signed_module_count_;
 
-    if (certificate_info.type == CertificateType::CERTIFICATE_IN_CATALOG)
+    if (certificate_info.type == CertificateInfo::Type::CERTIFICATE_IN_CATALOG)
       ++catalog_module_count_;
 
     base::StringPiece16 certificate_subject = certificate_info.subject;
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.cc b/chrome/browser/devtools/device/port_forwarding_controller.cc
index 9953976..6950f17 100644
--- a/chrome/browser/devtools/device/port_forwarding_controller.cc
+++ b/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -36,6 +36,7 @@
 #include "net/log/net_log_with_source.h"
 #include "net/socket/tcp_client_socket.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/blink/public/public_buildflags.h"
@@ -154,7 +155,7 @@
 using ResolveHostCallback = base::OnceCallback<void(net::AddressList)>;
 
 // This class is created and runs on BrowserThread::UI thread.
-class PortForwardingHostResolver : public network::mojom::ResolveHostClient {
+class PortForwardingHostResolver : public network::ResolveHostClientBase {
  public:
   PortForwardingHostResolver(Profile* profile,
                              const std::string& host,
diff --git a/chrome/browser/devtools/device/tcp_device_provider.cc b/chrome/browser/devtools/device/tcp_device_provider.cc
index b66257d..b40807d3 100644
--- a/chrome/browser/devtools/device/tcp_device_provider.cc
+++ b/chrome/browser/devtools/device/tcp_device_provider.cc
@@ -21,6 +21,7 @@
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
 #include "net/socket/tcp_client_socket.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
 namespace {
@@ -35,8 +36,7 @@
   callback.Run(result, std::move(socket));
 }
 
-class ResolveHostAndOpenSocket final
-    : public network::mojom::ResolveHostClient {
+class ResolveHostAndOpenSocket final : public network::ResolveHostClientBase {
  public:
   ResolveHostAndOpenSocket(const net::HostPortPair& address,
                            const AdbClientSocket::SocketCallback& callback,
diff --git a/chrome/browser/dom_distiller/dom_distiller_service_factory.cc b/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
index eb3cdcd5..943b5b2 100644
--- a/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
+++ b/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
@@ -15,8 +15,8 @@
 #include "components/dom_distiller/core/distiller.h"
 #include "components/dom_distiller/core/dom_distiller_store.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -59,9 +59,8 @@
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
 
-  std::unique_ptr<leveldb_proto::ProtoDatabaseImpl<ArticleEntry>> db(
-      new leveldb_proto::ProtoDatabaseImpl<ArticleEntry>(
-          background_task_runner));
+  auto db = leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<ArticleEntry>(
+      background_task_runner);
 
   base::FilePath database_dir(
       profile->GetPath().Append(FILE_PATH_LITERAL("Articles")));
diff --git a/chrome/browser/download/download_offline_content_provider.cc b/chrome/browser/download/download_offline_content_provider.cc
index e499bed..18b976b3 100644
--- a/chrome/browser/download/download_offline_content_provider.cc
+++ b/chrome/browser/download/download_offline_content_provider.cc
@@ -32,7 +32,8 @@
 const int kThumbnailSizeInDP = 64;
 
 bool ShouldShowDownloadItem(const download::DownloadItem* item) {
-  return !item->IsTemporary() && !item->IsTransient() && !item->IsDangerous();
+  return !item->IsTemporary() && !item->IsTransient() && !item->IsDangerous() &&
+         !item->GetTargetFilePath().empty();
 }
 
 }  // namespace
diff --git a/chrome/browser/download/download_shelf_controller.cc b/chrome/browser/download/download_shelf_controller.cc
index 94942ca6..ee8e314 100644
--- a/chrome/browser/download/download_shelf_controller.cc
+++ b/chrome/browser/download/download_shelf_controller.cc
@@ -65,7 +65,7 @@
 
 void DownloadShelfController::OnNewOfflineItemReady(
     DownloadUIModel::DownloadUIModelPtr model) {
-  Browser* browser = browser = chrome::FindLastActiveWithProfile(profile_);
+  Browser* browser = chrome::FindLastActiveWithProfile(profile_);
 
   if (browser && browser->window()) {
     // Add the offline item to DownloadShelf in the browser window.
diff --git a/chrome/browser/download/offline_item_utils.cc b/chrome/browser/download/offline_item_utils.cc
index c20e658..d9fce380 100644
--- a/chrome/browser/download/offline_item_utils.cc
+++ b/chrome/browser/download/offline_item_utils.cc
@@ -77,8 +77,7 @@
   item.externally_removed = download_item->GetFileExternallyRemoved();
   item.creation_time = download_item->GetStartTime();
   item.last_accessed_time = download_item->GetLastAccessTime();
-  item.is_openable = download_item->CanOpenDownload() &&
-                     blink::IsSupportedMimeType(download_item->GetMimeType());
+  item.is_openable = download_item->CanOpenDownload();
   item.file_path = download_item->GetTargetFilePath();
   item.mime_type = download_item->GetMimeType();
 
@@ -87,6 +86,7 @@
   item.is_off_the_record = off_the_record;
 
   item.is_resumable = download_item->CanResume();
+  item.allow_metered = download_item->AllowMetered();
   item.received_bytes = download_item->GetReceivedBytes();
   item.is_dangerous = download_item->IsDangerous();
 
@@ -94,7 +94,6 @@
   bool time_remaining_known = download_item->TimeRemaining(&time_delta);
   item.time_remaining_ms = time_remaining_known ? time_delta.InMilliseconds()
                                                 : kUnknownRemainingTime;
-  // TODO(crbug.com/857549): Add allow_metered, fail_state, pending_state.
   item.fail_state =
       ConvertDownloadInterruptReasonToFailState(download_item->GetLastReason());
   switch (download_item->GetState()) {
@@ -110,14 +109,21 @@
     case DownloadItem::CANCELLED:
       item.state = OfflineItemState::CANCELLED;
       break;
-    case DownloadItem::INTERRUPTED:
-      item.state = download_item->CanResume() ? OfflineItemState::INTERRUPTED
-                                              : OfflineItemState::FAILED;
-      break;
+    case DownloadItem::INTERRUPTED: {
+      item.state =
+          download_item->IsPaused()
+              ? OfflineItemState::PAUSED
+              : (download_item->CanResume() ? OfflineItemState::INTERRUPTED
+                                            : OfflineItemState::FAILED);
+    } break;
     default:
       NOTREACHED();
   }
 
+  // TODO(crbug.com/857549): Set pending_state correctly.
+  item.pending_state = item.state == OfflineItemState::INTERRUPTED
+                           ? PendingState::PENDING_NETWORK
+                           : PendingState::NOT_PENDING;
   item.progress.value = download_item->GetReceivedBytes();
   if (download_item->PercentComplete() != -1)
     item.progress.max = download_item->GetTotalBytes();
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 9d89cdc..4a5d0c4 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -319,9 +319,12 @@
   if (!EventRouter::Get(profile_))
     return;
 
-  std::unique_ptr<base::Value> result = base::JSONReader::Read(message);
-  if (!result || !result->is_dict())
+  std::unique_ptr<base::Value> result =
+      base::JSONReader::Read(message, base::JSON_REPLACE_INVALID_CHARACTERS);
+  if (!result || !result->is_dict()) {
+    LOG(ERROR) << "Tried to send invalid message to extension: " << message;
     return;
+  }
   base::DictionaryValue* dictionary =
       static_cast<base::DictionaryValue*>(result.get());
 
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 46160c0..9904fc1 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -46,7 +46,6 @@
 #include "components/signin/core/browser/account_fetcher_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/signin_buildflags.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 0714d12..ea738af7 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -382,7 +382,9 @@
   features->AppendString(GenerateFeatureFlag("spellcheck", config.spell_check));
   features->AppendString(
       GenerateFeatureFlag("handwriting", config.handwriting));
-
+  features->AppendString(GenerateFeatureFlag(
+      "handwritinggesture",
+      base::FeatureList::IsEnabled(features::kHandwritingGesture)));
   results->Set("features", std::move(features));
 
   std::move(on_settings_callback).Run(std::move(results));
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 6eca1a7..667af46 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_piece.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_web_ui.h"
 #include "chrome/browser/extensions/extension_webkit_preferences.h"
@@ -867,6 +868,20 @@
 }
 
 // static
+bool ChromeContentBrowserClientExtensionsPart::IsBuiltinComponent(
+    content::BrowserContext* browser_context,
+    const url::Origin& origin) {
+  if (origin.scheme() != extensions::kExtensionScheme)
+    return false;
+
+  const auto& extension_id = origin.host();
+  return ExtensionSystem::Get(browser_context)
+      ->extension_service()
+      ->component_loader()
+      ->Exists(extension_id);
+}
+
+// static
 void ChromeContentBrowserClientExtensionsPart::RecordShouldAllowOpenURLFailure(
     ShouldAllowOpenURLFailureReason reason,
     const GURL& site_url) {
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
index 8933e7ae..b3ea9d7c 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
@@ -100,6 +100,9 @@
       network::mojom::TrustedURLLoaderHeaderClientPtrInfo* header_client,
       const url::Origin& request_initiator);
 
+  static bool IsBuiltinComponent(content::BrowserContext* browser_context,
+                                 const url::Origin& origin);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(ChromeContentBrowserClientExtensionsPartTest,
                            ShouldAllowOpenURLMetricsForEmptySiteURL);
@@ -156,4 +159,3 @@
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_CHROME_CONTENT_BROWSER_CLIENT_EXTENSIONS_PART_H_
-
diff --git a/chrome/browser/extensions/updater/chrome_extension_downloader_factory.cc b/chrome/browser/extensions/updater/chrome_extension_downloader_factory.cc
index 43095db..c2b8879 100644
--- a/chrome/browser/extensions/updater/chrome_extension_downloader_factory.cc
+++ b/chrome/browser/extensions/updater/chrome_extension_downloader_factory.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/google/google_brand.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/update_client/update_query_params.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/service_manager_connection.h"
@@ -62,7 +61,7 @@
       delegate, connector, profile->GetPath());
 
   // NOTE: It is not obvious why it is OK to pass raw pointers to the token
-  // service and signin manager here. The logic is as follows:
+  // service and identity manager here. The logic is as follows:
   // ExtensionDownloader is owned by ExtensionUpdater.
   // ExtensionUpdater is owned by ExtensionService.
   // ExtensionService is owned by ExtensionSystemImpl::Shared.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d6add47..bfa9e24 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1199,11 +1199,6 @@
     "expiry_milestone": 78
   },
   {
-    "name": "enable-experimental-crostini-ui",
-    "owners": [ "nverne", "benwells" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-experimental-productivity-features",
     "owners": [ "feature-control@chromium.org" ],
     "expiry_milestone": 76
@@ -1214,11 +1209,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "enable-fast-unload",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-first-run-ui-transitions",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
@@ -2333,6 +2323,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "handwriting-gesture",
+    "owners": ["shend"],
+    "expiry_milestone": 76
+  },
+  {
     "name": "happiness-tracking-surveys-for-desktop",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 68a2429..71f1dce 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -966,10 +966,6 @@
     "Enables the use of experimental canvas features which are still in "
     "development.";
 
-const char kExperimentalCrostiniUIName[] = "Experimental Crostini";
-const char kExperimentalCrostiniUIDescription[] =
-    "Enables in-development Crostini features.";
-
 const char kExperimentalExtensionApisName[] = "Experimental Extension APIs";
 const char kExperimentalExtensionApisDescription[] =
     "Enables experimental extension APIs. Note that the extension gallery "
@@ -1014,11 +1010,6 @@
     "Enables running extensions on chrome:// URLs, where extensions explicitly "
     "request this permission.";
 
-const char kFastUnloadName[] = "Fast tab/window close";
-const char kFastUnloadDescription[] =
-    "Enables fast tab/window closing - runs a tab's onunload js handler "
-    "independently of the GUI.";
-
 const char kFeaturePolicyName[] = "Feature Policy";
 const char kFeaturePolicyDescription[] =
     "Enables granting and removing access to features through the "
@@ -1094,6 +1085,10 @@
     "#views-browser-windows on Mac. This feature is activated if either this "
     "flag or #upcoming-ui-features is enabled";
 
+const char kHandwritingGestureName[] = "Handwriting Gestures";
+const char kHandwritingGestureDescription[] =
+    "Enables handwriting gestures within the virtual keyboard";
+
 const char kHideActiveAppsFromShelfName[] =
     "Hide running apps (that are not pinned) from the shelf";
 const char kHideActiveAppsFromShelfDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f33ceb7c..933e17f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -588,9 +588,6 @@
 extern const char kExperimentalCanvasFeaturesName[];
 extern const char kExperimentalCanvasFeaturesDescription[];
 
-extern const char kExperimentalCrostiniUIName[];
-extern const char kExperimentalCrostiniUIDescription[];
-
 extern const char kExperimentalExtensionApisName[];
 extern const char kExperimentalExtensionApisDescription[];
 
@@ -612,9 +609,6 @@
 extern const char kExtensionsOnChromeUrlsName[];
 extern const char kExtensionsOnChromeUrlsDescription[];
 
-extern const char kFastUnloadName[];
-extern const char kFastUnloadDescription[];
-
 extern const char kFeaturePolicyName[];
 extern const char kFeaturePolicyDescription[];
 
@@ -658,6 +652,9 @@
 extern const char kGoogleProfileInfoName[];
 extern const char kGoogleProfileInfoDescription[];
 
+extern const char kHandwritingGestureName[];
+extern const char kHandwritingGestureDescription[];
+
 extern const char kHarfbuzzRendertextName[];
 extern const char kHarfbuzzRendertextDescription[];
 
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index a9d2242f..8fd2a4c 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -260,9 +260,7 @@
 
 }  // namespace
 
-class BrowserCloseManagerBrowserTest
-    : public InProcessBrowserTest,
-      public testing::WithParamInterface<bool> {
+class BrowserCloseManagerBrowserTest : public InProcessBrowserTest {
  protected:
   void SetUpOnMainThread() override {
     SessionStartupPref::SetStartupPref(
@@ -277,8 +275,6 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    if (GetParam())
-      command_line->AppendSwitch(switches::kEnableFastUnload);
 #if defined(OS_CHROMEOS)
     command_line->AppendSwitch(
         chromeos::switches::kIgnoreUserProfileMappingForTests);
@@ -313,7 +309,7 @@
   std::vector<Browser*> browsers_;
 };
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, TestSingleTabShutdown) {
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, TestSingleTabShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/beforeunload.html")));
   PrepareForDialog(browser());
@@ -335,7 +331,7 @@
   EXPECT_TRUE(BrowserList::GetInstance()->empty());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestShutdownMoreThanOnce) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/beforeunload.html")));
@@ -360,8 +356,7 @@
   EXPECT_TRUE(BrowserList::GetInstance()->empty());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
-                       PRE_TestSessionRestore) {
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, PRE_TestSessionRestore) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/beforeunload.html")));
   AddBlankTabAndShow(browser());
@@ -402,7 +397,7 @@
 #else
 #define MAYBE_TestSessionRestore TestSessionRestore
 #endif
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        MAYBE_TestSessionRestore) {
   // The testing framework launches Chrome with about:blank as args.
   EXPECT_EQ(2, browser()->tab_strip_model()->count());
@@ -414,7 +409,7 @@
 
 // Test that browser windows are only closed if all browsers are ready to close
 // and that all beforeunload dialogs are shown again after a cancel.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, TestMultipleWindows) {
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, TestMultipleWindows) {
   browsers_.push_back(CreateBrowser(browser()->profile()));
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -462,7 +457,7 @@
 // Test that tabs in the same window with a beforeunload event that hangs are
 // treated the same as the user accepting the close, but do not close the tab
 // early.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestHangInBeforeUnloadMultipleTabs) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload_hang.html")));
@@ -497,7 +492,7 @@
 // Test that tabs in different windows with a beforeunload event that hangs are
 // treated the same as the user accepting the close, but do not close the tab
 // early.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestHangInBeforeUnloadMultipleWindows) {
   browsers_.push_back(CreateBrowser(browser()->profile()));
   browsers_.push_back(CreateBrowser(browser()->profile()));
@@ -540,7 +535,7 @@
 #else
 #define MAYBE_TestUnloadMultipleSlowTabs TestUnloadMultipleSlowTabs
 #endif
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        MAYBE_TestUnloadMultipleSlowTabs) {
   const int kTabCount = 5;
   const int kResposiveTabIndex = 2;
@@ -594,7 +589,7 @@
 #define MAYBE_TestBeforeUnloadMultipleSlowWindows \
   TestBeforeUnloadMultipleSlowWindows
 #endif
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        MAYBE_TestBeforeUnloadMultipleSlowWindows) {
   const int kBrowserCount = 5;
   const int kResposiveBrowserIndex = 2;
@@ -646,7 +641,7 @@
 #endif
 
 // Test that a window created during shutdown is closed.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        MAYBE_TestAddWindowDuringShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -664,7 +659,7 @@
 
 // Test that a window created during shutdown with a beforeunload handler can
 // cancel the shutdown.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestAddWindowWithBeforeUnloadDuringShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -696,7 +691,7 @@
 }
 
 // Test that tabs added during shutdown are closed.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestAddTabDuringShutdown) {
   browsers_.push_back(CreateBrowser(browser()->profile()));
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
@@ -721,7 +716,7 @@
 // Test that tabs created during shutdown with beforeunload handlers can cancel
 // the shutdown.
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestAddTabWithBeforeUnloadDuringShutdown) {
   browsers_.push_back(CreateBrowser(browser()->profile()));
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
@@ -771,13 +766,8 @@
 #define MAYBE_AddBeforeUnloadDuringClosing AddBeforeUnloadDuringClosing
 #endif
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        MAYBE_AddBeforeUnloadDuringClosing) {
-  // TODO(crbug.com/250305): Currently FastUnloadController is broken.
-  // And it is difficult to fix this issue without fixing that one.
-  if (GetParam())
-    return;
-
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/title1.html")));
 
@@ -867,7 +857,7 @@
             browser2->tab_strip_model()->GetWebContentsAt(1)->GetURL());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestCloseTabDuringShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -901,7 +891,7 @@
   EXPECT_TRUE(BrowserList::GetInstance()->empty());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestOpenAndCloseWindowDuringShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -935,7 +925,7 @@
   EXPECT_TRUE(BrowserList::GetInstance()->empty());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestCloseWindowDuringShutdown) {
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
       browsers_[0], embedded_test_server()->GetURL("/beforeunload.html")));
@@ -972,7 +962,7 @@
 // BrowserCloseManager should simply close all browsers. If there are no
 // browsers, it should not crash.
 #if defined(OS_MACOSX)
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, TestWithDownloads) {
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, TestWithDownloads) {
   ASSERT_NO_FATAL_FAILURE(CreateStalledDownload(browser()));
 
   RepeatedNotificationObserver close_observer(
@@ -992,7 +982,7 @@
 #else  // defined(OS_MACOSX)
 
 // Test shutdown with a DANGEROUS_URL download undecided.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestWithDangerousUrlDownload) {
   // Set up the fake delegate that forces the download to be malicious.
   std::unique_ptr<TestDownloadManagerDelegate> test_delegate(
@@ -1031,7 +1021,7 @@
 }
 
 // Test shutdown with a download in progress.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest, TestWithDownloads) {
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest, TestWithDownloads) {
   ASSERT_NO_FATAL_FAILURE(CreateStalledDownload(browser()));
   content::TestNavigationObserver navigation_observer(
       browser()->tab_strip_model()->GetActiveWebContents(), 1);
@@ -1057,7 +1047,7 @@
 }
 
 // Test shutdown with a download in progress in an off-the-record profile.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestWithOffTheRecordDownloads) {
   Profile* otr_profile = browser()->profile()->GetOffTheRecordProfile();
   Browser* otr_browser = CreateBrowser(otr_profile);
@@ -1094,7 +1084,7 @@
 // parent profile.
 // TODO(https://crbug.com/844019): Fix the notification expectation around the
 // call to AttemptClose.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        DISABLED_TestWithOffTheRecordWindowAndRegularDownload) {
   Profile* otr_profile = browser()->profile()->GetOffTheRecordProfile();
   Browser* otr_browser = CreateBrowser(otr_profile);
@@ -1141,7 +1131,7 @@
 
 // Test shutdown with a download in progress from one profile, where the only
 // open windows are for another profile.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        TestWithDownloadsFromDifferentProfiles) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   Profile* other_profile = nullptr;
@@ -1195,7 +1185,7 @@
 
 // Fails on ChromeOS and Linux, times out on Win. crbug.com/749098
 // Test shutdown with downloads in progress and beforeunload handlers.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerBrowserTest,
                        DISABLED_TestBeforeUnloadAndDownloads) {
   ASSERT_NO_FATAL_FAILURE(CreateStalledDownload(browser()));
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
@@ -1223,10 +1213,6 @@
 
 #endif  // defined(OS_MACOSX)
 
-INSTANTIATE_TEST_CASE_P(BrowserCloseManagerBrowserTest,
-                        BrowserCloseManagerBrowserTest,
-                        testing::Bool());
-
 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
 
 class BrowserCloseManagerWithBackgroundModeBrowserTest
@@ -1253,7 +1239,7 @@
 // Check that background mode is suspended when closing all browsers unless we
 // are quitting and that background mode is resumed when a new browser window is
 // opened.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerWithBackgroundModeBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerWithBackgroundModeBrowserTest,
                        CloseAllBrowsersWithBackgroundMode) {
   EXPECT_FALSE(IsBackgroundModeSuspended());
   std::unique_ptr<ScopedKeepAlive> tmp_keep_alive;
@@ -1289,7 +1275,7 @@
 
 // Check that closing the last browser window individually does not affect
 // background mode.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerWithBackgroundModeBrowserTest,
+IN_PROC_BROWSER_TEST_F(BrowserCloseManagerWithBackgroundModeBrowserTest,
                        DISABLED_CloseSingleBrowserWithBackgroundMode) {
   RepeatedNotificationObserver close_observer(
       chrome::NOTIFICATION_BROWSER_CLOSED, 1);
@@ -1303,8 +1289,9 @@
 
 // Check that closing all browsers with no browser windows open suspends
 // background mode but does not cause Chrome to quit.
-IN_PROC_BROWSER_TEST_P(BrowserCloseManagerWithBackgroundModeBrowserTest,
-                       DISABLED_CloseAllBrowsersWithNoOpenBrowsersWithBackgroundMode) {
+IN_PROC_BROWSER_TEST_F(
+    BrowserCloseManagerWithBackgroundModeBrowserTest,
+    DISABLED_CloseAllBrowsersWithNoOpenBrowsersWithBackgroundMode) {
   RepeatedNotificationObserver close_observer(
       chrome::NOTIFICATION_BROWSER_CLOSED, 1);
   EXPECT_FALSE(IsBackgroundModeSuspended());
@@ -1322,7 +1309,4 @@
   EXPECT_TRUE(IsBackgroundModeSuspended());
 }
 
-INSTANTIATE_TEST_CASE_P(BrowserCloseManagerWithBackgroundModeBrowserTest,
-                        BrowserCloseManagerWithBackgroundModeBrowserTest,
-                        testing::Bool());
 #endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
diff --git a/chrome/browser/media/capture_access_handler_base.cc b/chrome/browser/media/capture_access_handler_base.cc
index b29f67e..7943d68 100644
--- a/chrome/browser/media/capture_access_handler_base.cc
+++ b/chrome/browser/media/capture_access_handler_base.cc
@@ -147,14 +147,15 @@
   return false;
 }
 
-void CaptureAccessHandlerBase::UpdateCapturingLinkSecured(int render_process_id,
-                                                          int render_frame_id,
-                                                          int page_request_id,
-                                                          bool is_secure) {
+void CaptureAccessHandlerBase::UpdateVideoScreenCaptureStatus(
+    int render_process_id,
+    int render_frame_id,
+    int page_request_id,
+    bool is_secure) {
   auto it = FindSession(render_process_id, render_frame_id, page_request_id);
   if (it != sessions_.end()) {
     it->is_capturing_link_secure = is_secure;
-    DVLOG(2) << "UpdateCapturingLinkSecured:"
+    DVLOG(2) << "UpdateVideoScreenCaptureStatus:"
              << " render_process_id: " << render_process_id
              << " render_frame_id: " << render_frame_id
              << " page_request_id: " << page_request_id
diff --git a/chrome/browser/media/capture_access_handler_base.h b/chrome/browser/media/capture_access_handler_base.h
index 1164694..0477b8f5 100644
--- a/chrome/browser/media/capture_access_handler_base.h
+++ b/chrome/browser/media/capture_access_handler_base.h
@@ -26,16 +26,19 @@
                                blink::MediaStreamType stream_type,
                                content::MediaRequestState state) override;
 
-  // Return true if there is any ongoing insecured capturing. The capturing is
-  // deemed secure if all connected video sinks are reported secure and the
-  // connections to the sinks are being managed by a trusted extension.
+  // Returns true if there is any ongoing insecured capturing. Returns false
+  // otherwise, e.g. there is no capturing, or all capturing are secure. A
+  // capturing is deemed secure if all connected video sinks are reported secure
+  // and the connections to the sinks are also secure, e.g. being managed by a
+  // trusted extension.
   bool IsInsecureCapturingInProgress(int render_process_id,
                                      int render_frame_id) override;
 
-  void UpdateCapturingLinkSecured(int render_process_id,
-                                  int render_frame_id,
-                                  int page_request_id,
-                                  bool is_secure) override;
+  // Updates video screen capture status with whether it |is_secure| or not.
+  void UpdateVideoScreenCaptureStatus(int render_process_id,
+                                      int render_frame_id,
+                                      int page_request_id,
+                                      bool is_secure) override;
 
  protected:
   static bool IsExtensionWhitelistedForScreenCapture(
diff --git a/chrome/browser/media/media_access_handler.h b/chrome/browser/media/media_access_handler.h
index 39c630e4..142ffb88 100644
--- a/chrome/browser/media/media_access_handler.h
+++ b/chrome/browser/media/media_access_handler.h
@@ -60,10 +60,10 @@
                                              int render_frame_id);
 
   //  Update any ongoing insecured capturing state.
-  virtual void UpdateCapturingLinkSecured(int render_process_id,
-                                          int render_frame_id,
-                                          int page_request_id,
-                                          bool is_secure) {}
+  virtual void UpdateVideoScreenCaptureStatus(int render_process_id,
+                                              int render_frame_id,
+                                              int page_request_id,
+                                              bool is_secure) {}
 
  protected:
   // Helper function for derived classes which takes in whether audio/video
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
index e5925d8b..fc3965e 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -461,34 +461,30 @@
     blink::MediaStreamType stream_type,
     bool is_secure) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (stream_type != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE &&
-      stream_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE &&
-      stream_type != blink::MEDIA_DISPLAY_VIDEO_CAPTURE) {
+
+  if (!IsVideoScreenCaptureMediaType(stream_type))
     return;
-  }
+
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&MediaCaptureDevicesDispatcher::UpdateCapturingLinkSecured,
-                     base::Unretained(this), render_process_id, render_frame_id,
-                     page_request_id, stream_type, is_secure));
+      base::BindOnce(
+          &MediaCaptureDevicesDispatcher::UpdateVideoScreenCaptureStatus,
+          base::Unretained(this), render_process_id, render_frame_id,
+          page_request_id, stream_type, is_secure));
 }
 
-void MediaCaptureDevicesDispatcher::UpdateCapturingLinkSecured(
+void MediaCaptureDevicesDispatcher::UpdateVideoScreenCaptureStatus(
     int render_process_id,
     int render_frame_id,
     int page_request_id,
     blink::MediaStreamType stream_type,
     bool is_secure) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (stream_type != blink::MEDIA_GUM_TAB_VIDEO_CAPTURE &&
-      stream_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE &&
-      stream_type != blink::MEDIA_DISPLAY_VIDEO_CAPTURE) {
-    return;
-  }
+  DCHECK(IsVideoScreenCaptureMediaType(stream_type));
 
   for (const auto& handler : media_access_handlers_) {
-    handler->UpdateCapturingLinkSecured(render_process_id, render_frame_id,
-                                        page_request_id, is_secure);
+    handler->UpdateVideoScreenCaptureStatus(render_process_id, render_frame_id,
+                                            page_request_id, is_secure);
     break;
   }
 }
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
index 6064834..5d9cbce 100644
--- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
+++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.h
@@ -178,11 +178,11 @@
                                          content::MediaRequestState state);
   void OnCreatingAudioStreamOnUIThread(int render_process_id,
                                        int render_frame_id);
-  void UpdateCapturingLinkSecured(int render_process_id,
-                                  int render_frame_id,
-                                  int page_request_id,
-                                  blink::MediaStreamType stream_type,
-                                  bool is_secure);
+  void UpdateVideoScreenCaptureStatus(int render_process_id,
+                                      int render_frame_id,
+                                      int page_request_id,
+                                      blink::MediaStreamType stream_type,
+                                      bool is_secure);
 
   // Only for testing, a list of cached audio capture devices.
   blink::MediaStreamDevices test_audio_devices_;
diff --git a/chrome/browser/metrics/field_trial_synchronizer.cc b/chrome/browser/metrics/field_trial_synchronizer.cc
index cd17b03..235098e 100644
--- a/chrome/browser/metrics/field_trial_synchronizer.cc
+++ b/chrome/browser/metrics/field_trial_synchronizer.cc
@@ -37,9 +37,10 @@
   for (content::RenderProcessHost::iterator it(
           content::RenderProcessHost::AllHostsIterator());
        !it.IsAtEnd(); it.Advance()) {
-    IPC::ChannelProxy* channel = it.GetCurrentValue()->GetChannel();
+    auto* host = it.GetCurrentValue();
+    IPC::ChannelProxy* channel = host->GetChannel();
     // channel might be null in tests.
-    if (channel) {
+    if (host->IsInitializedAndNotDead() && channel) {
       chrome::mojom::RendererConfigurationAssociatedPtr renderer_configuration;
       channel->GetRemoteAssociatedInterface(&renderer_configuration);
       renderer_configuration->SetFieldTrialGroup(field_trial_name, group_name);
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 1648a6dee..6f81883 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -341,7 +341,8 @@
     // cookies) in tests. Without this, the real GaiaCookieManagerService would
     // try talking to Google servers which of course wouldn't work in tests.
     fake_gaia_cookie_manager_factory_ =
-        secondary_account_helper::SetUpFakeGaiaCookieManagerService();
+        secondary_account_helper::SetUpFakeGaiaCookieManagerService(
+            &test_url_loader_factory_);
     UkmBrowserTest::SetUpInProcessBrowserTestFixture();
   }
 
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index c34349ac..f279366 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -2077,7 +2077,8 @@
   this->ExpectOfflinePageAccessCount(offline_id2, 0);
 }
 
-TYPED_TEST(OfflinePageRequestHandlerTest, EmptyFile) {
+// Disabled due to https://crbug.com/917113.
+TYPED_TEST(OfflinePageRequestHandlerTest, DISABLED_EmptyFile) {
   this->SimulateHasNetworkConnectivity(false);
 
   const std::string expected_data("");
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
index 74f03ee..0c7a5fac8 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
@@ -28,7 +28,7 @@
 namespace {
 
 #define ADS_HISTOGRAM(suffix, hist_macro, value) \
-  hist_macro("PageLoad.Clients.Ads.SubresourceFilter." suffix, value);
+  hist_macro("PageLoad.Clients.Ads." suffix, value);
 
 #define RESOURCE_BYTES_HISTOGRAM(suffix, was_cached, value)                \
   if (was_cached) {                                                        \
@@ -77,7 +77,7 @@
     AdOriginStatus origin_status,
     bool frame_navigated)
     : frame_bytes(0u),
-      frame_bytes_uncached(0u),
+      frame_network_bytes(0u),
       frame_tree_node_id(frame_tree_node_id),
       origin_status(origin_status),
       frame_navigated(frame_navigated) {}
@@ -366,23 +366,24 @@
     return;
   }
 
-  if (resource->is_complete) {
+  // |delta_bytes| only includes bytes used by the network.
+  page_bytes_ += resource->delta_bytes;
+  page_network_bytes_ += resource->delta_bytes;
+  if (resource->is_complete && resource->was_fetched_via_cache)
     page_bytes_ += resource->encoded_body_length;
-    if (!resource->was_fetched_via_cache)
-      uncached_page_bytes_ += resource->encoded_body_length;
-  }
 
   // Determine if the frame (or its ancestor) is an ad, if so attribute the
   // bytes to the highest ad ancestor.
   AdFrameData* ancestor_data = id_and_data->second;
+  if (!ancestor_data)
+    return;
 
-  if (ancestor_data) {
-    if (resource->is_complete) {
-      ancestor_data->frame_bytes += resource->encoded_body_length;
-      if (!resource->was_fetched_via_cache)
-        ancestor_data->frame_bytes_uncached += resource->encoded_body_length;
-    }
-  }
+  ancestor_data->frame_bytes += resource->delta_bytes;
+  ancestor_data->frame_network_bytes += resource->delta_bytes;
+
+  // Report cached resource body bytes to overall frame bytes.
+  if (resource->is_complete && resource->was_fetched_via_cache)
+    ancestor_data->frame_bytes += resource->encoded_body_length;
 }
 
 AdsPageLoadMetricsObserver::ResourceMimeType
@@ -419,7 +420,6 @@
   if (it == page_resources_.end())
     total_number_page_resources_++;
 
-  page_resource_bytes_ += resource->delta_bytes;
   if (resource->reported_as_ad_resource) {
     // If the resource had already started loading, and is now labeled as an ad,
     // but was not before, we need to account for all the previously received
@@ -518,8 +518,6 @@
   // Only records histograms on pages that have some ad bytes.
   if (page_ad_resource_bytes_ == 0)
     return;
-  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Total",
-                       page_resource_bytes_);
   PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Ads",
                        page_ad_resource_bytes_);
   PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds",
@@ -532,7 +530,7 @@
 
   auto* ukm_recorder = ukm::UkmRecorder::Get();
   ukm::builders::AdPageLoad builder(source_id);
-  builder.SetTotalBytes(page_resource_bytes_ >> 10)
+  builder.SetTotalBytes(page_bytes_ >> 10)
       .SetAdBytes(page_ad_resource_bytes_ >> 10)
       .SetAdJavascriptBytes(page_ad_javascript_bytes_ >> 10)
       .SetAdVideoBytes(page_ad_video_bytes_ >> 10);
@@ -571,7 +569,7 @@
 
   int non_zero_ad_frames = 0;
   size_t total_ad_frame_bytes = 0;
-  size_t uncached_ad_frame_bytes = 0;
+  size_t ad_frame_network_bytes = 0;
 
   for (const AdFrameData& ad_frame_data : ad_frames_data_storage_) {
     if (ad_frame_data.frame_bytes == 0)
@@ -579,23 +577,23 @@
 
     non_zero_ad_frames += 1;
     total_ad_frame_bytes += ad_frame_data.frame_bytes;
+    ad_frame_network_bytes += ad_frame_data.frame_network_bytes;
 
-    uncached_ad_frame_bytes += ad_frame_data.frame_bytes_uncached;
     ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Total", PAGE_BYTES_HISTOGRAM,
                   ad_frame_data.frame_bytes);
     ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Network", PAGE_BYTES_HISTOGRAM,
-                  ad_frame_data.frame_bytes_uncached);
+                  ad_frame_data.frame_network_bytes);
     ADS_HISTOGRAM(
         "Bytes.AdFrames.PerFrame.PercentNetwork", UMA_HISTOGRAM_PERCENTAGE,
-
-        ad_frame_data.frame_bytes_uncached * 100 / ad_frame_data.frame_bytes);
-    ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.OriginStatus",
-                  UMA_HISTOGRAM_ENUMERATION, ad_frame_data.origin_status);
+        ad_frame_data.frame_network_bytes * 100 / ad_frame_data.frame_bytes);
+    ADS_HISTOGRAM(
+        "SubresourceFilter.FrameCounts.AdFrames.PerFrame.OriginStatus",
+        UMA_HISTOGRAM_ENUMERATION, ad_frame_data.origin_status);
   }
 
   // TODO(ericrobinson): Consider renaming this to match
   //   'FrameCounts.AdFrames.PerFrame.OriginStatus'.
-  ADS_HISTOGRAM("FrameCounts.AnyParentFrame.AdFrames",
+  ADS_HISTOGRAM("SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames",
                 UMA_HISTOGRAM_COUNTS_1000, non_zero_ad_frames);
 
   // Don't post UMA for pages that don't have ads.
@@ -607,27 +605,26 @@
 
   ADS_HISTOGRAM("Bytes.FullPage.Total", PAGE_BYTES_HISTOGRAM, page_bytes_);
   ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM,
-                uncached_page_bytes_);
+                page_network_bytes_);
 
   if (page_bytes_) {
     ADS_HISTOGRAM("Bytes.FullPage.Total.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
                   total_ad_frame_bytes * 100 / page_bytes_);
   }
-  if (uncached_page_bytes_ > 0) {
+  if (page_network_bytes_ > 0) {
     ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-
-                  uncached_ad_frame_bytes * 100 / uncached_page_bytes_);
+                  ad_frame_network_bytes * 100 / page_network_bytes_);
   }
 
   ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
                 total_ad_frame_bytes);
   ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
-                uncached_ad_frame_bytes);
+                ad_frame_network_bytes);
 
   if (total_ad_frame_bytes) {
     ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.PercentNetwork",
                   UMA_HISTOGRAM_PERCENTAGE,
-                  uncached_ad_frame_bytes * 100 / total_ad_frame_bytes);
+                  ad_frame_network_bytes * 100 / total_ad_frame_bytes);
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
index 0ba3e67..cc22ba8 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
@@ -86,10 +86,10 @@
     AdFrameData(FrameTreeNodeId frame_tree_node_id,
                 AdOriginStatus origin_status,
                 bool frame_navigated);
-
-    // Total prefilter (body) bytes loaded for complete resources.
+    // Total bytes used to load resources on the page, including headers.
     size_t frame_bytes;
-    size_t frame_bytes_uncached;
+    size_t frame_network_bytes;
+
     const FrameTreeNodeId frame_tree_node_id;
     AdOriginStatus origin_status;
     bool frame_navigated;
@@ -177,7 +177,6 @@
   // entire page.
   size_t page_ad_javascript_bytes_ = 0u;
   size_t page_ad_video_bytes_ = 0u;
-  size_t page_resource_bytes_ = 0u;
   size_t page_ad_resource_bytes_ = 0u;
   size_t page_main_frame_ad_resource_bytes_ = 0u;
   uint32_t total_number_page_resources_ = 0;
@@ -192,7 +191,7 @@
   size_t page_ad_resource_bytes_since_interactive_ = 0u;
 
   size_t page_bytes_ = 0u;
-  size_t uncached_page_bytes_ = 0u;
+  size_t page_network_bytes_ = 0u;
   bool committed_ = false;
 
   ScopedObserver<subresource_filter::SubresourceFilterObserverManager,
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 6c815234..c4c8736 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -251,8 +251,7 @@
       "AdFrames",
       1, 1);
   histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.SubresourceFilter.Bytes.AdFrames.Aggregate.Total",
-      0 /* < 1 KB */, 1);
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0 /* < 1 KB */, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
@@ -502,7 +501,7 @@
       "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 2);
 
   histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Resources.Bytes.Total", 4, 1);
+      "PageLoad.Clients.Ads.Bytes.FullPage.Network", 4, 1);
   // We have received 4 KB of ads and 1 KB of toplevel ads.
   histogram_tester.ExpectBucketCount("PageLoad.Clients.Ads.Resources.Bytes.Ads",
                                      4, 1);
@@ -514,6 +513,65 @@
       "PageLoad.Clients.Ads.Resources.Bytes.Unfinished", 1, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       IncompleteResourcesRecordedToFrameMetrics) {
+  base::HistogramTester histogram_tester;
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ads_observer");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+
+  const char kHttpResponseHeader[] =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "\r\n";
+  auto incomplete_resource_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/incomplete_resource.js",
+          true /*relative_url_is_prefix*/);
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  browser()->OpenURL(content::OpenURLParams(
+      embedded_test_server()->GetURL("/ad_with_incomplete_resource.html"),
+      content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+      ui::PAGE_TRANSITION_TYPED, false));
+
+  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->Wait();
+  int64_t initial_page_bytes = waiter->current_network_bytes();
+
+  // Ad resource will not finish loading but should be reported to metrics.
+  incomplete_resource_response->WaitForRequest();
+  incomplete_resource_response->Send(kHttpResponseHeader);
+  incomplete_resource_response->Send(std::string(2048, ' '));
+
+  // Wait for the resource update to be received for the incomplete response.
+  waiter->AddMinimumNetworkBytesExpectation(2048);
+  waiter->Wait();
+
+  // Close all tabs instead of navigating as the embedded_test_server will
+  // hang waiting for loads to finish when we have an unfinished
+  // ControlledHttpResponse.
+  browser()->tab_strip_model()->CloseAllTabs();
+
+  int expected_page_kilobytes = (initial_page_bytes + 2048) / 1024;
+
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.FullPage.Network", expected_page_kilobytes,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Network", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Network", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Total", 2, 1);
+}
+
 // Verify that per-resource metrics are reported for cached resources and
 // resources loaded by the network.
 IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
index f805701..5e40344d 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
@@ -115,8 +115,7 @@
 };
 
 std::string SuffixedHistogram(const std::string& suffix) {
-  return base::StringPrintf("PageLoad.Clients.Ads.SubresourceFilter.%s",
-                            suffix.c_str());
+  return base::StringPrintf("PageLoad.Clients.Ads.%s", suffix.c_str());
 }
 
 // Verifies that the histograms match what is expected.
@@ -155,8 +154,9 @@
 
   // Test the histograms.
   histograms.ExpectUniqueSample(
-      SuffixedHistogram("FrameCounts.AnyParentFrame.AdFrames"), ad_frame_count,
-      1);
+      SuffixedHistogram(
+          "SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames"),
+      ad_frame_count, 1);
 
   if (ad_frame_count == 0)
     return;
@@ -318,8 +318,7 @@
 
   // Verify that other UMA wasn't written.
   histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.Ads.SubresourceFilter.Bytes.AdFrames.Aggregate.Total",
-      0);
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, PageWithAds) {
@@ -603,8 +602,7 @@
   // There shouldn't be any other histograms for a page with no ad
   // resources.
   EXPECT_EQ(1u, histogram_tester()
-                    .GetTotalCountsForPrefix(
-                        "PageLoad.Clients.Ads.SubresourceFilter.")
+                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
                     .size());
 }
 
@@ -629,8 +627,7 @@
 
   // There shouldn't be any histograms for an aborted main frame.
   EXPECT_EQ(0u, histogram_tester()
-                    .GetTotalCountsForPrefix(
-                        "PageLoad.Clients.Ads.SubresourceFilter.")
+                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
                     .size());
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
index 77de5c4f..398f6f2 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
@@ -18,6 +19,17 @@
 
 namespace previews {
 
+namespace {
+
+void RecordPageLoadExtraInfoMetrics(
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  UMA_HISTOGRAM_ENUMERATION(
+      internal::kHistogramOfflinePreviewsPageEndReason, info.page_end_reason,
+      page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
+}
+
+}  // namespace
+
 namespace internal {
 
 const char kHistogramOfflinePreviewsDOMContentLoadedEventFired[] =
@@ -34,6 +46,8 @@
     "NavigationToFirstContentfulPaint";
 const char kHistogramOfflinePreviewsParseStart[] =
     "PageLoad.Clients.Previews.OfflinePages.ParseTiming.NavigationToParseStart";
+const char kHistogramOfflinePreviewsPageEndReason[] =
+    "Previews.PageEndReason.Offline";
 
 }  // namespace internal
 
@@ -64,6 +78,20 @@
              : STOP_OBSERVING;
 }
 
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+OfflinePagePreviewsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  RecordPageLoadExtraInfoMetrics(info);
+  return STOP_OBSERVING;
+}
+
+void OfflinePagePreviewsPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  RecordPageLoadExtraInfoMetrics(info);
+}
+
 void OfflinePagePreviewsPageLoadMetricsObserver::OnDomContentLoadedEventStart(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
index db8e633..73d7e0a 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h
@@ -26,6 +26,7 @@
 extern const char kHistogramOfflinePreviewsLoadEventFired[];
 extern const char kHistogramOfflinePreviewsFirstContentfulPaint[];
 extern const char kHistogramOfflinePreviewsParseStart[];
+extern const char kHistogramOfflinePreviewsPageEndReason[];
 
 }  // namespace internal
 
@@ -40,6 +41,11 @@
   // page_load_metrics::PageLoadMetricsObserver:
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
+  ObservePolicy FlushMetricsOnAppEnterBackground(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
+                  const page_load_metrics::PageLoadExtraInfo& info) override;
   void OnDomContentLoadedEventStart(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& info) override;
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
index 01949c04..ca4ead85 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
@@ -70,31 +70,36 @@
   }
 
   void ValidateHistograms() {
-    ValidateHistogramsFor(
+    ValidateTimingHistogramsFor(
         internal::kHistogramOfflinePreviewsDOMContentLoadedEventFired,
         timing_.document_timing->dom_content_loaded_event_start);
-    ValidateHistogramsFor(internal::kHistogramOfflinePreviewsFirstLayout,
-                          timing_.document_timing->first_layout);
-    ValidateHistogramsFor(internal::kHistogramOfflinePreviewsLoadEventFired,
-                          timing_.document_timing->load_event_start);
-    ValidateHistogramsFor(
+    ValidateTimingHistogramsFor(internal::kHistogramOfflinePreviewsFirstLayout,
+                                timing_.document_timing->first_layout);
+    ValidateTimingHistogramsFor(
+        internal::kHistogramOfflinePreviewsLoadEventFired,
+        timing_.document_timing->load_event_start);
+    ValidateTimingHistogramsFor(
         internal::kHistogramOfflinePreviewsFirstContentfulPaint,
         timing_.paint_timing->first_contentful_paint);
-    ValidateHistogramsFor(internal::kHistogramOfflinePreviewsParseStart,
-                          timing_.parse_timing->parse_start);
+    ValidateTimingHistogramsFor(internal::kHistogramOfflinePreviewsParseStart,
+                                timing_.parse_timing->parse_start);
+    ValidateHistogramsFor(internal::kHistogramOfflinePreviewsPageEndReason,
+                          page_load_metrics::PageEndReason::END_NEW_NAVIGATION);
   }
 
-  void ValidateHistogramsFor(const std::string& histogram_,
-                             const base::Optional<base::TimeDelta>& event) {
-    histogram_tester().ExpectTotalCount(histogram_,
-                                        is_offline_preview_ ? 1 : 0);
+  void ValidateTimingHistogramsFor(
+      const std::string& histogram,
+      const base::Optional<base::TimeDelta>& event) {
+    ValidateHistogramsFor(histogram, static_cast<base::HistogramBase::Sample>(
+                                         event.value().InMilliseconds()));
+  }
+
+  void ValidateHistogramsFor(const std::string& histogram,
+                             const base::HistogramBase::Sample sample) {
+    histogram_tester().ExpectTotalCount(histogram, is_offline_preview_ ? 1 : 0);
     if (!is_offline_preview_)
       return;
-    histogram_tester().ExpectUniqueSample(
-        histogram_,
-        static_cast<base::HistogramBase::Sample>(
-            event.value().InMilliseconds()),
-        1);
+    histogram_tester().ExpectUniqueSample(histogram, sample, 1);
   }
 
  protected:
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index 35bc4632..cab0bd2 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -128,28 +128,6 @@
       .Record(ukm::UkmRecorder::Get());
 }
 
-void UkmPageLoadMetricsObserver::OnUserInput(
-    const blink::WebInputEvent& event,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (was_hidden_)
-    return;
-
-  switch (event.GetType()) {
-    // Mouse move/enter/leave are common events, and most pages don't insert new
-    // DOM elements for these events, so we ignore them.
-    case blink::WebInputEvent::kMouseMove:
-    case blink::WebInputEvent::kMouseEnter:
-    case blink::WebInputEvent::kMouseLeave:
-      return;
-    default:
-      break;
-  }
-
-  ukm::builders::PageLoad builder(extra_info.source_id);
-  RecordBeforeUserInputMetrics(&builder, timing, extra_info);
-}
-
 void UkmPageLoadMetricsObserver::OnComplete(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
@@ -291,10 +269,6 @@
   if (main_frame_timing_)
     ReportMainResourceTimingMetrics(timing, &builder);
 
-  // Ensure that before user input metrics are recorded for all page loads, even
-  // if no user input was processed during the page load lifetime.
-  RecordBeforeUserInputMetrics(&builder, timing, info);
-
   builder.Record(ukm::UkmRecorder::Get());
 }
 
@@ -348,28 +322,6 @@
   builder.Record(ukm::UkmRecorder::Get());
 }
 
-void UkmPageLoadMetricsObserver::RecordBeforeUserInputMetrics(
-    ukm::builders::PageLoad* builder,
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  if (recorded_before_user_input_metrics_)
-    return;
-  recorded_before_user_input_metrics_ = true;
-
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->largest_image_paint, extra_info)) {
-    builder
-        ->SetExperimental_PaintTiming_NavigationToLargestImagePaint_BeforeUserInput(
-            timing.paint_timing->largest_image_paint.value().InMilliseconds());
-  }
-  if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->largest_text_paint, extra_info)) {
-    builder
-        ->SetExperimental_PaintTiming_NavigationToLargestTextPaint_BeforeUserInput(
-            timing.paint_timing->largest_text_paint.value().InMilliseconds());
-  }
-}
-
 void UkmPageLoadMetricsObserver::ReportMainResourceTimingMetrics(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     ukm::builders::PageLoad* builder) {
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
index dced8f2..9f60668 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h
@@ -58,11 +58,6 @@
       const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info,
       const page_load_metrics::PageLoadExtraInfo& extra_info) override;
 
-  void OnUserInput(
-      const blink::WebInputEvent& event,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-
   void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
                   const page_load_metrics::PageLoadExtraInfo& info) override;
 
@@ -90,11 +85,6 @@
 
   void ReportLayoutStability(const page_load_metrics::PageLoadExtraInfo& info);
 
-  void RecordBeforeUserInputMetrics(
-      ukm::builders::PageLoad* builder,
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info);
-
   // Guaranteed to be non-null during the lifetime of |this|.
   network::NetworkQualityTracker* network_quality_tracker_;
 
@@ -126,8 +116,6 @@
   // True if the page main resource was served from disk cache.
   bool was_cached_ = false;
 
-  bool recorded_before_user_input_metrics_ = false;
-
   // The number of main frame redirects that occurred before commit.
   uint32_t main_frame_request_redirect_count_ = 0;
 
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 1b2b8454..3290822 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -258,11 +258,6 @@
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName,
         600);
-    test_ukm_recorder().ExpectEntryMetric(
-        kv.second.get(),
-        PageLoad::
-            kExperimental_PaintTiming_NavigationToLargestImagePaint_BeforeUserInputName,
-        600);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc
index b308719e..696de20 100644
--- a/chrome/browser/password_manager/password_store_x.cc
+++ b/chrome/browser/password_manager/password_store_x.cc
@@ -82,8 +82,8 @@
   switch (step) {
     case PasswordStoreX::NOT_ATTEMPTED:
       return LinuxBackendMigrationStatus::kNotAttempted;
-    case PasswordStoreX::FAILED:
-      return LinuxBackendMigrationStatus::kFailed;
+    case PasswordStoreX::DEPRECATED_FAILED:
+      return LinuxBackendMigrationStatus::kDeprecatedFailed;
     case PasswordStoreX::COPIED_ALL:
       return LinuxBackendMigrationStatus::kCopiedAll;
     case PasswordStoreX::LOGIN_DB_REPLACED:
@@ -92,12 +92,18 @@
       return LinuxBackendMigrationStatus::kStarted;
     case PasswordStoreX::POSTPONED:
       return LinuxBackendMigrationStatus::kPostponed;
-    case PasswordStoreX::FAILED_CREATE_ENCRYPTED:
-      return LinuxBackendMigrationStatus::kFailedCreatedEncrypted;
+    case PasswordStoreX::DEPRECATED_FAILED_CREATE_ENCRYPTED:
+      return LinuxBackendMigrationStatus::kDeprecatedFailedCreatedEncrypted;
     case PasswordStoreX::FAILED_ACCESS_NATIVE:
       return LinuxBackendMigrationStatus::kFailedAccessNative;
     case PasswordStoreX::FAILED_REPLACE:
       return LinuxBackendMigrationStatus::kFailedReplace;
+    case PasswordStoreX::FAILED_INIT_ENCRYPTED:
+      return LinuxBackendMigrationStatus::kFailedInitEncrypted;
+    case PasswordStoreX::FAILED_RECREATE_ENCRYPTED:
+      return LinuxBackendMigrationStatus::kFailedRecreateEncrypted;
+    case PasswordStoreX::FAILED_WRITE_TO_ENCRYPTED:
+      return LinuxBackendMigrationStatus::kFailedWriteToEncrypted;
   }
   NOTREACHED();
   return LinuxBackendMigrationStatus::kNotAttempted;
@@ -378,6 +384,10 @@
     } else {
       UpdateMigrationToLoginDBStep(POSTPONED);
     }
+
+    base::UmaHistogramEnumeration(
+        "PasswordManager.LinuxBackendMigration.AttemptResult",
+        StepForMetrics(migration_to_login_db_step_));
   }
 }
 
@@ -444,7 +454,7 @@
   if (!encrypted_login_db->Init()) {
     VLOG(1) << "Failed to init the encrypted database file. Migration "
                "aborted.";
-    UpdateMigrationToLoginDBStep(FAILED_CREATE_ENCRYPTED);
+    UpdateMigrationToLoginDBStep(FAILED_INIT_ENCRYPTED);
     return;  // Serve from the native backend.
   }
 
@@ -502,7 +512,7 @@
 
   if (!login_db->DeleteAndRecreateDatabaseFile()) {
     LOG(ERROR) << "Failed to create the encrypted login database file";
-    return FAILED_CREATE_ENCRYPTED;
+    return FAILED_RECREATE_ENCRYPTED;
   }
 
   std::vector<std::unique_ptr<PasswordForm>> forms;
@@ -512,7 +522,7 @@
   for (auto& form : forms) {
     PasswordStoreChangeList changes = login_db->AddLogin(*form);
     if (changes.empty() || changes.back().type() != PasswordStoreChange::ADD) {
-      return FAILED_CREATE_ENCRYPTED;
+      return FAILED_WRITE_TO_ENCRYPTED;
     }
   }
 
diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h
index c780f64b..0f83adf 100644
--- a/chrome/browser/password_manager/password_store_x.h
+++ b/chrome/browser/password_manager/password_store_x.h
@@ -36,21 +36,29 @@
     // Neither started nor failed.
     NOT_ATTEMPTED = 0,
     // The last attempt was not completed.
-    FAILED,
+    DEPRECATED_FAILED,
     // All the data is in the temporary encrypted loginDB.
     COPIED_ALL,
     // The standard login database is encrypted.
     LOGIN_DB_REPLACED,
-    // The migration is about to be attempted.
+    // The migration is about to be attempted. This value was deprecated and
+    // replaced by more price entries. It may still be store in users'
+    // preferences.
     STARTED,
     // No access to the native backend.
     POSTPONED,
     // Could not create or write into the temporary file.
-    FAILED_CREATE_ENCRYPTED,
+    DEPRECATED_FAILED_CREATE_ENCRYPTED,
     // Could not read from the native backend.
     FAILED_ACCESS_NATIVE,
     // Could not replace old database.
     FAILED_REPLACE,
+    // Could not initialise the temporary encrypted database.
+    FAILED_INIT_ENCRYPTED,
+    // Could not reset th temporary encrypted database.
+    FAILED_RECREATE_ENCRYPTED,
+    // Could not add entries into the temporary encrypted database.
+    FAILED_WRITE_TO_ENCRYPTED,
   };
 
   // NativeBackends more or less implement the PaswordStore interface, but
diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc
index 19b0658..243dd38e 100644
--- a/chrome/browser/password_manager/password_store_x_unittest.cc
+++ b/chrome/browser/password_manager/password_store_x_unittest.cc
@@ -740,11 +740,19 @@
     EXPECT_TRUE(stored_forms.empty());
     EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED,
               migration_step_pref_.GetValue());
+
+    histogram_tester_.ExpectBucketCount(
+        "PasswordManager.LinuxBackendMigration.AttemptResult",
+        LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
   } else if (GetParam() == FAILING_BACKEND) {
     // No values should be written if we can't read the backend.
     auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
     EXPECT_TRUE(stored_forms.empty());
     EXPECT_THAT(migration_step_pref_.GetValue(), PasswordStoreX::POSTPONED);
+
+    histogram_tester_.ExpectBucketCount(
+        "PasswordManager.LinuxBackendMigration.AttemptResult",
+        LinuxBackendMigrationStatus::kPostponed, 1);
   } else {  // NO_BACKEND
     // No values should be moved without a working backend.
     auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
@@ -823,6 +831,8 @@
   histogram_tester_.ExpectBucketCount(
       "PasswordManager.LinuxBackendMigration.Adoption",
       LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
+  histogram_tester_.ExpectTotalCount(
+      "PasswordManager.LinuxBackendMigration.AttemptResult", 0);
 }
 
 INSTANTIATE_TEST_CASE_P(NoBackend,
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 6463cf4..7bac3cf 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -2062,3 +2062,50 @@
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, ServiceWorkerInterception) {
   RunServiceWorkerTest("respond_with_fetch_worker.js");
 }
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest, EmbeddedPdfGetsFocus) {
+  GURL test_iframe_url(embedded_test_server()->GetURL(
+      "/pdf/test-offset-cross-site-iframe.html"));
+  ui_test_utils::NavigateToURL(browser(), test_iframe_url);
+  WebContents* contents = GetActiveWebContents();
+
+  // Get BrowserPluginGuest for the PDF.
+  WebContents* guest_contents = nullptr;
+  content::BrowserPluginGuestManager* guest_manager =
+      contents->GetBrowserContext()->GetGuestManager();
+  guest_manager->ForEachGuest(
+      contents, base::BindRepeating(&RetrieveGuestContents, &guest_contents));
+  ASSERT_TRUE(guest_contents);
+  EXPECT_NE(contents, guest_contents);
+  // Wait for the guest's view to be created.
+  while (!guest_contents->GetRenderWidgetHostView()) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
+    run_loop.Run();
+  }
+  WaitForHitTestDataOrGuestSurfaceReady(guest_contents);
+
+  // Verify it's not focused.
+  EXPECT_FALSE(IsWebContentsBrowserPluginFocused(guest_contents));
+
+  // Send mouse-click.
+  gfx::Point point_in_pdf(10, 10);
+  gfx::Point point_in_root =
+      guest_contents->GetRenderWidgetHostView()->TransformPointToRootCoordSpace(
+          point_in_pdf);
+  EXPECT_NE(point_in_pdf, point_in_root);
+  content::SimulateRoutedMouseClickAt(contents, kDefaultKeyModifier,
+                                      blink::WebMouseEvent::Button::kLeft,
+                                      point_in_root);
+
+  // Wait for the BPG to get focus. This test will timeout if the focus fails
+  // to occur. Alternatively, we could add an IPC filter to the guest's
+  // RenderProcessHost.
+  while (!IsWebContentsBrowserPluginFocused(guest_contents)) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
+    run_loop.Run();
+  }
+}
diff --git a/chrome/browser/permissions/chooser_context_base.cc b/chrome/browser/permissions/chooser_context_base.cc
index 5b220277..217e02a24 100644
--- a/chrome/browser/permissions/chooser_context_base.cc
+++ b/chrome/browser/permissions/chooser_context_base.cc
@@ -26,14 +26,14 @@
 
 ChooserContextBase::Object::Object(GURL requesting_origin,
                                    GURL embedding_origin,
-                                   base::DictionaryValue* object,
-                                   const std::string& source,
+                                   base::DictionaryValue* value,
+                                   content_settings::SettingSource source,
                                    bool incognito)
     : requesting_origin(requesting_origin),
       embedding_origin(embedding_origin),
       source(source),
       incognito(incognito) {
-  this->object.Swap(object);
+  this->value.Swap(value);
 }
 
 ChooserContextBase::Object::~Object() = default;
@@ -50,7 +50,7 @@
   return content_setting == CONTENT_SETTING_ASK;
 }
 
-std::vector<std::unique_ptr<base::DictionaryValue>>
+std::vector<std::unique_ptr<ChooserContextBase::Object>>
 ChooserContextBase::GetGrantedObjects(const GURL& requesting_origin,
                                       const GURL& embedding_origin) {
   DCHECK_EQ(requesting_origin, requesting_origin.GetOrigin());
@@ -59,9 +59,10 @@
   if (!CanRequestObjectPermission(requesting_origin, embedding_origin))
     return {};
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> results;
+  std::vector<std::unique_ptr<Object>> results;
+  auto* info = new content_settings::SettingInfo();
   std::unique_ptr<base::DictionaryValue> setting =
-      GetWebsiteSetting(requesting_origin, embedding_origin);
+      GetWebsiteSetting(requesting_origin, embedding_origin, info);
   std::unique_ptr<base::Value> objects;
   if (!setting->Remove(kObjectListKey, &objects))
     return results;
@@ -72,12 +73,12 @@
     return results;
 
   for (auto& object : *object_list) {
-    // Steal ownership of |object| from |object_list|.
-    std::unique_ptr<base::DictionaryValue> object_dict =
-        base::DictionaryValue::From(
-            base::Value::ToUniquePtrValue(std::move(object)));
-    if (object_dict && IsValidObject(*object_dict))
-      results.push_back(std::move(object_dict));
+    base::DictionaryValue* object_dict;
+    if (object.GetAsDictionary(&object_dict) && IsValidObject(*object_dict)) {
+      results.push_back(std::make_unique<Object>(
+          requesting_origin, embedding_origin, object_dict, info->source,
+          host_content_settings_map_->is_incognito()));
+    }
   }
   return results;
 }
@@ -98,8 +99,9 @@
     if (!CanRequestObjectPermission(requesting_origin, embedding_origin))
       continue;
 
+    content_settings::SettingInfo info;
     std::unique_ptr<base::DictionaryValue> setting =
-        GetWebsiteSetting(requesting_origin, embedding_origin);
+        GetWebsiteSetting(requesting_origin, embedding_origin, &info);
     base::ListValue* object_list;
     if (!setting->GetList(kObjectListKey, &object_list))
       continue;
@@ -112,8 +114,8 @@
       }
 
       results.push_back(std::make_unique<Object>(
-          requesting_origin, embedding_origin, object_dict,
-          content_setting.source, content_setting.incognito));
+          requesting_origin, embedding_origin, object_dict, info.source,
+          content_setting.incognito));
     }
   }
 
@@ -130,7 +132,7 @@
   DCHECK(IsValidObject(*object));
 
   std::unique_ptr<base::DictionaryValue> setting =
-      GetWebsiteSetting(requesting_origin, embedding_origin);
+      GetWebsiteSetting(requesting_origin, embedding_origin, /*info=*/nullptr);
   base::ListValue* object_list;
   if (!setting->GetList(kObjectListKey, &object_list)) {
     object_list =
@@ -149,7 +151,7 @@
   DCHECK(IsValidObject(object));
 
   std::unique_ptr<base::DictionaryValue> setting =
-      GetWebsiteSetting(requesting_origin, embedding_origin);
+      GetWebsiteSetting(requesting_origin, embedding_origin, /*info=*/nullptr);
   base::ListValue* object_list;
   if (!setting->GetList(kObjectListKey, &object_list))
     return;
@@ -159,11 +161,12 @@
 
 std::unique_ptr<base::DictionaryValue> ChooserContextBase::GetWebsiteSetting(
     const GURL& requesting_origin,
-    const GURL& embedding_origin) {
+    const GURL& embedding_origin,
+    content_settings::SettingInfo* info) {
   std::unique_ptr<base::DictionaryValue> value =
       base::DictionaryValue::From(host_content_settings_map_->GetWebsiteSetting(
           requesting_origin, embedding_origin, data_content_settings_type_,
-          std::string(), nullptr));
+          std::string(), info));
   if (!value)
     value.reset(new base::DictionaryValue());
 
diff --git a/chrome/browser/permissions/chooser_context_base.h b/chrome/browser/permissions/chooser_context_base.h
index 11b3fe86..2ff9716d 100644
--- a/chrome/browser/permissions/chooser_context_base.h
+++ b/chrome/browser/permissions/chooser_context_base.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/values.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "url/gurl.h"
@@ -26,15 +27,15 @@
     // The contents of |object| are Swap()ed into the internal dictionary.
     Object(GURL requesting_origin,
            GURL embedding_origin,
-           base::DictionaryValue* object,
-           const std::string& source,
+           base::DictionaryValue* value,
+           content_settings::SettingSource source,
            bool incognito);
     ~Object();
 
     GURL requesting_origin;
     GURL embedding_origin;
-    base::DictionaryValue object;
-    std::string source;
+    base::DictionaryValue value;
+    content_settings::SettingSource source;
     bool incognito;
   };
 
@@ -55,7 +56,7 @@
   //
   // This method may be extended by a subclass to return objects not stored in
   // |host_content_settings_map_|.
-  virtual std::vector<std::unique_ptr<base::DictionaryValue>> GetGrantedObjects(
+  virtual std::vector<std::unique_ptr<Object>> GetGrantedObjects(
       const GURL& requesting_origin,
       const GURL& embedding_origin);
 
@@ -92,7 +93,8 @@
  private:
   std::unique_ptr<base::DictionaryValue> GetWebsiteSetting(
       const GURL& requesting_origin,
-      const GURL& embedding_origin);
+      const GURL& embedding_origin,
+      content_settings::SettingInfo* info);
   void SetWebsiteSetting(const GURL& requesting_origin,
                          const GURL& embedding_origin,
                          std::unique_ptr<base::Value> value);
diff --git a/chrome/browser/permissions/chooser_context_base_unittest.cc b/chrome/browser/permissions/chooser_context_base_unittest.cc
index a881034..a450fb7 100644
--- a/chrome/browser/permissions/chooser_context_base_unittest.cc
+++ b/chrome/browser/permissions/chooser_context_base_unittest.cc
@@ -66,11 +66,11 @@
   context.GrantObjectPermission(origin1_, origin1_, object1_.CreateDeepCopy());
   context.GrantObjectPermission(origin1_, origin1_, object2_.CreateDeepCopy());
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       context.GetGrantedObjects(origin1_, origin1_);
   EXPECT_EQ(2u, objects.size());
-  EXPECT_TRUE(object1_.Equals(objects[0].get()));
-  EXPECT_TRUE(object2_.Equals(objects[1].get()));
+  EXPECT_EQ(object1_, objects[0]->value);
+  EXPECT_EQ(object2_, objects[1]->value);
 
   // Granting permission to one origin should not grant them to another.
   objects = context.GetGrantedObjects(origin2_, origin2_);
@@ -86,10 +86,10 @@
   context.GrantObjectPermission(origin1_, origin1_, object1_.CreateDeepCopy());
   context.GrantObjectPermission(origin1_, origin1_, object1_.CreateDeepCopy());
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       context.GetGrantedObjects(origin1_, origin1_);
   EXPECT_EQ(1u, objects.size());
-  EXPECT_TRUE(object1_.Equals(objects[0].get()));
+  EXPECT_EQ(object1_, objects[0]->value);
 
   context.RevokeObjectPermission(origin1_, origin1_, object1_);
   objects = context.GetGrantedObjects(origin1_, origin1_);
@@ -100,10 +100,10 @@
   TestChooserContext context(profile());
   context.GrantObjectPermission(origin1_, origin2_, object1_.CreateDeepCopy());
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       context.GetGrantedObjects(origin1_, origin2_);
   EXPECT_EQ(1u, objects.size());
-  EXPECT_TRUE(object1_.Equals(objects[0].get()));
+  EXPECT_EQ(object1_, objects[0]->value);
 
   // The embedding origin still does not have permission.
   objects = context.GetGrantedObjects(origin2_, origin2_);
@@ -128,12 +128,12 @@
     if (object->requesting_origin == origin1_) {
       EXPECT_FALSE(found_one);
       EXPECT_EQ(origin1_, object->embedding_origin);
-      EXPECT_TRUE(object->object.Equals(&object1_));
+      EXPECT_EQ(object1_, objects[0]->value);
       found_one = true;
     } else if (object->requesting_origin == origin2_) {
       EXPECT_FALSE(found_two);
       EXPECT_EQ(origin2_, object->embedding_origin);
-      EXPECT_TRUE(object->object.Equals(&object2_));
+      EXPECT_EQ(object2_, objects[1]->value);
       found_two = true;
     } else {
       ADD_FAILURE() << "Unexpected object.";
@@ -153,14 +153,14 @@
   context.GrantObjectPermission(origin1_, origin1_, object1_.CreateDeepCopy());
   context.GrantObjectPermission(origin2_, origin2_, object2_.CreateDeepCopy());
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects1 =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects1 =
       context.GetGrantedObjects(origin1_, origin1_);
   EXPECT_EQ(0u, objects1.size());
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects2 =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects2 =
       context.GetGrantedObjects(origin2_, origin2_);
   ASSERT_EQ(1u, objects2.size());
-  EXPECT_TRUE(object2_.Equals(objects2[0].get()));
+  EXPECT_EQ(object2_, objects2[0]->value);
 }
 
 TEST_F(ChooserContextBaseTest, GetAllGrantedObjectsWithGuardBlocked) {
@@ -178,5 +178,5 @@
   ASSERT_EQ(1u, objects.size());
   EXPECT_EQ(origin2_, objects[0]->requesting_origin);
   EXPECT_EQ(origin2_, objects[0]->embedding_origin);
-  EXPECT_TRUE(objects[0]->object.Equals(&object2_));
+  EXPECT_EQ(object2_, objects[0]->value);
 }
diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc
index fef09208a..237b1851 100644
--- a/chrome/browser/predictors/preconnect_manager.cc
+++ b/chrome/browser/predictors/preconnect_manager.cc
@@ -223,8 +223,10 @@
       preresolve_jobs_.Remove(job_id);
     }
 
-    if (info)
+    if (info) {
+      DCHECK_LE(1u, info->queued_count);
       --info->queued_count;
+    }
   }
 }
 
@@ -275,8 +277,10 @@
     info->stats->requests_stats.emplace_back(job->url, need_preconnect);
   preresolve_jobs_.Remove(job_id);
   --inflight_preresolves_count_;
-  if (info)
+  if (info) {
+    DCHECK_LE(1u, info->inflight_count);
     --info->inflight_count;
+  }
   if (info && info->is_done())
     AllPreresolvesForUrlFinished(info);
   TryToLaunchPreresolveJobs();
diff --git a/chrome/browser/predictors/preconnect_manager_unittest.cc b/chrome/browser/predictors/preconnect_manager_unittest.cc
index 06dc3e5..181adc8 100644
--- a/chrome/browser/predictors/preconnect_manager_unittest.cc
+++ b/chrome/browser/predictors/preconnect_manager_unittest.cc
@@ -136,6 +136,12 @@
   PreconnectManagerTest();
   ~PreconnectManagerTest() override;
 
+  void VerifyAndClearExpectations() const {
+    base::RunLoop().RunUntilIdle();
+    Mock::VerifyAndClearExpectations(mock_network_context_.get());
+    Mock::VerifyAndClearExpectations(mock_delegate_.get());
+  }
+
  protected:
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<TestingProfile> profile_;
@@ -158,7 +164,9 @@
   preconnect_manager_->SetNetworkContextForTesting(mock_network_context_.get());
 }
 
-PreconnectManagerTest::~PreconnectManagerTest() = default;
+PreconnectManagerTest::~PreconnectManagerTest() {
+  VerifyAndClearExpectations();
+}
 
 TEST_F(PreconnectManagerTest, TestStartOneUrlPreresolve) {
   GURL main_frame_url("http://google.com");
@@ -186,6 +194,174 @@
   mock_network_context_->CompleteHostLookup(url_to_preconnect.host(), net::OK);
 }
 
+// Sends preconnect request for a webpage, and stops the request before
+// all pertaining preconnect requests finish. Next, preconnect request
+// for the same webpage is sent again. Verifies that all the preconnects
+// related to the second request are dispatched on the network.
+TEST_F(PreconnectManagerTest, TestStartOneUrlPreconnect_MultipleTimes) {
+  GURL main_frame_url("http://google.com");
+  size_t count = PreconnectManager::kMaxInflightPreresolves;
+  std::vector<PreconnectRequest> requests;
+  for (size_t i = 0; i < count + 1; ++i) {
+    // Exactly PreconnectManager::kMaxInflightPreresolves should be preresolved.
+    requests.emplace_back(
+        GURL(base::StringPrintf("http://cdn%" PRIuS ".google.com", i)), 1);
+  }
+  for (size_t i = 0; i < count; ++i) {
+    // Exactly PreconnectManager::kMaxInflightPreresolves should be preresolved.
+    EXPECT_CALL(*mock_network_context_,
+                ResolveHostProxy(requests[i].origin.host()));
+  }
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
+  preconnect_manager_->Start(main_frame_url, requests);
+  preconnect_manager_->Stop(main_frame_url);
+  for (size_t i = 0; i < count; ++i) {
+    mock_network_context_->CompleteHostLookup(requests[i].origin.host(),
+                                              net::OK);
+  }
+  VerifyAndClearExpectations();
+
+  // Now, restart the preconnect request.
+  EXPECT_CALL(
+      *mock_network_context_,
+      PreconnectSockets(1, requests.back().origin, kNormalLoadFlags, false));
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(requests.back().origin.host()));
+  for (size_t i = 0; i < count; ++i) {
+    EXPECT_CALL(
+        *mock_network_context_,
+        PreconnectSockets(1, requests[i].origin, kNormalLoadFlags, false));
+    EXPECT_CALL(*mock_network_context_,
+                ResolveHostProxy(requests[i].origin.host()));
+  }
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
+
+  preconnect_manager_->Start(main_frame_url, requests);
+
+  for (size_t i = 0; i < count + 1; ++i) {
+    mock_network_context_->CompleteHostLookup(requests[i].origin.host(),
+                                              net::OK);
+  }
+}
+
+// Sends preconnect request for two webpages, and stops one request before
+// all pertaining preconnect requests finish. Next, preconnect request
+// for the same webpage is sent again. Verifies that all the preconnects
+// related to the second request are dispatched on the network.
+TEST_F(PreconnectManagerTest, TestTwoConcurrentMainFrameUrls_MultipleTimes) {
+  GURL main_frame_url_1("http://google.com");
+  size_t count = PreconnectManager::kMaxInflightPreresolves;
+  std::vector<PreconnectRequest> requests;
+  for (size_t i = 0; i < count + 1; ++i) {
+    requests.emplace_back(
+        GURL(base::StringPrintf("http://cdn%" PRIuS ".google.com", i)), 1);
+  }
+
+  GURL main_frame_url_2("http://google2.com");
+
+  for (size_t i = 0; i < count; ++i) {
+    EXPECT_CALL(*mock_network_context_,
+                ResolveHostProxy(requests[i].origin.host()));
+  }
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_1));
+  for (size_t i = 0; i < count - 1; ++i) {
+    EXPECT_CALL(
+        *mock_network_context_,
+        PreconnectSockets(1, requests[i].origin, kNormalLoadFlags, false));
+  }
+
+  preconnect_manager_->Start(
+      main_frame_url_1, std::vector<PreconnectRequest>(
+                            requests.begin(), requests.begin() + count - 1));
+  preconnect_manager_->Start(main_frame_url_2,
+                             std::vector<PreconnectRequest>(
+                                 requests.begin() + count - 1, requests.end()));
+
+  preconnect_manager_->Stop(main_frame_url_2);
+  for (size_t i = 0; i < count - 1; ++i) {
+    mock_network_context_->CompleteHostLookup(requests[i].origin.host(),
+                                              net::OK);
+  }
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
+  // Preconnect to |requests[count-1].origin| finishes after |main_frame_url_2|
+  // is stopped. Finishing of |requests[count-1].origin| should cause preconnect
+  // manager to clear all internal state related to |main_frame_url_2|.
+  mock_network_context_->CompleteHostLookup(requests[count - 1].origin.host(),
+                                            net::OK);
+  VerifyAndClearExpectations();
+
+  // Now, restart the preconnect request.
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(requests[count - 1].origin.host()));
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(requests[count].origin.host()));
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
+  // Since state related to |main_frame_url_2| has been cleared,
+  // re-issuing a request for connect to |main_frame_url_2| should be
+  // successful.
+  preconnect_manager_->Start(main_frame_url_2,
+                             std::vector<PreconnectRequest>(
+                                 requests.begin() + count - 1, requests.end()));
+
+  EXPECT_CALL(*mock_network_context_,
+              PreconnectSockets(1, requests[count - 1].origin, kNormalLoadFlags,
+                                false));
+  EXPECT_CALL(
+      *mock_network_context_,
+      PreconnectSockets(1, requests[count].origin, kNormalLoadFlags, false));
+
+  mock_network_context_->CompleteHostLookup(requests[count - 1].origin.host(),
+                                            net::OK);
+  mock_network_context_->CompleteHostLookup(requests[count].origin.host(),
+                                            net::OK);
+}
+
+// Sends a preconnect request again after the first request finishes. Verifies
+// that the second preconnect request is dispatched to the network.
+TEST_F(PreconnectManagerTest,
+       TestStartOneUrlPreconnect_MultipleTimes_LessThanThree) {
+  GURL main_frame_url("http://google.com");
+  GURL url_to_preconnect_1("http://cdn.google1.com");
+  GURL url_to_preconnect_2("http://cdn.google2.com");
+
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(url_to_preconnect_1.host()));
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(url_to_preconnect_2.host()));
+
+  preconnect_manager_->Start(main_frame_url,
+                             {PreconnectRequest(url_to_preconnect_1, 1),
+                              PreconnectRequest(url_to_preconnect_2, 1)});
+  preconnect_manager_->Stop(main_frame_url);
+
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
+  mock_network_context_->CompleteHostLookup(url_to_preconnect_1.host(),
+                                            net::OK);
+  mock_network_context_->CompleteHostLookup(url_to_preconnect_2.host(),
+                                            net::OK);
+  VerifyAndClearExpectations();
+
+  // Now, start the preconnect request again.
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(url_to_preconnect_1.host()));
+  EXPECT_CALL(*mock_network_context_,
+              ResolveHostProxy(url_to_preconnect_2.host()));
+  EXPECT_CALL(
+      *mock_network_context_,
+      PreconnectSockets(1, url_to_preconnect_1, kNormalLoadFlags, false));
+  EXPECT_CALL(
+      *mock_network_context_,
+      PreconnectSockets(1, url_to_preconnect_2, kNormalLoadFlags, false));
+  EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
+  preconnect_manager_->Start(main_frame_url,
+                             {PreconnectRequest(url_to_preconnect_1, 1),
+                              PreconnectRequest(url_to_preconnect_2, 1)});
+  mock_network_context_->CompleteHostLookup(url_to_preconnect_1.host(),
+                                            net::OK);
+  mock_network_context_->CompleteHostLookup(url_to_preconnect_2.host(),
+                                            net::OK);
+}
+
 TEST_F(PreconnectManagerTest, TestStopOneUrlBeforePreconnect) {
   GURL main_frame_url("http://google.com");
   GURL url_to_preconnect("http://cdn.google.com");
diff --git a/chrome/browser/predictors/resolve_host_client_impl.h b/chrome/browser/predictors/resolve_host_client_impl.h
index 6fb8c04..dac796f3 100644
--- a/chrome/browser/predictors/resolve_host_client_impl.h
+++ b/chrome/browser/predictors/resolve_host_client_impl.h
@@ -11,6 +11,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 
 class GURL;
@@ -27,7 +28,7 @@
 
 // This class helps perform the host resolution using the NetworkContext.
 // An instance of this class must be deleted after the callback is invoked.
-class ResolveHostClientImpl : public network::mojom::ResolveHostClient {
+class ResolveHostClientImpl : public network::ResolveHostClientBase {
  public:
   // Starts the host resolution for |url|. |callback| is called when the host is
   // resolved or when an error occurs.
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 5d57bb1..29ebdd1 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -195,6 +195,7 @@
 #include "chrome/browser/android/ntp/recent_tabs_page_prefs.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h"
 #include "chrome/browser/android/preferences/browser_prefs_android.h"
+#include "chrome/browser/android/usage_stats/usage_stats_bridge.h"
 #include "chrome/browser/geolocation/geolocation_permission_context_android.h"
 #include "chrome/browser/ntp_snippets/download_suggestions_provider.h"
 #include "components/cdm/browser/media_drm_storage_impl.h"
@@ -663,6 +664,7 @@
   GeolocationPermissionContextAndroid::RegisterProfilePrefs(registry);
   PartnerBookmarksShim::RegisterProfilePrefs(registry);
   RecentTabsPagePrefs::RegisterProfilePrefs(registry);
+  usage_stats::UsageStatsBridge::RegisterProfilePrefs(registry);
 #if BUILDFLAG(ENABLE_FEED_IN_CHROME)
   feed::FeedSchedulerHost::RegisterProfilePrefs(registry);
   feed::RefreshThrottler::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 29ef3f5..5a807b58 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -344,18 +344,19 @@
   prerender_manager_->RecordFinalStatus(origin(), final_status());
   prerender_manager_->RecordNetworkBytesConsumed(origin(), network_bytes_);
 
-  // Broadcast the removal of aliases.
-  for (content::RenderProcessHost::iterator host_iterator =
-           content::RenderProcessHost::AllHostsIterator();
-       !host_iterator.IsAtEnd();
-       host_iterator.Advance()) {
-    content::RenderProcessHost* host = host_iterator.GetCurrentValue();
-    IPC::ChannelProxy* channel = host->GetChannel();
-    // |channel| might be NULL in tests.
-    if (channel) {
-      chrome::mojom::PrerenderDispatcherAssociatedPtr prerender_dispatcher;
-      channel->GetRemoteAssociatedInterface(&prerender_dispatcher);
-      prerender_dispatcher->PrerenderRemoveAliases(alias_urls_);
+  if (prerender_mode_ == FULL_PRERENDER) {
+    // Broadcast the removal of aliases.
+    for (content::RenderProcessHost::iterator host_iterator =
+             content::RenderProcessHost::AllHostsIterator();
+         !host_iterator.IsAtEnd(); host_iterator.Advance()) {
+      content::RenderProcessHost* host = host_iterator.GetCurrentValue();
+      IPC::ChannelProxy* channel = host->GetChannel();
+      // |channel| might be NULL in tests.
+      if (host->IsInitializedAndNotDead() && channel) {
+        chrome::mojom::PrerenderDispatcherAssociatedPtr prerender_dispatcher;
+        channel->GetRemoteAssociatedInterface(&prerender_dispatcher);
+        prerender_dispatcher->PrerenderRemoveAliases(alias_urls_);
+      }
     }
   }
 
@@ -467,17 +468,18 @@
 
   alias_urls_.push_back(url);
 
-  for (content::RenderProcessHost::iterator host_iterator =
-           content::RenderProcessHost::AllHostsIterator();
-       !host_iterator.IsAtEnd();
-       host_iterator.Advance()) {
-    content::RenderProcessHost* host = host_iterator.GetCurrentValue();
-    IPC::ChannelProxy* channel = host->GetChannel();
-    // |channel| might be NULL in tests.
-    if (channel) {
-      chrome::mojom::PrerenderDispatcherAssociatedPtr prerender_dispatcher;
-      channel->GetRemoteAssociatedInterface(&prerender_dispatcher);
-      prerender_dispatcher->PrerenderAddAlias(url);
+  if (prerender_mode_ == FULL_PRERENDER) {
+    for (content::RenderProcessHost::iterator host_iterator =
+             content::RenderProcessHost::AllHostsIterator();
+         !host_iterator.IsAtEnd(); host_iterator.Advance()) {
+      content::RenderProcessHost* host = host_iterator.GetCurrentValue();
+      IPC::ChannelProxy* channel = host->GetChannel();
+      // |channel| might be NULL in tests.
+      if (host->IsInitializedAndNotDead() && channel) {
+        chrome::mojom::PrerenderDispatcherAssociatedPtr prerender_dispatcher;
+        channel->GetRemoteAssociatedInterface(&prerender_dispatcher);
+        prerender_dispatcher->PrerenderAddAlias(url);
+      }
     }
   }
 
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc
index c117ec0..c9c03b60 100644
--- a/chrome/browser/previews/previews_service.cc
+++ b/chrome/browser/previews/previews_service.cc
@@ -133,7 +133,7 @@
           profile_path.Append(chrome::kPreviewsOptOutDBFilename)),
       optimization_guide_service
           ? std::make_unique<previews::PreviewsOptimizationGuide>(
-                optimization_guide_service, ui_task_runner)
+                optimization_guide_service, ui_task_runner, profile_path)
           : nullptr,
       base::Bind(&IsPreviewsTypeEnabled),
       std::make_unique<previews::PreviewsLogger>(), GetAllowedPreviews(),
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
index 1e2d43f..49f5881 100644
--- a/chrome/browser/printing/print_job_worker.cc
+++ b/chrome/browser/printing/print_job_worker.cc
@@ -211,8 +211,7 @@
   }
 }
 
-void PrintJobWorker::SetSettings(
-    std::unique_ptr<base::DictionaryValue> new_settings) {
+void PrintJobWorker::SetSettings(base::Value new_settings) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK(query_);
 
@@ -239,11 +238,10 @@
 }
 #endif
 
-void PrintJobWorker::UpdatePrintSettings(
-    std::unique_ptr<base::DictionaryValue> new_settings) {
+void PrintJobWorker::UpdatePrintSettings(base::Value new_settings) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PrintingContext::Result result =
-      printing_context_->UpdatePrintSettings(*new_settings);
+      printing_context_->UpdatePrintSettings(std::move(new_settings));
   GetSettingsDone(result);
 }
 
diff --git a/chrome/browser/printing/print_job_worker.h b/chrome/browser/printing/print_job_worker.h
index 182f756..95341c40 100644
--- a/chrome/browser/printing/print_job_worker.h
+++ b/chrome/browser/printing/print_job_worker.h
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/printing/printer_query.h"
 #include "content/public/browser/browser_thread.h"
@@ -18,10 +19,6 @@
 #include "printing/print_job_constants.h"
 #include "printing/printing_context.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 class PrintJob;
@@ -57,7 +54,7 @@
                    bool is_modifiable);
 
   // Set the new print settings from a dictionary value.
-  void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings);
+  void SetSettings(base::Value new_settings);
 
 #if defined(OS_CHROMEOS)
   // Set the new print settings from a POD type.
@@ -139,7 +136,7 @@
       bool is_scripted);
 
   // Called on the UI thread to update the print settings.
-  void UpdatePrintSettings(std::unique_ptr<base::DictionaryValue> new_settings);
+  void UpdatePrintSettings(base::Value new_settings);
 
 #if defined(OS_CHROMEOS)
   // Called on the UI thread to update the print settings.
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index 620c890..73419c0 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -78,12 +78,11 @@
 }
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-void CreateQueryWithSettings(
-    std::unique_ptr<base::DictionaryValue> job_settings,
-    int render_process_id,
-    int render_frame_id,
-    scoped_refptr<PrintQueriesQueue> queue,
-    PrintSettingsCallback callback) {
+void CreateQueryWithSettings(base::Value job_settings,
+                             int render_process_id,
+                             int render_frame_id,
+                             scoped_refptr<PrintQueriesQueue> queue,
+                             PrintSettingsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   scoped_refptr<printing::PrinterQuery> printer_query =
       queue->CreatePrinterQuery(render_process_id, render_frame_id);
@@ -133,16 +132,15 @@
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 void PrintViewManagerBase::PrintForPrintPreview(
-    std::unique_ptr<base::DictionaryValue> job_settings,
-    const scoped_refptr<base::RefCountedMemory>& print_data,
+    base::Value job_settings,
+    scoped_refptr<base::RefCountedMemory> print_data,
     content::RenderFrameHost* rfh,
     PrinterHandler::PrintCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  int page_count;
-  job_settings->GetInteger(kSettingPreviewPageCount, &page_count);
   PrintSettingsCallback settings_callback =
       base::BindOnce(&PrintViewManagerBase::OnPrintSettingsDone,
-                     weak_ptr_factory_.GetWeakPtr(), print_data, page_count,
+                     weak_ptr_factory_.GetWeakPtr(), print_data,
+                     job_settings.FindIntKey(kSettingPreviewPageCount).value(),
                      std::move(callback));
   base::PostTaskWithTraits(
       FROM_HERE, {content::BrowserThread::IO},
diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h
index fa8d5a17..a2569836 100644
--- a/chrome/browser/printing/print_view_manager_base.h
+++ b/chrome/browser/printing/print_view_manager_base.h
@@ -55,11 +55,10 @@
   // |job_settings|. Runs |callback| with an error string on failure and with an
   // empty string if the print job is started successfully. |rfh| is the render
   // frame host for the preview initiator contents respectively.
-  void PrintForPrintPreview(
-      std::unique_ptr<base::DictionaryValue> job_settings,
-      const scoped_refptr<base::RefCountedMemory>& print_data,
-      content::RenderFrameHost* rfh,
-      PrinterHandler::PrintCallback callback);
+  void PrintForPrintPreview(base::Value job_settings,
+                            scoped_refptr<base::RefCountedMemory> print_data,
+                            content::RenderFrameHost* rfh,
+                            PrinterHandler::PrintCallback callback);
 #endif
 
   // Whether printing is enabled or not.
diff --git a/chrome/browser/printing/print_view_manager_unittest.cc b/chrome/browser/printing/print_view_manager_unittest.cc
index 9591235..470c8e2 100644
--- a/chrome/browser/printing/print_view_manager_unittest.cc
+++ b/chrome/browser/printing/print_view_manager_unittest.cc
@@ -135,17 +135,13 @@
   print_view_manager->PrintPreviewNow(web_contents->GetMainFrame(), false);
 
   base::Value print_ticket = GetPrintTicket(printing::kLocalPrinter, false);
-  std::unique_ptr<base::DictionaryValue> job_settings =
-      base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(std::move(print_ticket)));
-
   const char kTestData[] = "abc";
   auto print_data = base::MakeRefCounted<base::RefCountedStaticMemory>(
       kTestData, sizeof(kTestData));
   PrinterHandler::PrintCallback callback =
       base::BindOnce(&TestPrintViewManager::FakePrintCallback,
                      base::Unretained(print_view_manager.get()));
-  print_view_manager->PrintForPrintPreview(std::move(job_settings), print_data,
+  print_view_manager->PrintForPrintPreview(std::move(print_ticket), print_data,
                                            web_contents->GetMainFrame(),
                                            std::move(callback));
   print_view_manager->WaitForCallback();
diff --git a/chrome/browser/printing/printer_query.cc b/chrome/browser/printing/printer_query.cc
index 09f8e164..06461a0 100644
--- a/chrome/browser/printing/printer_query.cc
+++ b/chrome/browser/printing/printer_query.cc
@@ -97,9 +97,8 @@
                      has_selection, margin_type, is_scripted, is_modifiable));
 }
 
-void PrinterQuery::SetSettings(
-    std::unique_ptr<base::DictionaryValue> new_settings,
-    base::OnceClosure callback) {
+void PrinterQuery::SetSettings(base::Value new_settings,
+                               base::OnceClosure callback) {
   StartWorker(std::move(callback));
 
   worker_->PostTask(FROM_HERE, base::BindOnce(&PrintJobWorker::SetSettings,
diff --git a/chrome/browser/printing/printer_query.h b/chrome/browser/printing/printer_query.h
index 64db84f..3b17390 100644
--- a/chrome/browser/printing/printer_query.h
+++ b/chrome/browser/printing/printer_query.h
@@ -10,12 +10,12 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/values.h"
 #include "printing/print_job_constants.h"
 #include "printing/print_settings.h"
 #include "printing/printing_context.h"
 
 namespace base {
-class DictionaryValue;
 class Location;
 class SequencedTaskRunner;
 }
@@ -60,7 +60,7 @@
                    base::OnceClosure callback);
 
   // Updates the current settings with |new_settings| dictionary values.
-  virtual void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,
+  virtual void SetSettings(base::Value new_settings,
                            base::OnceClosure callback);
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/printing/printing_message_filter.cc b/chrome/browser/printing/printing_message_filter.cc
index e415483..8cf82aaf 100644
--- a/chrome/browser/printing/printing_message_filter.cc
+++ b/chrome/browser/printing/printing_message_filter.cc
@@ -305,11 +305,9 @@
 }
 #endif
 
-void PrintingMessageFilter::OnUpdatePrintSettings(
-    int document_cookie, const base::DictionaryValue& job_settings,
-    IPC::Message* reply_msg) {
-  std::unique_ptr<base::DictionaryValue> new_settings(job_settings.DeepCopy());
-
+void PrintingMessageFilter::OnUpdatePrintSettings(int document_cookie,
+                                                  base::Value job_settings,
+                                                  IPC::Message* reply_msg) {
   scoped_refptr<PrinterQuery> printer_query;
   if (!is_printing_enabled_.GetValue()) {
     // Reply with NULL query.
@@ -322,7 +320,7 @@
         content::ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
   }
   printer_query->SetSettings(
-      std::move(new_settings),
+      std::move(job_settings),
       base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this,
                  printer_query, reply_msg));
 }
diff --git a/chrome/browser/printing/printing_message_filter.h b/chrome/browser/printing/printing_message_filter.h
index a881a85..63b71257 100644
--- a/chrome/browser/printing/printing_message_filter.h
+++ b/chrome/browser/printing/printing_message_filter.h
@@ -11,6 +11,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "components/prefs/pref_member.h"
@@ -22,12 +23,11 @@
 struct PrintHostMsg_ScriptedPrint_Params;
 class Profile;
 
-namespace base {
-class DictionaryValue;
 #if defined(OS_ANDROID)
+namespace base {
 struct FileDescriptor;
-#endif
 }
+#endif
 
 namespace printing {
 
@@ -88,7 +88,7 @@
   // handled by the print worker thread and the UI thread. The reply occurs on
   // the IO thread.
   void OnUpdatePrintSettings(int document_cookie,
-                             const base::DictionaryValue& job_settings,
+                             base::Value job_settings,
                              IPC::Message* reply_msg);
   void OnUpdatePrintSettingsReply(scoped_refptr<PrinterQuery> printer_query,
                                   IPC::Message* reply_msg);
diff --git a/chrome/browser/printing/test_printer_query.cc b/chrome/browser/printing/test_printer_query.cc
index 7234d78..6d26be12 100644
--- a/chrome/browser/printing/test_printer_query.cc
+++ b/chrome/browser/printing/test_printer_query.cc
@@ -47,9 +47,8 @@
 
 TestPrinterQuery::~TestPrinterQuery() {}
 
-void TestPrinterQuery::SetSettings(
-    std::unique_ptr<base::DictionaryValue> new_settings,
-    base::OnceClosure callback) {
+void TestPrinterQuery::SetSettings(base::Value new_settings,
+                                   base::OnceClosure callback) {
   DCHECK(offsets_);
 #if defined(OS_WIN)
   DCHECK(printer_type_);
@@ -57,7 +56,7 @@
   set_callback(std::move(callback));
   PrintSettings settings;
   PrintingContext::Result result =
-      PrintSettingsFromJobSettings(*new_settings, &settings)
+      PrintSettingsFromJobSettings(new_settings, &settings)
           ? PrintingContext::OK
           : PrintingContext::FAILED;
 
diff --git a/chrome/browser/printing/test_printer_query.h b/chrome/browser/printing/test_printer_query.h
index 934fc99d..9bc62d1 100644
--- a/chrome/browser/printing/test_printer_query.h
+++ b/chrome/browser/printing/test_printer_query.h
@@ -9,14 +9,11 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/printing/print_job_manager.h"
 #include "chrome/browser/printing/printer_query.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 class TestPrintQueriesQueue : public PrintQueriesQueue {
@@ -60,7 +57,7 @@
   // Updates the current settings with |new_settings| dictionary values. Also
   // fills in the settings with values from |offsets_| and |printer_type_| that
   // would normally be filled in by the PrintingContext.
-  void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,
+  void SetSettings(base::Value new_settings,
                    base::OnceClosure callback) override;
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/profiles/host_zoom_map_browsertest.cc b/chrome/browser/profiles/host_zoom_map_browsertest.cc
index 45a94b1..babc2efe 100644
--- a/chrome/browser/profiles/host_zoom_map_browsertest.cc
+++ b/chrome/browser/profiles/host_zoom_map_browsertest.cc
@@ -131,7 +131,7 @@
   }
 
   std::string GetSigninPromoURL() {
-    return signin::GetPromoURLForTab(
+    return signin::GetEmbeddedPromoURL(
                signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
                signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false)
         .spec();
diff --git a/chrome/browser/push_messaging/budget_database.cc b/chrome/browser/push_messaging/budget_database.cc
index 8d71b8f3..019cce32 100644
--- a/chrome/browser/push_messaging/budget_database.cc
+++ b/chrome/browser/push_messaging/budget_database.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/push_messaging/budget.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -55,10 +55,10 @@
 
 BudgetDatabase::BudgetDatabase(Profile* profile)
     : profile_(profile),
-      db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>(
-          base::CreateSequencedTaskRunnerWithTraits(
-              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
+      db_(leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+          budget_service::Budget>(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
       clock_(base::WrapUnique(new base::DefaultClock)),
       weak_ptr_factory_(this) {
   db_->Init(kDatabaseUMAName,
diff --git a/chrome/browser/push_messaging/budget_database.h b/chrome/browser/push_messaging/budget_database.h
index 18176a5..cfa943a 100644
--- a/chrome/browser/push_messaging/budget_database.h
+++ b/chrome/browser/push_messaging/budget_database.h
@@ -13,7 +13,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace base {
 class Clock;
diff --git a/chrome/browser/push_messaging/budget_database_unittest.cc b/chrome/browser/push_messaging/budget_database_unittest.cc
index 5d50fb9e..0027e7b8 100644
--- a/chrome/browser/push_messaging/budget_database_unittest.cc
+++ b/chrome/browser/push_messaging/budget_database_unittest.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/push_messaging/budget.pb.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
index 292e050..44a9f6b 100644
--- a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
 #include "chrome/common/pref_names.h"
@@ -72,10 +73,18 @@
 
 }  // namespace
 
+#if defined(OS_LINUX)
+#define MAYBE_AccessibilityLabelsNotShownWithoutScreenReader \
+  DISABLED_AccessibilityLabelsNotShownWithoutScreenReader
+#else
+#define MAYBE_AccessibilityLabelsNotShownWithoutScreenReader \
+  AccessibilityLabelsNotShownWithoutScreenReader
+#endif
 // Tests that opening a context menu does not show the menu option if a
 // screen reader is not enabled, regardless of the image labels setting.
+// TODO(crbug.com/921487) Investigate flakes on linux.
 IN_PROC_BROWSER_TEST_F(AccessibilityLabelsMenuObserverTest,
-                       AccessibilityLabelsNotShownWithoutScreenReader) {
+                       MAYBE_AccessibilityLabelsNotShownWithoutScreenReader) {
   menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsEnabled,
                                  false);
   InitMenu();
@@ -116,8 +125,16 @@
   EXPECT_FALSE(item.hidden);
 }
 #else
+#if defined(OS_LINUX)
+#define MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled \
+  DISABLED_AccessibilityLabelsShowWithScreenReaderEnabled
+#else
+#define MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled \
+  AccessibilityLabelsShowWithScreenReaderEnabled
+#endif
+// TODO(crbug.com/921487) Investigate flakes on linux.
 IN_PROC_BROWSER_TEST_F(AccessibilityLabelsMenuObserverTest,
-                       AccessibilityLabelsShowWithScreenReaderEnabled) {
+                       MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled) {
   // Spoof a screen reader.
   content::BrowserAccessibilityState::GetInstance()->AddAccessibilityModeFlags(
       ui::AXMode::kScreenReader);
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 7cc01ba5..f3952ca 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2156,7 +2156,7 @@
       break;
 
     case IDC_CONTENT_CONTEXT_EMOJI:
-      ui::ShowEmojiPanel();
+      GetBrowser()->window()->ShowEmojiPanel();
       break;
 
     default:
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
index 86e3121..7158f55 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
@@ -120,6 +120,8 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
                            CandidatesSortedWithFocusedAppAndTab);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
+                           CandidatesSortedWithNewProcessTypes);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
                            DoNotKillRecentlyKilledArcProcesses);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest, IsRecentlyKilledArcProcess);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest, KillMultipleProcesses);
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
index 97d1f6a1..088b684 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/resource_coordinator/test_lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/time.h"
@@ -103,6 +104,54 @@
   EXPECT_EQ("focused", candidates[1].app()->process_name());
 }
 
+TEST_F(TabManagerDelegateTest, CandidatesSortedWithNewProcessTypes) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({features::kNewProcessTypes},
+                                {features::kTabRanker});
+  std::vector<arc::ArcProcess> arc_processes;
+  arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
+                             kIsFocused, 100);
+  arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 200);
+  arc_processes.emplace_back(
+      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
+  arc_processes.emplace_back(4, 40, "cached",
+                             arc::mojom::ProcessState::CACHED_EMPTY,
+                             kNotFocused, 150);
+
+  TestLifecycleUnit focused_tab(base::TimeTicks::Max());
+  TestLifecycleUnit protected_tab(
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1), 0, false);
+  TestLifecycleUnit background_tab(base::TimeTicks() +
+                                   base::TimeDelta::FromSeconds(5));
+  LifecycleUnitVector lifecycle_units{&focused_tab, &protected_tab,
+                                      &background_tab};
+
+  std::vector<TabManagerDelegate::Candidate> candidates;
+  candidates =
+      TabManagerDelegate::GetSortedCandidates(lifecycle_units, arc_processes);
+
+  ASSERT_EQ(7U, candidates.size());
+  EXPECT_EQ(&focused_tab, candidates[0].lifecycle_unit());
+  EXPECT_EQ(ProcessType::FOCUSED_TAB, candidates[0].process_type());
+  ASSERT_TRUE(candidates[1].app());
+  EXPECT_EQ("focused", candidates[1].app()->process_name());
+  EXPECT_EQ(ProcessType::FOCUSED_APP, candidates[1].process_type());
+  ASSERT_TRUE(candidates[2].app());
+  EXPECT_EQ("visible1", candidates[2].app()->process_name());
+  EXPECT_EQ(ProcessType::PROTECTED_BACKGROUND, candidates[2].process_type());
+  EXPECT_EQ(&protected_tab, candidates[3].lifecycle_unit());
+  EXPECT_EQ(ProcessType::PROTECTED_BACKGROUND, candidates[3].process_type());
+  ASSERT_TRUE(candidates[4].app());
+  EXPECT_EQ("service", candidates[4].app()->process_name());
+  EXPECT_EQ(ProcessType::BACKGROUND, candidates[4].process_type());
+  EXPECT_EQ(&background_tab, candidates[5].lifecycle_unit());
+  EXPECT_EQ(ProcessType::BACKGROUND, candidates[5].process_type());
+  ASSERT_TRUE(candidates[6].app());
+  EXPECT_EQ("cached", candidates[6].app()->process_name());
+  EXPECT_EQ(ProcessType::CACHED_APP, candidates[6].process_type());
+}
+
 class MockTabManagerDelegate : public TabManagerDelegate {
  public:
   MockTabManagerDelegate()
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 5c83a24..42e9b1c 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -16,6 +16,7 @@
     if (is_linux || is_win || is_mac) {
       deps += [
         "discards:closure_compile",
+        "local_state:closure_compile",
         "management:closure_compile",
         "md_bookmarks:closure_compile",
         "md_downloads:closure_compile",
diff --git a/chrome/browser/resources/app_management/BUILD.gn b/chrome/browser/resources/app_management/BUILD.gn
index 70815c2..be75bfec 100644
--- a/chrome/browser/resources/app_management/BUILD.gn
+++ b/chrome/browser/resources/app_management/BUILD.gn
@@ -111,7 +111,10 @@
 
   js_library("metadata_view") {
     deps = [
+      ":browser_proxy",
+      ":constants",
       ":fake_page_handler",
+      ":types",
     ]
   }
 
diff --git a/chrome/browser/resources/app_management/constants.js b/chrome/browser/resources/app_management/constants.js
index fa85387e..1bce449 100644
--- a/chrome/browser/resources/app_management/constants.js
+++ b/chrome/browser/resources/app_management/constants.js
@@ -42,3 +42,5 @@
 const PermissionValueType = apps.mojom.PermissionValueType;
 
 const TriState = apps.mojom.TriState;
+
+const OptionalBool = apps.mojom.OptionalBool;
diff --git a/chrome/browser/resources/app_management/fake_page_handler.js b/chrome/browser/resources/app_management/fake_page_handler.js
index 78e4e37..b21bd5e 100644
--- a/chrome/browser/resources/app_management/fake_page_handler.js
+++ b/chrome/browser/resources/app_management/fake_page_handler.js
@@ -90,6 +90,18 @@
 
     /**
      * @param {string} appId
+     * @param {OptionalBool} pinnedValue
+     */
+    setPinned(appId, pinnedValue) {
+      const app = app_management.Store.getInstance().data.apps[appId];
+
+      const newApp =
+          /** @type {App} */ (Object.assign({}, app, {isPinned: pinnedValue}));
+      this.page.onAppChanged(newApp);
+    }
+
+    /**
+     * @param {string} appId
      * @param {Permission} permission
      */
     setPermission(appId, permission) {
diff --git a/chrome/browser/resources/app_management/metadata_view.html b/chrome/browser/resources/app_management/metadata_view.html
index 22bcecf..b1a3877 100644
--- a/chrome/browser/resources/app_management/metadata_view.html
+++ b/chrome/browser/resources/app_management/metadata_view.html
@@ -37,12 +37,15 @@
       justify-content: space-around;
     }
     </style>
-    <div id="shelf-switch-row">
-      <span id="shelf-switch" class="header-text">
-        $i18n{pinToShelf}
-        <cr-toggle></cr-toggle>
-      </span>
-    </div>
+    <template is="dom-if" if="[[pinToShelfToggleVisible_(app)]]">
+      <div id="shelf-switch-row">
+        <span id="shelf-switch" class="header-text">
+          $i18n{pinToShelf}
+          <cr-toggle checked="[[isPinned_(app)]]" on-change="togglePinned_">
+          </cr-toggle>
+        </span>
+      </div>
+    </template>
 
     <div id="metadata-overview" class="secondary-text">
       <span>[[versionString_(app)]]</span>
diff --git a/chrome/browser/resources/app_management/metadata_view.js b/chrome/browser/resources/app_management/metadata_view.js
index 3f786cb..891c13d 100644
--- a/chrome/browser/resources/app_management/metadata_view.js
+++ b/chrome/browser/resources/app_management/metadata_view.js
@@ -6,13 +6,54 @@
   is: 'app-management-metadata-view',
 
   properties: {
-    /** @type {appManagement.mojom.App} */
+    /** @type {App} */
     app: {
       type: Object,
     },
   },
 
   /**
+   * @param {App} app
+   * @return bool
+   * @private
+   */
+  pinToShelfToggleVisible_: function(app) {
+    return !(app.isPinned === OptionalBool.kUnknown);
+  },
+
+  /**
+   * Returns a bool representation of the app's isPinned value, used to
+   * determine the position of the "Pin to Shelf" toggle.
+   * @param {App} app
+   * @return bool
+   * @private
+   */
+  isPinned_: function(app) {
+    return app.isPinned === OptionalBool.kTrue;
+  },
+
+  togglePinned_: function() {
+    assert(this.app);
+
+    let newPinnedValue;
+
+    switch (this.app.isPinned) {
+      case OptionalBool.kFalse:
+        newPinnedValue = OptionalBool.kTrue;
+        break;
+      case OptionalBool.kTrue:
+        newPinnedValue = OptionalBool.kFalse;
+        break;
+      default:
+        assertNotReached();
+    }
+
+    app_management.BrowserProxy.getInstance().handler.setPinned(
+        this.app.id, newPinnedValue);
+  },
+
+  /**
+   * @param {App} app
    * @return {string?}
    * @private
    */
@@ -25,6 +66,7 @@
   },
 
   /**
+   * @param {App} app
    * @return {string?}
    * @private
    */
diff --git a/chrome/browser/resources/app_management/permission_item.js b/chrome/browser/resources/app_management/permission_item.js
index 51d3040..7fcce27 100644
--- a/chrome/browser/resources/app_management/permission_item.js
+++ b/chrome/browser/resources/app_management/permission_item.js
@@ -60,15 +60,19 @@
    * @private
    */
   getPermissionValue_: function(app, permissionType) {
-    // TODO(@rekanorman) Remove the second one in if statement when PWA's are
-    // sent thorough with the correct app type.
-    if (!app || !(app.permissions)[PwaPermissionType[permissionType]]) {
+    if (!app) {
       return null;
     }
 
-    return /** @type Object<number, Permission> */ (
-               app.permissions)[PwaPermissionType[permissionType]]
-        .value;
+    // TODO(rekanorman): Remove once permissions are implemented for all
+    // app types.
+    if (Object.keys(app.permissions).length === 0) {
+      return null;
+    }
+
+    // TODO(rekanorman): Make generic once permissions are implemented for
+    // other app types.
+    return app.permissions[PwaPermissionType[permissionType]].value;
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index ecbaa8f..0acb504 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -129,6 +129,14 @@
       <p>After</p>
     </div>
   */},
+
+  languageSwitchingDoc: function() {/*!
+    <p id="breakfast">In the morning, I sometimes eat breakfast.</p>
+    <p id="lunch" lang="fr">Dans l'apres-midi, je dejeune.</p>
+    <p id="greeting">
+      Hello it's a pleasure to meet you. <span lang="fr">Comment ca va?</span>
+    </p>
+  */},
 };
 
 /** Tests that ChromeVox classic is in this context. */
@@ -1861,3 +1869,23 @@
         .replay();
   });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'DetectLanguage', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(this.languageSwitchingDoc, function() {
+
+    mockFeedback.call(doCmd('jumpToTop'))
+        .expectSpeech("In the morning, I sometimes eat breakfast.");
+    mockFeedback.call(doCmd('nextLine'))
+        .expectSpeech("Dans l'apres-midi, je dejeune.");
+    mockFeedback.call(doCmd('nextLine'))
+        .expectSpeech("Hello it's a pleasure to meet you. ");
+    mockFeedback.call(doCmd('nextLine'))
+        .expectSpeech("Comment ca va?");
+
+    // TODO(akihiroota): Implement expectSpeechWithLanguage() to assert correct
+    // output language.
+
+    mockFeedback.replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/switch_access/BUILD.gn
index 0eadb48..a73c276 100644
--- a/chrome/browser/resources/chromeos/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/switch_access/BUILD.gn
@@ -34,6 +34,13 @@
     "auto_scan_manager.js",
     "background.js",
     "commands.js",
+    "icons/dictation.svg",
+    "icons/options.svg",
+    "icons/scroll_down.svg",
+    "icons/scroll_left.svg",
+    "icons/scroll_right.svg",
+    "icons/scroll_up.svg",
+    "icons/select.svg",
     "keyboard_handler.js",
     "menu_manager.js",
     "menu_panel.css",
@@ -79,7 +86,6 @@
   test_type = "extension"
   sources = [
     "auto_scan_manager_test.extjs",
-    "menu_manager_test.extjs",
     "navigation_manager_test.extjs",
     "switch_access_predicate_test.extjs",
   ]
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/dictation.svg b/chrome/browser/resources/chromeos/switch_access/icons/dictation.svg
new file mode 100644
index 0000000..b2087ec6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/dictation.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
+  <g fill="none" fill-rule="evenodd">
+    <polygon points="0 0 40 0 40 40 0 40"/>
+    <path fill="#fff" d="M24.4121429,20 C24.4121429,22.7666667 22.2271429,25 19.5,25 C16.7728571,25 14.5714286,22.7666667 14.5714286,20 L14.5714286,10 C14.5714286,7.23333333 16.7728571,5 19.5,5 C22.2271429,5 24.4285714,7.23333333 24.4285714,10 L24.4121429,20 Z M19.5,7.5 C18.1192881,7.5 17,8.61928813 17,10 L17,20 C17,21.3807119 18.1192881,22.5 19.5,22.5 C20.8807119,22.5 22,21.3807119 22,20 L22,10 C22,8.61928813 20.8807119,7.5 19.5,7.5 Z M28,20 C28,25.0526316 24.0342857,28.5789474 19.5,28.5789474 C14.9657143,28.5789474 11,25.0526316 11,20 L8,20 C8,25.76 12.6114286,30.6505263 18,31.4757895 L18,37 L21,37 L21,31.4757895 C26.3885714,30.6673684 31,25.76 31,20 L28,20 Z"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/options.svg b/chrome/browser/resources/chromeos/switch_access/icons/options.svg
new file mode 100644
index 0000000..af87651d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/options.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path fill="none" d="M0 0h20v20H0V0z"/><path fill="#fff" d="M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/scroll_down.svg b/chrome/browser/resources/chromeos/switch_access/icons/scroll_down.svg
new file mode 100644
index 0000000..96b5e5da
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/scroll_down.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/scroll_left.svg b/chrome/browser/resources/chromeos/switch_access/icons/scroll_left.svg
new file mode 100644
index 0000000..fef8f907
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/scroll_left.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/scroll_right.svg b/chrome/browser/resources/chromeos/switch_access/icons/scroll_right.svg
new file mode 100644
index 0000000..506f890
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/scroll_right.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/scroll_up.svg b/chrome/browser/resources/chromeos/switch_access/icons/scroll_up.svg
new file mode 100644
index 0000000..6324b7d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/scroll_up.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/icons/select.svg b/chrome/browser/resources/chromeos/switch_access/icons/select.svg
new file mode 100644
index 0000000..b6e598b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/switch_access/icons/select.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><defs><path fill="white" id="a" d="M0 0h24v24H0V0z"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path fill="white" d="M9 11.24V7.5C9 6.12 10.12 5 11.5 5S14 6.12 14 7.5v3.74c1.21-.81 2-2.18 2-3.74C16 5.01 13.99 3 11.5 3S7 5.01 7 7.5c0 1.56.79 2.93 2 3.74zm9.84 4.63l-4.54-2.26c-.17-.07-.35-.11-.54-.11H13v-6c0-.83-.67-1.5-1.5-1.5S10 6.67 10 7.5v10.74l-3.43-.72c-.08-.01-.15-.03-.24-.03-.31 0-.59.13-.79.33l-.79.8 4.94 4.94c.27.27.65.44 1.06.44h6.79c.75 0 1.33-.55 1.44-1.28l.75-5.27c.01-.07.02-.14.02-.2 0-.62-.38-1.16-.91-1.38z" clip-path="url(#b)"/></svg>
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_manager.js b/chrome/browser/resources/chromeos/switch_access/menu_manager.js
index 2340dc5..2a35915 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/menu_manager.js
@@ -67,12 +67,14 @@
           MessageHandler.Destination.MENU_PANEL, 'setActions', actions);
     }
 
-    if (navNode.location) {
+    this.node_ = this.menuNode();
+
+    if (navNode.location)
       chrome.accessibilityPrivate.setSwitchAccessMenuState(
           true, navNode.location);
-    } else {
+    else
       console.log('Unable to show Switch Access menu.');
-    }
+    this.moveForward();
   }
 
   /**
@@ -94,9 +96,7 @@
    * @return {boolean} Whether this function had any effect.
    */
   moveForward() {
-    // Checking this.node_ is a formality for the benefit of the closure
-    // type compiler.
-    if (!this.inMenu_ || !this.calculateCurrentNode() || !this.node_)
+    if (!this.node_ || !this.inMenu_)
       return false;
 
     this.clearFocusRing_();
@@ -105,7 +105,7 @@
         SwitchAccessPredicate.restrictions(this.menuNode()));
     const node = treeWalker.next().node;
     if (!node)
-      this.node_ = null;
+      this.node_ = this.menuNode();
     else
       this.node_ = node;
     this.updateFocusRing_();
@@ -118,9 +118,7 @@
    * @return {boolean} Whether this function had any effect.
    */
   moveBackward() {
-    // Checking this.node_ is a formality for the benefit of the closure
-    // type compiler.
-    if (!this.inMenu_ || !this.calculateCurrentNode() || !this.node_)
+    if (!this.node_ || !this.inMenu_)
       return false;
 
     this.clearFocusRing_();
@@ -152,7 +150,7 @@
    * @return {boolean} Whether this function had any effect.
    */
   selectCurrentNode() {
-    if (!this.inMenu_ || !this.calculateCurrentNode())
+    if (!this.node_ || !this.inMenu_)
       return false;
 
     this.clearFocusRing_();
@@ -201,16 +199,20 @@
     if (SwitchAccessPredicate.isTextInput(node))
       actions.push(MenuManager.Action.DICTATION);
 
-    if (node.scrollable) {
+    let scrollableAncestor = node;
+    while (!scrollableAncestor.scrollable && scrollableAncestor.parent)
+      scrollableAncestor = scrollableAncestor.parent;
+
+    if (scrollableAncestor.scrollable) {
       // TODO(crbug/920659) This does not work for ARC++. Implement scroll
       // directions via standardActions.
-      if (node.scrollX > node.scrollXMin)
+      if (scrollableAncestor.scrollX > scrollableAncestor.scrollXMin)
         actions.push(MenuManager.Action.SCROLL_LEFT);
-      if (node.scrollX < node.scrollXMax)
+      if (scrollableAncestor.scrollX < scrollableAncestor.scrollXMax)
         actions.push(MenuManager.Action.SCROLL_RIGHT);
-      if (node.scrollY > node.scrollYMin)
+      if (scrollableAncestor.scrollY > scrollableAncestor.scrollYMin)
         actions.push(MenuManager.Action.SCROLL_UP);
-      if (node.scrollY < node.scrollYMax)
+      if (scrollableAncestor.scrollY < scrollableAncestor.scrollYMax)
         actions.push(MenuManager.Action.SCROLL_DOWN);
     }
 
@@ -255,35 +257,13 @@
    * @param {boolean=} opt_clear If true, will clear the focus ring.
    */
   updateFocusRing_(opt_clear) {
-    if (!this.inMenu_ || !this.calculateCurrentNode())
+    if (!this.node_)
       return;
     const id = this.node_.htmlAttributes.id;
     const onOrOff = opt_clear ? 'off' : 'on';
     MessageHandler.sendMessage(
         MessageHandler.Destination.MENU_PANEL, 'setFocusRing', [id, onOrOff]);
   }
-
-  /**
-   * Updates the value of |this.node_|.
-   *
-   * - If it has a value, change nothing.
-   * - Otherwise, if menu node has a reasonable value, set |this.node_| to menu
-   *   node.
-   * - If not, set it to null.
-   *
-   * Return |this.node_|'s value after the update.
-   *
-   * @private
-   * @return {chrome.automation.AutomationNode}
-   */
-  calculateCurrentNode() {
-    if (this.node_)
-      return this.node_;
-    this.node_ = this.menuNode();
-    if (this.node_ === this.desktop_)
-      this.node_ = null;
-    return this.node_;
-  }
 }
 
 /**
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_manager_test.extjs b/chrome/browser/resources/chromeos/switch_access/menu_manager_test.extjs
deleted file mode 100644
index 8d637f9b..0000000
--- a/chrome/browser/resources/chromeos/switch_access/menu_manager_test.extjs
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-GEN_INCLUDE(['switch_access_e2e_test_base.js']);
-
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessMenuManagerTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessMenuManagerTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-
-  /** @override */
-  setUp: function() {
-    this.menuManager =
-        switchAccess.navigationManager_.menuManager_;
-  },
-
-  runAndWaitForMenuLoad: function(website, callback) {
-    console.log("##### In runAndWaitForMenuLoad");
-    callback = this.newCallback(callback);
-
-    this.runWithLoadedTree(website, (desktop) => {
-      console.log("@@@@@ In runWithLoadedTree");
-      this.navigateToWebpage(desktop);
-
-      const onMenuReady = (e) => {
-        console.log("$$$$$ In onMenuReady");
-        if (e.target.role === chrome.automation.RoleType.MENU &&
-            e.target.name === 'Switch Access Menu') {
-          return;
-        }
-
-        assertNotEquals(this.menuManager.desktop_,
-                        this.menuManager.menuNode());
-        callback(desktop);
-      };
-
-      assertFalse(this.menuManager.inMenu_);
-
-      // Prepare to wait for the Switch Access menu to load.
-      desktop.addEventListener('childrenChanged', onMenuReady);
-
-      // Enter the menu.
-      switchAccess.enterMenu();
-      assertTrue(this.menuManager.inMenu_);
-      console.log("@@@@@ Done with runWithLoadedTree");
-    });
-  }
-}
-
-function currentNode() {
-  return switchAccess.navigationManager_.node_;
-}
-
-function currentMenuItemNode() {
-  return switchAccess.navigationManager_
-                    .menuManager_.calculateCurrentNode();
-}
-
-TEST_F_WITH_PREAMBLE(`
-// Disable the test on debug builds, as the childrenChanged events are not being
-// generated.
-#if defined(NDEBUG) && !defined(MEMORY_SANITIZER) && !defined(ADDRESS_SANITIZER)
-#define MAYBE_EnterMenu EnterMenu
-#else
-#define MAYBE_EnterMenu DISABLED_EnterMenu
-#endif
-`, 'SwitchAccessMenuManagerTest', 'MAYBE_EnterMenu', function() {
-  const website = `data:text/html;charset=utf-8,
-                  <button>button1</button>`;
-  this.runAndWaitForMenuLoad(website, (desktop) => {
-    assertTrue(this.menuManager.inMenu_);
-    assertNotEquals(desktop, this.menuManager.menuNode());
-  });
-});
-
-TEST_F_WITH_PREAMBLE(`
-// Disable the test on debug builds, as the childrenChanged events are not being
-// generated.
-#if defined(NDEBUG) && !defined(MEMORY_SANITIZER) && !defined(ADDRESS_SANITIZER)
-#define MAYBE_NavigationInMenu NavigationInMenu
-#else
-#define MAYBE_NavigationInMenu DISABLED_NavigationInMenu
-#endif
-`, 'SwitchAccessMenuManagerTest', 'MAYBE_NavigationInMenu', function() {
-  console.log("!!!!! In test NavigationInMenu");
-  const website = `data:text/html;charset=utf-8,
-                  <button>button1</button>`;
-
-  this.runAndWaitForMenuLoad(website, (desktop) => {
-    console.log("^^^^^ In callback from runAndWaitForMenuLoad");
-    assertTrue(this.menuManager.inMenu_);
-    const startNode = currentNode();
-    let menuRoot = currentMenuItemNode();
-    assertEquals(this.menuManager.menuNode(), menuRoot);
-
-    // Because we are in the Switch Access menu, navigation commands should be
-    // forwarded to the MenuManager.
-    switchAccess.moveForward();
-    // The NavigationManager should still be on the node we opened the menu on
-    // while we navigate within the menu.
-    assertEquals(startNode, currentNode());
-
-    let firstMenuItem = currentMenuItemNode();
-    assertNotEquals(menuRoot, currentMenuItemNode());
-
-    // Move to the second menu item.
-    switchAccess.moveForward();
-    assertEquals(startNode, currentNode());
-    assertNotEquals(menuRoot, currentMenuItemNode());
-    assertNotEquals(firstMenuItem, currentMenuItemNode());
-
-    // Move back to the first menu item.
-    switchAccess.moveBackward();
-    assertEquals(startNode, currentNode());
-    assertEquals(firstMenuItem, currentMenuItemNode());
-
-    // Move back to the menu itself.
-    switchAccess.moveBackward();
-    assertEquals(startNode, currentNode());
-    assertEquals(menuRoot, currentMenuItemNode());
-
-    // Move back to the final item.
-    switchAccess.moveBackward();
-    assertEquals(startNode, currentNode());
-    assertNotEquals(menuRoot, currentMenuItemNode());
-    assertNotEquals(firstMenuItem, currentMenuItemNode());
-
-    // Move forward to the menu itself.
-    switchAccess.moveForward();
-    assertEquals(startNode, currentNode());
-    assertEquals(menuRoot, currentMenuItemNode());
-  });
-});
-
-TEST_F_WITH_PREAMBLE(`
-// Disable the test on debug builds, as the childrenChanged events are not being
-// generated.
-#if defined(NDEBUG) && !defined(MEMORY_SANITIZER) && !defined(ADDRESS_SANITIZER)
-#define MAYBE_ExitMenu ExitMenu
-#else
-#define MAYBE_ExitMenu DISABLED_ExitMenu
-#endif
-`, 'SwitchAccessMenuManagerTest', 'MAYBE_ExitMenu', function() {
-  const website = `data:text/html;charset=utf-8,
-                  <button>button1</button>`;
-
-  this.runAndWaitForMenuLoad(website, (desktop) => {
-    assertTrue(this.menuManager.inMenu_);
-    assertNotEquals(desktop, currentMenuItemNode());
-
-    // Switch Access users exit the menu without performing an action by
-    // selecting the whole menu.
-    assertEquals(this.menuManager.menuNode(), currentMenuItemNode());
-    switchAccess.selectCurrentNode();
-    assertFalse(this.menuManager.inMenu_);
-  });
-});
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_panel.css b/chrome/browser/resources/chromeos/switch_access/menu_panel.css
index 038776bb..27f37f88 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_panel.css
+++ b/chrome/browser/resources/chromeos/switch_access/menu_panel.css
@@ -5,8 +5,8 @@
  */
 
 body {
-  background: white;
-  border: 5px solid grey;
+  background-color: #111;
+  border: 5px solid #333;
   height: 180px;
   margin: 0;
   padding: 4px 3px 5px 3px;
@@ -15,8 +15,14 @@
   padding: 2px;
 }
 .action {
+  color: white;
   font-size: 150%;
-  margin: 2px;
+  margin: 8px;
+  padding: 2px;
+}
+button.action {
+  background: none;
+  border: none;
 }
 .focus {
   outline: solid thick #78e428;  /** light green from automation_manager.js */
@@ -24,4 +30,11 @@
 div.focus {
   outline-color: #de742f;  /** dark orange from automation_manager.js */
 }
+img {
+  display: block;
+  margin-bottom: 5px;
+  margin-left: auto;
+  margin-right: auto;
+  width: 40px;
+}
 
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_panel.html b/chrome/browser/resources/chromeos/switch_access/menu_panel.html
index acf7439..8b359b5 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_panel.html
+++ b/chrome/browser/resources/chromeos/switch_access/menu_panel.html
@@ -12,13 +12,35 @@
   <link rel="stylesheet" href="menu_panel.css">
 </head>
 <body>
-  <div id="switchaccess_menu_actions" aria-label="Switch Access Menu">
-    <button class="action" id="select">Select</button>
-    <button class="action" id="scroll-down" disabled>Scroll down</button>
-    <button class="action" id="scroll-up" disabled>Scroll up</button>
-    <button class="action" id="scroll-right" disabled>Scroll right</button>
-    <button class="action" id="scroll-left" disabled>Scroll left</button>
-    <button class="action" id="dictation">Dictation</button>
-    <button class="action" id="options">Options</button>
+  <div id="switchaccess_menu_actions"
+      aria-label="Switch Access Menu">
+    <button class="action" id="select">
+      <img src="icons/select.svg">
+      Select
+    </button>
+    <button class="action" id="scroll-down" hidden>
+      <img src="icons/scroll_down.svg">
+      Scroll down
+    </button>
+    <button class="action" id="scroll-up" hidden>
+      <img src="icons/scroll_up.svg">
+      Scroll up
+    </button>
+    <button class="action" id="scroll-right" hidden>
+      <img src="icons/scroll_right.svg">
+      Scroll right
+    </button>
+    <button class="action" id="scroll-left" hidden>
+      <img src="icons/scroll_left.svg">
+      Scroll left
+    </button>
+    <button class="action" id="dictation">
+      <img src="icons/dictation.svg">
+      Dictation
+    </button>
+    <button class="action" id="options">
+      <img src="icons/options.svg">
+      Options
+    </button>
   </div>
 </body>
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_panel.js b/chrome/browser/resources/chromeos/switch_access/menu_panel.js
index 06d5497..da66412 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_panel.js
+++ b/chrome/browser/resources/chromeos/switch_access/menu_panel.js
@@ -94,7 +94,7 @@
 Panel.setActions = function(actions) {
   let div = document.getElementById(Panel.MENU_ID);
   for (let button of div.children)
-    button.disabled = !actions.includes(button.id);
+    button.hidden = !actions.includes(button.id);
 };
 
 /**
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/switch_access/switch_access_e2e_test_base.js
index fcf72c1..b10d64e 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access_e2e_test_base.js
@@ -57,26 +57,6 @@
     `);
   },
 
-  navigateToWebpage: function(desktop) {
-    assertTrue(desktop != null);
-    // Find the node for the desired root web area.
-    const node = new AutomationTreeWalker(desktop, constants.Dir.FORWARD, {
-                   visit: (node) =>
-                       node.role === chrome.automation.RoleType.ROOT_WEB_AREA &&
-                       SwitchAccessPredicate.isInterestingSubtree(node, node)
-                 })
-                     .next()
-                     .node;
-
-    assertTrue(node != null);
-
-    // Set the node and scope in the navigation manager.
-    switchAccess.navigationManager_.scope_ = node;
-    switchAccess.navigationManager_.node_ = node;
-    // Move to the next node (first node inside the root web area).
-    switchAccess.moveForward();
-  },
-
   /**
    * Creates a callback that optionally calls {@code opt_callback} when
    * called.  If this method is called one or more times, then
diff --git a/chrome/browser/resources/cryptotoken/enroller.js b/chrome/browser/resources/cryptotoken/enroller.js
index 1a18a7d..f23e6286 100644
--- a/chrome/browser/resources/cryptotoken/enroller.js
+++ b/chrome/browser/resources/cryptotoken/enroller.js
@@ -859,11 +859,31 @@
   });
 };
 
+const googleCorpAppId =
+    'https://www.gstatic.com/securitykey/a/google.com/origins.json';
+
 /**
  * Proxies the registration request over the WebAuthn API.
  * @private
  */
 Enroller.prototype.doRegisterWebAuthn_ = function(appId, challenge, request) {
+  if (appId == googleCorpAppId) {
+    this.doRegisterWebAuthnContinue_(appId, challenge, request, true);
+    return;
+  }
+
+  if (!chrome.cryptotokenPrivate) {
+    this.doRegisterWebAuthnContinue_(appId, challenge, request, false);
+    return;
+  }
+
+  chrome.cryptotokenPrivate.isAppIdHashInEnterpriseContext(
+      decodeWebSafeBase64ToArray(B64_encode(sha256HashOfString(appId))),
+      this.doRegisterWebAuthnContinue_.bind(this, appId, challenge, request));
+};
+
+Enroller.prototype.doRegisterWebAuthnContinue_ = function(
+    appId, challenge, request, useIndividualAttestation) {
   // Set a random ID.
   const randomId = new Uint8Array(new ArrayBuffer(16));
   crypto.getRandomValues(randomId);
@@ -878,6 +898,10 @@
     });
   }
 
+  // Request enterprise attestation for the gstatic corp App ID and domains
+  // whitelisted via enterprise policy. Otherwise request 'direct' attestation
+  // (which might later get stripped).
+  const attestationMode = useIndividualAttestation ? 'enterprise' : 'direct';
   const options = {
     publicKey: {
       rp: {
@@ -901,7 +925,7 @@
         requireResidentKey: false,
         userVerification: 'discouraged',
       },
-      attestation: 'direct',
+      attestation: attestationMode,
     },
   };
   navigator.credentials.create(options)
diff --git a/chrome/browser/resources/local_ntp/icons/add_link_white.svg b/chrome/browser/resources/local_ntp/icons/add_link_white.svg
new file mode 100644
index 0000000..21ccdfb
--- /dev/null
+++ b/chrome/browser/resources/local_ntp/icons/add_link_white.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <g fill="none" fill-rule="evenodd">
+    <rect width="2" height="12" x="7" y="2" fill="#F8F9FA"/>
+    <rect width="2" height="12" x="7" y="2" fill="#F8F9FA" transform="rotate(-90 8 8)"/>
+  </g>
+</svg>
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index 4f06496..ec409c5 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -4,9 +4,10 @@
 
 html {
   /* Material Design constants */
+  --md-add-size: 24px;
   --md-edit-menu-size: 20px;
-  --md-fallback-letter-size: 21px;
-  --md-favicon-size: 24px;
+  --md-fallback-letter-size: 16px;
+  --md-favicon-size: 32px;
   --md-icon-margin-bottom: 16px;
   --md-icon-size: 48px;
   --md-max-tiles-row: 5;
@@ -104,6 +105,12 @@
   transition-duration: 200ms;
 }
 
+html[darkmode=true] .md-tile-container.reorder .md-tile {
+  background-color: rgb(50, 54, 57);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
+      0 4px 8px 3px rgba(0, 0, 0, 0.25);
+}
+
 .md-tile-container.reorder .md-tile .md-tile-inner {
   z-index: unset;
 }
@@ -122,10 +129,18 @@
   background-color: rgba(33, 32, 36, 0.06);
 }
 
+html[darkmode=true] body:not(.reordering) .md-tile-container:hover {
+  background-color: rgba(0, 0, 0, 0.1);
+}
+
 body.dark-theme:not(.reordering) .md-tile-container:hover {
   background-color: rgba(255, 255, 255, 0.1);
 }
 
+html[darkmode=true] body.dark-theme:not(.reordering) .md-tile-container:hover {
+  background-color: rgba(218, 220, 224, 0.1);
+}
+
 body:not(.reordering) .md-tile-container:hover .md-menu {
   opacity: 1;
   transition-delay: 500ms;
@@ -157,22 +172,18 @@
 
 .md-icon {
   margin-bottom: var(--md-icon-margin-bottom);
-}
-
-.md-favicon {
   pointer-events: none;
 }
 
-.md-favicon img {
+.md-icon img {
   /* Icons returned by the NTP Icon Source are always of this size. */
   height: var(--md-icon-size);
   width: var(--md-icon-size);
 }
 
-.md-fallback-background,
-.md-add-background {
+.md-icon-background {
   align-items: center;
-  background-color: rgb(136, 136, 136);
+  background-color: rgb(241, 243, 244);
   border-radius: 50%;
   display: flex;
   height: var(--md-icon-size);
@@ -180,23 +191,29 @@
   width: var(--md-icon-size);
 }
 
-.md-fallback-letter {
-  color: white;
-  font-family: 'Segoe UI', 'Roboto', arial, sans-serif;
-  font-size: var(--md-fallback-letter-size);
-  height: var(--md-fallback-letter-size);
-  line-height: var(--md-fallback-letter-size);
-  text-align: center;
-  width: var(--md-fallback-letter-size);
-}
-
-.md-add-background {
-  background-color: rgb(241, 243, 244);
+html[darkmode=true] .md-icon-background {
+  background-color: rgb(32, 33, 36);
 }
 
 .md-add-icon {
   background: url(chrome-search://most-visited/add_link.svg) no-repeat center;
+  height: var(--md-add-size);
+  width: var(--md-add-size);
+}
+
+html[darkmode=true] .md-add-icon {
+  background: url(chrome-search://most-visited/add_link_white.svg) no-repeat center;
+}
+
+.md-fallback-letter {
+  background-color: rgb(136, 136, 136);
+  border-radius: 50%;
+  color: white;
+  font-family: 'Segoe UI', 'Roboto', arial, sans-serif;
+  font-size: var(--md-fallback-letter-size);
   height: var(--md-favicon-size);
+  line-height: var(--md-favicon-size);
+  text-align: center;
   width: var(--md-favicon-size);
 }
 
@@ -216,19 +233,25 @@
 .md-title span {
   line-height: var(--md-title-height);
 }
+
 /* Font weight on Mac and ChromeOS looks heavier on default background and
  * needs to be reduced. */
 body.mac-chromeos .md-title {
   font-weight: 400;
 }
 
-/* Apply when a custom background is set */
+/* Apply when Chrome is in dark mode. */
+html[darkmode=true] .md-title {
+  color: rgb(248, 249, 250);
+}
+
+/* Apply when a custom background is set. */
 body.dark-theme .md-tile-container:not(.reorder) .md-title {
   color: rgb(248, 249, 250);
   filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
 }
 
-/* Apply only when a theme is installed */
+/* Apply only when a theme is installed. */
 body.using-theme .md-title-container {
   background-color: white;
   /* Necessary for a "pill" shape. Using 50% creates an oval. */
@@ -236,11 +259,23 @@
   padding: 0 4px;
 }
 
+html[darkmode=true] body.using-theme .md-title-container {
+  background-color: rgb(32, 33, 36);
+}
+
+html[darkmode=true] body.using-theme .md-tile-container.reorder .md-title-container {
+  background-color: rgb(50, 54, 57);
+}
+
 body.using-theme .md-tile-container:not(.reorder) .md-title {
   color: rgba(33, 32, 36, 0.86);
   filter: unset;
 }
 
+html[darkmode=true] body.using-theme .md-tile-container:not(.reorder) .md-title {
+  color: rgb(248, 249, 250);
+}
+
 .md-menu {
   background-color: transparent;
   border: none;
@@ -282,6 +317,10 @@
   width: var(--md-menu-size);
 }
 
+html[darkmode=true] .md-menu::after {
+  background-color: rgb(248, 249, 250);
+}
+
 .md-edit-menu {
   height: var(--md-edit-menu-size);
   width: var(--md-edit-menu-size);
@@ -299,6 +338,11 @@
   background-color: rgba(33, 32, 36, 0.71);
 }
 
+html[darkmode=true] body:not(.reordering) .md-menu:hover::after,
+html[darkmode=true] body:not(.reordering) .md-menu:focus::after {
+  background-color: rgb(189, 193, 198);
+}
+
 body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
 body.dark-theme:not(.reordering) .md-menu:focus:not(.mouse-navigation)::after {
   background-color: white;
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 8c9a0fd..9271b92 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -47,12 +47,11 @@
   REORDERING: 'reordering',  // Applied while we are reordering.
   // Material Design classes.
   MD_EMPTY_TILE: 'md-empty-tile',
-  MD_FALLBACK_BACKGROUND: 'md-fallback-background',
+  MD_ICON_BACKGROUND: 'md-icon-background',
   MD_FALLBACK_LETTER: 'md-fallback-letter',
   MD_FAVICON: 'md-favicon',
   MD_ICON: 'md-icon',
   MD_ADD_ICON: 'md-add-icon',
-  MD_ADD_BACKGROUND: 'md-add-background',
   MD_MENU: 'md-menu',
   MD_EDIT_MENU: 'md-edit-menu',
   MD_TILE: 'md-tile',
@@ -716,16 +715,14 @@
   let mdIcon = document.createElement('div');
   mdIcon.className = CLASSES.MD_ICON;
 
-  let mdFavicon = document.createElement('div');
-  mdFavicon.className = CLASSES.MD_FAVICON;
   if (data.isAddButton) {
     let mdAdd = document.createElement('div');
     mdAdd.className = CLASSES.MD_ADD_ICON;
     let addBackground = document.createElement('div');
-    addBackground.className = CLASSES.MD_ADD_BACKGROUND;
+    addBackground.className = CLASSES.MD_ICON_BACKGROUND;
 
     addBackground.appendChild(mdAdd);
-    mdFavicon.appendChild(addBackground);
+    mdIcon.appendChild(addBackground);
   } else {
     let fi = document.createElement('img');
     // Set title and alt to empty so screen readers won't say the image name.
@@ -747,18 +744,18 @@
     });
     fi.addEventListener('error', function(ev) {
       let fallbackBackground = document.createElement('div');
-      fallbackBackground.className = CLASSES.MD_FALLBACK_BACKGROUND;
+      fallbackBackground.className = CLASSES.MD_ICON_BACKGROUND;
       let fallbackLetter = document.createElement('div');
       fallbackLetter.className = CLASSES.MD_FALLBACK_LETTER;
-      fallbackLetter.innerText = data.title.charAt(0).toUpperCase();
+      fallbackLetter.textContent = data.title.charAt(0).toUpperCase();
       if (navigator.userAgent.indexOf('Windows') > -1) {
         fallbackLetter.style.fontWeight = 600;
       }
-      mdFavicon.classList.add(CLASSES.FAILED_FAVICON);
+      mdIcon.classList.add(CLASSES.FAILED_FAVICON);
 
       fallbackBackground.appendChild(fallbackLetter);
-      mdFavicon.removeChild(fi);
-      mdFavicon.appendChild(fallbackBackground);
+      mdIcon.removeChild(fi);
+      mdIcon.appendChild(fallbackBackground);
 
       // Store the type for a potential later navigation.
       tileType = TileVisualType.ICON_DEFAULT;
@@ -771,10 +768,9 @@
       countLoad();
     });
 
-    mdFavicon.appendChild(fi);
+    mdIcon.appendChild(fi);
   }
 
-  mdIcon.appendChild(mdFavicon);
   mdTileInner.appendChild(mdIcon);
 
   let mdTitleContainer = document.createElement('div');
@@ -869,6 +865,11 @@
     html.dir = 'rtl';
   }
 
+  // Enable dark mode.
+  if (queryArgs['enableDarkMode'] == '1') {
+    document.documentElement.setAttribute('darkmode', true);
+  }
+
   // Enable custom links.
   if (queryArgs['enableCustomLinks'] == '1') {
     isCustomLinksEnabled = true;
diff --git a/chrome/browser/resources/local_ntp_resources.grd b/chrome/browser/resources/local_ntp_resources.grd
index 1a43d530..3eae537 100644
--- a/chrome/browser/resources/local_ntp_resources.grd
+++ b/chrome/browser/resources/local_ntp_resources.grd
@@ -10,6 +10,7 @@
     <includes>
       <include name="IDR_INSTANT_IFRAME_VALIDATION_JS" file="local_ntp\instant_iframe_validation.js" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_ADD_SVG" file="local_ntp\icons\add_link.svg" type="BINDATA" />
+      <include name="IDR_CUSTOM_LINKS_ADD_WHITE_SVG" file="local_ntp\icons\add_link_white.svg" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_EDIT_HTML" file="local_ntp\custom_links_edit.html" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_EDIT_CSS" file="local_ntp\custom_links_edit.css" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_EDIT_JS" file="local_ntp\custom_links_edit.js" type="BINDATA" />
diff --git a/chrome/browser/resources/local_state/BUILD.gn b/chrome/browser/resources/local_state/BUILD.gn
new file mode 100644
index 0000000..3a34954
--- /dev/null
+++ b/chrome/browser/resources/local_state/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":local_state",
+  ]
+}
+
+js_library("local_state") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:util",
+  ]
+}
diff --git a/chrome/browser/resources/md_extensions/BUILD.gn b/chrome/browser/resources/md_extensions/BUILD.gn
index be90cd8..9c992880 100644
--- a/chrome/browser/resources/md_extensions/BUILD.gn
+++ b/chrome/browser/resources/md_extensions/BUILD.gn
@@ -103,6 +103,7 @@
     ":activity_log_item",
     "//ui/webui/resources/cr_elements/cr_search_field:cr_search_field",
     "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js/cr/ui:focus_without_ink",
   ]
   externs_list = [
     "$externs_path/activity_log_private.js",
diff --git a/chrome/browser/resources/md_extensions/activity_log.html b/chrome/browser/resources/md_extensions/activity_log.html
index 54fb91c..30750bf 100644
--- a/chrome/browser/resources/md_extensions/activity_log.html
+++ b/chrome/browser/resources/md_extensions/activity_log.html
@@ -39,7 +39,7 @@
       <div class="page-content">
         <div class="page-header">
           <paper-icon-button-light class="icon-arrow-back no-overlap">
-            <button id="close-button" aria-label="$i18n{back}"
+            <button id="closeButton" aria-label="$i18n{back}"
                 on-click="onCloseButtonTap_"></button>
           </paper-icon-button-light>
           <span id="activity-log-heading">$i18n{activityLogPageHeading}</span>
diff --git a/chrome/browser/resources/md_extensions/activity_log.js b/chrome/browser/resources/md_extensions/activity_log.js
index ccb04b99..e828743 100644
--- a/chrome/browser/resources/md_extensions/activity_log.js
+++ b/chrome/browser/resources/md_extensions/activity_log.js
@@ -42,57 +42,127 @@
   }
 
   /**
-   * Group activity log entries by the API call and merge their counts.
-   * We currently assume that every API call matches to one activity type.
+   * Content scripts activities do not have an API call, so we use the names of
+   * the scripts executed (specified as a stringified JSON array in the args
+   * field) as the keys for an activity group instead.
+   * @private
+   * @param {!chrome.activityLogPrivate.ExtensionActivity} activity
+   * @return {!Array<string>}
+   */
+  function getActivityGroupKeysForContentScript_(activity) {
+    assert(
+        activity.activityType ===
+        chrome.activityLogPrivate.ExtensionActivityType.CONTENT_SCRIPT);
+
+    if (!activity.args) {
+      return [];
+    }
+
+    const parsedArgs = JSON.parse(activity.args);
+    assert(Array.isArray(parsedArgs), 'Invalid API data.');
+    return /** @type {!Array<string>} */ (parsedArgs);
+  }
+
+  /**
+   * Web request activities can have extra information which describes what the
+   * web request does in more detail than just the api_call. This information
+   * is in activity.other.webRequest and we use this to generate more activity
+   * group keys if possible.
+   * @private
+   * @param {!chrome.activityLogPrivate.ExtensionActivity} activity
+   * @return {!Array<string>}
+   */
+  function getActivityGroupKeysForWebRequest_(activity) {
+    assert(
+        activity.activityType ===
+        chrome.activityLogPrivate.ExtensionActivityType.WEB_REQUEST);
+
+    const apiCall = activity.apiCall;
+    const other = activity.other;
+
+    if (!other || !other.webRequest) {
+      return [apiCall];
+    }
+
+    const webRequest = /** @type {!Object} */ (JSON.parse(other.webRequest));
+    assert(typeof webRequest === 'object', 'Invalid API data');
+
+    // If there is extra information in the other.webRequest object,
+    // construct a group for each consisting of the API call and each object key
+    // in other.webRequest. Otherwise we default to just the API call.
+    return Object.keys(webRequest).length === 0 ?
+        [apiCall] :
+        Object.keys(webRequest).map(field => `${apiCall} (${field})`);
+  }
+
+  /**
+   * Group activity log entries by a key determined from each entry. Usually
+   * this would be the activity's API call though content script and web
+   * requests have different keys. We currently assume that every API call
+   * matches to one activity type.
    * @param {!Array<!chrome.activityLogPrivate.ExtensionActivity>}
    *     activityData
-   * @return {!Map<string, !extensions.ApiGroup>}
+   * @return {!Map<string, !extensions.ActivityGroup>}
    */
-  function groupActivitiesByApiCall(activityData) {
-    const activitiesByApiCall = new Map();
+  function groupActivities(activityData) {
+    const groupedActivities = new Map();
 
     for (const activity of activityData) {
-      const apiCall = activity.apiCall;
+      const activityType = activity.activityType;
       const count = activity.count;
       const pageUrl = activity.pageUrl;
 
-      if (!activitiesByApiCall.has(apiCall)) {
-        const apiGroup = {
-          apiCall,
-          count,
-          activityType: activity.activityType,
-          countsByUrl: pageUrl ? new Map([[pageUrl, count]]) : new Map()
-        };
-        activitiesByApiCall.set(apiCall, apiGroup);
-      } else {
-        const apiGroup = activitiesByApiCall.get(apiCall);
-        apiGroup.count += count;
+      const isContentScript = activityType ===
+          chrome.activityLogPrivate.ExtensionActivityType.CONTENT_SCRIPT;
+      const isWebRequest = activityType ===
+          chrome.activityLogPrivate.ExtensionActivityType.WEB_REQUEST;
 
-        if (pageUrl) {
-          const currentCount = apiGroup.countsByUrl.get(pageUrl) || 0;
-          apiGroup.countsByUrl.set(pageUrl, currentCount + count);
+      let activityGroupKeys = [activity.apiCall];
+      if (isContentScript) {
+        activityGroupKeys = getActivityGroupKeysForContentScript_(activity);
+      } else if (isWebRequest) {
+        activityGroupKeys = getActivityGroupKeysForWebRequest_(activity);
+      }
+
+      for (const key of activityGroupKeys) {
+        if (!groupedActivities.has(key)) {
+          const activityGroup = {
+            key,
+            count,
+            activityType,
+            countsByUrl: pageUrl ? new Map([[pageUrl, count]]) : new Map()
+          };
+          groupedActivities.set(key, activityGroup);
+        } else {
+          const activityGroup = groupedActivities.get(key);
+          activityGroup.count += count;
+
+          if (pageUrl) {
+            const currentCount = activityGroup.countsByUrl.get(pageUrl) || 0;
+            activityGroup.countsByUrl.set(pageUrl, currentCount + count);
+          }
         }
       }
     }
 
-    return activitiesByApiCall;
+    return groupedActivities;
   }
 
   /**
-   * Sort activities by the total count for each API call. Resolve ties by the
-   * alphabetical order of the API call name.
-   * @param {!Map<string, !extensions.ApiGroup>} activitiesByApiCall
-   * @return {!Array<!extensions.ApiGroup>}
+   * Sort activities by the total count for each activity group key. Resolve
+   * ties by the alphabetical order of the key.
+   * @param {!Map<string, !extensions.ActivityGroup>} groupedActivities
+   * @return {!Array<!extensions.ActivityGroup>}
    */
-  function sortActivitiesByCallCount(activitiesByApiCall) {
-    return Array.from(activitiesByApiCall.values()).sort(function(a, b) {
+  function sortActivitiesByCallCount(groupedActivities) {
+    return Array.from(groupedActivities.values()).sort(function(a, b) {
       if (a.count != b.count) {
         return b.count - a.count;
       }
-      if (a.apiCall < b.apiCall) {
+      if (a.key < b.key) {
         return -1;
       }
-      if (a.apiCall > b.apiCall) {
+      if (a.key > b.key) {
         return 1;
       }
       return 0;
@@ -115,9 +185,10 @@
 
       /**
        * An array representing the activity log. Stores activities grouped by
-       * API call sorted in descending order of the call count.
+       * API call or content script name sorted in descending order of the call
+       * count.
        * @private
-       * @type {!Array<!extensions.ApiGroup>}
+       * @type {!Array<!extensions.ActivityGroup>}
        */
       activityData_: {
         type: Array,
@@ -142,12 +213,19 @@
       onDataFetched: {type: Object, value: new PromiseResolver()},
 
       /** @private */
-      lastSearch_: String,
+      lastSearch_: {
+        type: String,
+        value: '',
+      },
     },
 
     /** @private {?number} */
     navigationListener_: null,
 
+    listeners: {
+      'view-enter-start': 'onViewEnterStart_',
+    },
+
     /** @override */
     attached: function() {
       // Fetch the activity log for the extension when this page is attached.
@@ -174,6 +252,15 @@
     },
 
     /**
+     * Focuses the back button when page is loaded.
+     * @private
+     */
+    onViewEnterStart_: function() {
+      Polymer.RenderStatus.afterNextRender(
+          this, () => cr.ui.focusWithoutInk(this.$.closeButton));
+    },
+
+    /**
      * @private
      * @return {boolean}
      */
@@ -220,27 +307,41 @@
     processActivities_: function(activityData) {
       this.pageState_ = ActivityLogPageState.LOADED;
       this.activityData_ =
-          sortActivitiesByCallCount(groupActivitiesByApiCall(activityData));
+          sortActivitiesByCallCount(groupActivities(activityData));
       if (!this.onDataFetched.isFulfilled) {
         this.onDataFetched.resolve();
       }
     },
 
-    /** @private */
+    /** @return {!Promise<void>} */
+    refreshActivities: function() {
+      if (this.lastSearch_ === '') {
+        return this.getActivityLog_();
+      }
+
+      return this.getFilteredActivityLog_(this.lastSearch_);
+    },
+
+    /**
+     * @private
+     * @return {!Promise<void>}
+     */
     getActivityLog_: function() {
       this.pageState_ = ActivityLogPageState.LOADING;
-      this.delegate.getExtensionActivityLog(this.extensionId).then(result => {
-        this.processActivities_(result.activities);
-      });
+      return this.delegate.getExtensionActivityLog(this.extensionId)
+          .then(result => {
+            this.processActivities_(result.activities);
+          });
     },
 
     /**
      * @private
      * @param {string} searchTerm
+     * @return {!Promise<void>}
      */
     getFilteredActivityLog_: function(searchTerm) {
       this.pageState_ = ActivityLogPageState.LOADING;
-      this.delegate
+      return this.delegate
           .getFilteredExtensionActivityLog(this.extensionId, searchTerm)
           .then(result => {
             this.processActivities_(result.activities);
@@ -258,11 +359,7 @@
       }
 
       this.lastSearch_ = searchTerm;
-      if (searchTerm === '') {
-        this.getActivityLog_();
-      } else {
-        this.getFilteredActivityLog_(searchTerm);
-      }
+      this.refreshActivities();
     },
   });
 
diff --git a/chrome/browser/resources/md_extensions/activity_log_item.html b/chrome/browser/resources/md_extensions/activity_log_item.html
index b219909..ecfd3b2 100644
--- a/chrome/browser/resources/md_extensions/activity_log_item.html
+++ b/chrome/browser/resources/md_extensions/activity_log_item.html
@@ -52,9 +52,12 @@
         flex: 0 85px;
       }
 
-      #api-call {
+      #activity-key {
         flex: 1;
         margin-inline-start: 10px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
       }
 
       #activity-count {
@@ -83,7 +86,7 @@
         on-click="onExpandTap_">
       <div id="activity-call-and-count">
         <span id="activity-type">[[data.activityType]]</span>
-        <span id="api-call">[[data.apiCall]]</span>
+        <span id="activity-key" title="[[data.key]]">[[data.key]]</span>
         <span id="activity-count">[[data.count]]</span>
       </div>
       <cr-expand-button expanded="{{isExpanded_}}"
diff --git a/chrome/browser/resources/md_extensions/activity_log_item.js b/chrome/browser/resources/md_extensions/activity_log_item.js
index 4d5833af..64ccee13 100644
--- a/chrome/browser/resources/md_extensions/activity_log_item.js
+++ b/chrome/browser/resources/md_extensions/activity_log_item.js
@@ -7,13 +7,13 @@
 
   /**
    * @typedef {{
-   *   apiCall: string,
+   *   key: string,
    *   count: number,
    *   activityType: !chrome.activityLogPrivate.ExtensionActivityFilter,
    *   countsByUrl: !Map<string, number>
    * }}
    */
-  let ApiGroup;
+  let ActivityGroup;
 
   /**
    * A struct used to describe each url and its associated counts. The id is
@@ -30,9 +30,9 @@
 
     properties: {
       /**
-       * The underlying ApiGroup that provides data for the
+       * The underlying ActivityGroup that provides data for the
        * ActivityLogItem displayed.
-       * @type {!extensions.ApiGroup}
+       * @type {!extensions.ActivityGroup}
        */
       data: Object,
 
@@ -58,8 +58,9 @@
     },
 
     /**
-     * Sort the page URLs by the number of times the API call was made for that
-     * page URL. Resolve ties by the alphabetical order of the page URL.
+     * Sort the page URLs by the number of times it was associated with the key
+     * for this ActivityGroup (API call or content script invocation.) Resolve
+     * ties by the alphabetical order of the page URL.
      * @private
      * @return {!Array<PageUrlItem>}
      */
@@ -82,8 +83,8 @@
     },
 
     /**
-     * Show the API call count for a particular page URL if more than one page
-     * URL is associated with this API call.
+     * Show the call count for a particular page URL if more than one page
+     * URL is associated with the key for this ActivityGroup.
      * @private
      * @return {boolean}
      */
@@ -94,6 +95,6 @@
 
   return {
     ActivityLogItem: ActivityLogItem,
-    ApiGroup: ApiGroup,
+    ActivityGroup: ActivityGroup,
   };
 });
diff --git a/chrome/browser/resources/md_extensions/detail_view.html b/chrome/browser/resources/md_extensions/detail_view.html
index 8ba28cb0..b4ed12f 100644
--- a/chrome/browser/resources/md_extensions/detail_view.html
+++ b/chrome/browser/resources/md_extensions/detail_view.html
@@ -388,7 +388,7 @@
             on-click="onExtensionOptionsTap_">
         </cr-link-row>
         <cr-link-row class="hr" icon-class="subpage-arrow"
-            id="extensions-activity-log-link" hidden$="[[!showActivityLog]]"
+            id="extensionsActivityLogLink" hidden$="[[!showActivityLog]]"
             label="$i18n{viewActivityLog}" on-click="onActivityLogTap_">
         </cr-link-row>
         <cr-link-row class="hr" hidden="[[!data.manifestHomePageUrl.length]]"
diff --git a/chrome/browser/resources/md_extensions/detail_view.js b/chrome/browser/resources/md_extensions/detail_view.js
index d1666696..8eb81e0 100644
--- a/chrome/browser/resources/md_extensions/detail_view.js
+++ b/chrome/browser/resources/md_extensions/detail_view.js
@@ -34,6 +34,9 @@
 
       /** Whether "View Activity Log" link should be shown. */
       showActivityLog: Boolean,
+
+      /** Whether the user navigated to this page from the activity log page. */
+      fromActivityLog: Boolean,
     },
 
     observers: [
@@ -49,8 +52,12 @@
      * @private
      */
     onViewEnterStart_: function() {
+      const elementToFocus = this.fromActivityLog ?
+          this.$.extensionsActivityLogLink :
+          this.$.closeButton;
+
       Polymer.RenderStatus.afterNextRender(
-          this, () => cr.ui.focusWithoutInk(this.$.closeButton));
+          this, () => cr.ui.focusWithoutInk(elementToFocus));
     },
 
     /** @private */
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index a8d153e..6a43fc2 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -90,6 +90,7 @@
         <template>
           <extensions-detail-view delegate="[[delegate]]" slot="view"
               in-dev-mode="[[inDevMode]]"
+              from-activity-log="[[fromActivityLog_]]"
               show-activity-log="[[showActivityLog]]"
               incognito-available="[[incognitoAvailable_]]"
               data="[[detailViewItem_]]">
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js
index aa7e05b1..8331782 100644
--- a/chrome/browser/resources/md_extensions/manager.js
+++ b/chrome/browser/resources/md_extensions/manager.js
@@ -133,6 +133,13 @@
       /** @private */
       showOptionsDialog_: Boolean,
 
+      /**
+       * Whether the last page the user navigated from was the activity log
+       * page.
+       * @private
+       */
+      fromActivityLog_: Boolean,
+
       // <if expr="chromeos">
       /** @private */
       kioskEnabled_: {
@@ -150,6 +157,8 @@
 
     listeners: {
       'load-error': 'onLoadError_',
+      'view-enter-start': 'onViewEnterStart_',
+      'view-exit-start': 'onViewExitStart_',
       'view-exit-finish': 'onViewExitFinish_',
     },
 
@@ -533,15 +542,32 @@
     },
 
     /** @private */
+    onViewEnterStart_: function() {
+      this.fromActivityLog_ = false;
+    },
+
+    /**
+     * @param {!CustomEvent} e
+     * @private
+     */
+    onViewExitStart_: function(e) {
+      const viewType = e.composedPath()[0].tagName;
+      this.fromActivityLog_ = viewType == 'EXTENSIONS-ACTIVITY-LOG';
+    },
+
+    /**
+     * @param {!CustomEvent} e
+     * @private
+     */
     onViewExitFinish_: function(e) {
-      const viewType = e.path[0].tagName;
+      const viewType = e.composedPath()[0].tagName;
       if (viewType == 'EXTENSIONS-ITEM-LIST' ||
           viewType == 'EXTENSIONS-KEYBOARD-SHORTCUTS' ||
           viewType == 'EXTENSIONS-ACTIVITY-LOG') {
         return;
       }
 
-      const extensionId = e.path[0].data.id;
+      const extensionId = e.composedPath()[0].data.id;
       const list = this.$$('extensions-item-list');
       const button = viewType == 'EXTENSIONS-DETAIL-VIEW' ?
           list.getDetailsButton(extensionId) :
diff --git a/chrome/browser/resources/md_extensions/service.js b/chrome/browser/resources/md_extensions/service.js
index d5a32f7c..6851b19 100644
--- a/chrome/browser/resources/md_extensions/service.js
+++ b/chrome/browser/resources/md_extensions/service.js
@@ -357,10 +357,9 @@
     getFilteredExtensionActivityLog(extensionId, searchTerm) {
       const anyType = chrome.activityLogPrivate.ExtensionActivityFilter.ANY;
 
-      // Construct one filter for each API call we will make: one for exact
-      // match on the api call (api does not support partial matches), one for
-      // substring search by page URL, and one for substring search by argument
-      // URL. For the last two, % acts as a wildcard.
+      // Construct one filter for each API call we will make: one for substring
+      // search by api call, one for substring search by page URL, and one for
+      // substring search by argument URL. % acts as a wildcard.
       const activityLogFilters = [
         {
           activityType: anyType,
diff --git a/chrome/browser/resources/md_history/app.html b/chrome/browser/resources/md_history/app.html
index be965fa5..e1306c4f 100644
--- a/chrome/browser/resources/md_history/app.html
+++ b/chrome/browser/resources/md_history/app.html
@@ -58,6 +58,10 @@
       :host([toolbar-shadow_]) #drop-shadow {
         opacity: var(--cr-container-shadow-max-opacity);
       }
+
+      :host-context([dark]) cr-drawer {
+        --cr-drawer-heading-color: var(--cr-primary-text-color);
+      }
     </style>
     <history-query-manager query-state="{{queryState_}}"
         query-result="[[queryResult_]]"
diff --git a/chrome/browser/resources/md_history/history.html b/chrome/browser/resources/md_history/history.html
index 9ad2898..0607638 100644
--- a/chrome/browser/resources/md_history/history.html
+++ b/chrome/browser/resources/md_history/history.html
@@ -63,6 +63,7 @@
 
     html[dark] #loading-toolbar {
       border-bottom: 1px solid var(--md-toolbar-border-color);
+      color: rgb(232, 234, 237);  /* --google-grey-200 */
     }
 
     #loading-message {
diff --git a/chrome/browser/resources/md_history/history_item.html b/chrome/browser/resources/md_history/history_item.html
index add4ccb..435c072 100644
--- a/chrome/browser/resources/md_history/history_item.html
+++ b/chrome/browser/resources/md_history/history_item.html
@@ -126,6 +126,10 @@
         margin-inline-start: 77px;
       }
 
+      :host-context([dark]) #time-gap-separator {
+        border-color: var(--google-grey-refresh-500);
+      }
+
       #background-clip {
         /* Prevent artifacts when zoomed by overlapping the next item. */
         bottom: -0.4px;
@@ -189,13 +193,13 @@
         <span id="time-accessed">
           [[item.readableTimestamp]]
         </span>
-        <div class="website-icon" id="icon"></div>
         <div id="title-and-domain">
-          <a href="[[item.url]]" id="title" class="website-title"
+          <a href="[[item.url]]" id="link" class="website-link"
               focus-row-control focus-type="link"
               title="[[item.title]]" on-click="onLinkClick_"
               on-contextmenu="onLinkRightClick_">
-            <history-searched-label title="[[item.title]]"
+            <div class="website-icon" id="icon"></div>
+            <history-searched-label class="website-title" title="[[item.title]]"
                 search-term="[[searchTerm]]"></history-searched-label>
           </a>
           <span id="domain">[[item.domain]]</span>
diff --git a/chrome/browser/resources/md_history/history_item.js b/chrome/browser/resources/md_history/history_item.js
index b18fa9a..fc0670a 100644
--- a/chrome/browser/resources/md_history/history_item.js
+++ b/chrome/browser/resources/md_history/history_item.js
@@ -287,7 +287,7 @@
      */
     getCustomEquivalent(sampleElement) {
       return sampleElement.getAttribute('focus-type') === 'star' ?
-          this.$.title :
+          this.$.link :
           null;
     },
   });
diff --git a/chrome/browser/resources/md_history/searched_label.js b/chrome/browser/resources/md_history/searched_label.js
index a63d9070..573c703 100644
--- a/chrome/browser/resources/md_history/searched_label.js
+++ b/chrome/browser/resources/md_history/searched_label.js
@@ -21,7 +21,10 @@
    * @private
    */
   setSearchedTextToBold_: function() {
-    let i = 0;
+    if (this.title === undefined) {
+      return;
+    }
+
     const titleText = this.title;
 
     if (this.searchTerm == '' || this.searchTerm == null) {
@@ -30,6 +33,7 @@
     }
 
     const re = new RegExp(quoteString(this.searchTerm), 'gim');
+    let i = 0;
     let match;
     this.textContent = '';
     while (match = re.exec(titleText)) {
diff --git a/chrome/browser/resources/md_history/shared_style.html b/chrome/browser/resources/md_history/shared_style.html
index 424a5e1..daccd428a 100644
--- a/chrome/browser/resources/md_history/shared_style.html
+++ b/chrome/browser/resources/md_history/shared_style.html
@@ -6,7 +6,7 @@
   <template>
     <style include="cr-hidden-style">
       a {
-        color: var(--link-color);
+        color: var(--cr-link-color);
         text-decoration: none;
       }
 
@@ -38,15 +38,22 @@
       .website-icon {
         background-repeat: no-repeat;
         background-size: 16px;
+        flex: none;
         height: 16px;
         width: 16px;
       }
 
-      .website-title {
+      .website-link {
+        align-items: center;
         color: var(--cr-primary-text-color);
-        margin-inline-start: 16px;
+        display: flex;
         overflow: hidden;
         text-decoration: none;
+      }
+
+      .website-title {
+        margin-inline-start: 16px;
+        overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap;
       }
diff --git a/chrome/browser/resources/md_history/shared_vars.html b/chrome/browser/resources/md_history/shared_vars.html
index ee1e436..b534248 100644
--- a/chrome/browser/resources/md_history/shared_vars.html
+++ b/chrome/browser/resources/md_history/shared_vars.html
@@ -25,12 +25,17 @@
     --item-height: 44px;
     --iron-icon-height: var(--cr-icon-size);
     --iron-icon-width: var(--cr-icon-size);
-    --link-color: var(--google-blue-700);
     --separator-color: rgba(0, 0, 0, 0.08);
     --side-bar-width: 256px;
     --sidebar-footer-text-color: #6e6e6e;
     --sidebar-unselected-color: #5a5a5a;
     --toolbar-height: 56px;
   }
+
+  html[dark] {
+    --history-item-time-color: var(--cr-secondary-text-color);
+    --sidebar-unselected-color: var(--cr-secondary-text-color);
+    /* TODO(dbeam): check with Namrata about --separator-color */
+  }
 </style>
 </custom-style>
diff --git a/chrome/browser/resources/md_history/side_bar.html b/chrome/browser/resources/md_history/side_bar.html
index 791d35d89..cfd310a6 100644
--- a/chrome/browser/resources/md_history/side_bar.html
+++ b/chrome/browser/resources/md_history/side_bar.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
@@ -61,7 +62,7 @@
       }
 
       iron-selector > a.iron-selected {
-        color: var(--link-color);
+        color: var(--cr-link-color);
       }
 
       iron-selector > a[disabled] {
diff --git a/chrome/browser/resources/md_history/synced_device_card.html b/chrome/browser/resources/md_history/synced_device_card.html
index 8dd8bf8..7a9b5a14 100644
--- a/chrome/browser/resources/md_history/synced_device_card.html
+++ b/chrome/browser/resources/md_history/synced_device_card.html
@@ -109,11 +109,12 @@
           <template is="dom-repeat" items="[[tabs]]" as="tab" id="tab-list"
               notify-dom-change>
             <div class="item-container">
-              <div class="website-icon"></div>
-              <a href="[[tab.url]]" class="website-title" title="[[tab.title]]"
+              <a href="[[tab.url]]" class="website-link" title="[[tab.title]]"
                   on-click="openTab_" on-contextmenu="onLinkRightClick_">
-                <history-searched-label title="[[tab.title]]"
-                    search-term="[[searchTerm]]"></history-searched-label>
+              <div class="website-icon"></div>
+              <history-searched-label class="website-title"
+                  title="[[tab.title]]"
+                  search-term="[[searchTerm]]"></history-searched-label>
               </a>
             </div>
             <div class="window-separator"
diff --git a/chrome/browser/resources/md_history/synced_device_card.js b/chrome/browser/resources/md_history/synced_device_card.js
index 6c2ec3db..5f95836 100644
--- a/chrome/browser/resources/md_history/synced_device_card.js
+++ b/chrome/browser/resources/md_history/synced_device_card.js
@@ -58,7 +58,7 @@
     if (this.opened) {
       this.shadowRoot.querySelectorAll('.item-container').forEach(function(el) {
         const row = new cr.ui.FocusRow(el, null);
-        row.addItem('title', '.website-title');
+        row.addItem('link', '.website-link');
         rows.push(row);
       });
     }
diff --git a/chrome/browser/resources/omnibox/omnibox.html b/chrome/browser/resources/omnibox/omnibox.html
index 9d23b7a..c604afd 100644
--- a/chrome/browser/resources/omnibox/omnibox.html
+++ b/chrome/browser/resources/omnibox/omnibox.html
@@ -38,6 +38,18 @@
             </span>
           </label>
         </div>
+        <div class="row response-selection-container">
+          <span>Quer<span class="accesskey">y</span></span>
+          <input id="response-selection" type="number" accesskey="y" value="0"
+                 min="0" max="0">
+          <span>of <span id="responses-count">0</span></span>
+        </div>
+        <div id="history-warning" class="row" hidden>
+          <span class="warning-text"
+                title="The output you are currently viewing is historical and may not reflect the current inputs.">
+            Viewing historical data
+          </span>
+        </div>
       </div>
 
       <div class="top-column">
@@ -147,30 +159,30 @@
                placeholder="Enter filter (e.g. 'google', 'is:star', 'not:del') [Alt+G]"
                title="Checks each cell individually; i.e. filter text should not span multiple columns. Supports fuzzyness; each character of filter text must be present in the cell, either adjacent to the previous matched character, or at the start of a new word. Words are defined as being delimited by either capital letters, groups of digits, or non alpha characters. E.g. 'abc' matches 'abc', 'a big cat', 'a-bigCat', 'a very big cat', and 'an amBer cat'; but does not match 'abigcat' or 'an amber cat'. 'green rainbow' is matched by 'gre rain', but not by 'gre bow'. One exception is the first character, which may be matched mid-word. E.g. 'een rain' can also match 'green rainbow'. Boolean properties can be searched for via the property name prefixed by 'is:' or 'not:'. Boolean property names are: 'Can Be Default', 'Starred', 'Has Tab Match', 'Del', 'Prev', and 'Done'.">
         <div class="row">
-          <span id="copy-text" class="button"
+          <span id="copy-text" class="button" accesskey="x"
                 title="Copy visible table in text format. This is affected by the visibility of ouput; i.e. toggling 'Show all details' affects what will be copied.">
             <i class="icon copy-icon"></i>
-            <span>Copy as text</span>
+            <span>Copy as te<span class="accesskey">x</span>t</span>
           </span>
         </div>
         <div class="row">
-          <span id="download-json" class="button"
+          <span id="download-json" class="button" accesskey="n"
                 title="Download responses in JSON format. This is not affected by the visibility of output and will include responses in their entirety as well as query and display inputs.">
             <i class="icon copy-icon"></i>
-            <span>Download as JSON</span>
+            <span>Dow<span class="accesskey">n</span>load as JSON</span>
           </span>
         </div>
         <div class="row">
           <label id="import-json" class="icon-button button drag-container"
-                 title="Import JSON">
+                 accesskey="m" title="Import JSON">
             <input id="import-json-input" type="file" accept=".json">
             <i class="icon copy-icon"></i>
-            <span>Import JSON</span>
+            <span>I<span class="accesskey">m</span>port JSON</span>
           </label>
         </div>
         <div id="imported-warning" class="row" hidden>
           <span class="warning-text"
-                title="The output you are currently viewing was imported and may not be the same as if you had entered these inputs now.">
+                title="The output you are currently viewing is imported and may not be the same as if you had entered these inputs now.">
             Viewing imported data
           </span>
         </div>
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index ccff3669..bcccc43 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -22,7 +22,7 @@
  * @typedef {{
  *   queryInputs: QueryInputs,
  *   displayInputs: DisplayInputs,
- *   responses: !Array<!mojom.OmniboxResult>,
+ *   responsesHistory: !Array<!Array<!mojom.OmniboxResult>>,
  * }}
  */
 let OmniboxExport;
@@ -62,7 +62,7 @@
     this.callbackRouter_.handleNewAutocompleteQuery.addListener(
         isPageController => {
           if (isPageController || omniboxInput.connectWindowOmnibox) {
-            omniboxOutput.clearAutocompleteResponses();
+            omniboxOutput.prepareNewQuery();
           }
         });
     this.callbackRouter_.handleAnswerImageData.addListener(
@@ -106,6 +106,13 @@
   omniboxInput.addEventListener('copy-text', () => exportDelegate.copyText());
   omniboxInput.addEventListener(
       'download-json', () => exportDelegate.downloadJson());
+  omniboxInput.addEventListener(
+      'response-select',
+      event => omniboxOutput.updateSelectedResponseIndex(event.detail));
+
+  omniboxOutput.addEventListener(
+      'responses-count-changed',
+      event => omniboxInput.responsesCount = event.detail);
 });
 
 class ExportDelegate {
@@ -127,20 +134,23 @@
     // best-attempt; e.g. if responses are missing 'relevance' values, then
     // those cells will be left blank.
     const valid = importData && importData.queryInputs &&
-        importData.displayInputs && Array.isArray(importData.responses) &&
-        importData.responses.every(
-            response => Array.isArray(response.combinedResults) &&
-                Array.isArray(response.resultsByProvider));
+        importData.displayInputs &&
+        Array.isArray(importData.responsesHistory) &&
+        importData.responsesHistory.every(
+            responses => Array.isArray(responses) &&
+                responses.every(
+                    response => Array.isArray(response.combinedResults) &&
+                        Array.isArray(response.resultsByProvider)));
     if (!valid) {
       return console.error(
           'invalid import format:',
-          'expected {queryInputs: {}, displayInputs: {}, responses: []}');
+          'expected {queryInputs: {}, displayInputs: {}, responsesHistory: []}');
     }
     this.omniboxInput_.queryInputs = importData.queryInputs;
     this.omniboxInput_.displayInputs = importData.displayInputs;
     this.omniboxOutput_.updateQueryInputs(importData.queryInputs);
     this.omniboxOutput_.updateDisplayInputs(importData.displayInputs);
-    this.omniboxOutput_.setAutocompleteResponses(importData.responses);
+    this.omniboxOutput_.setResponsesHistory(importData.responsesHistory);
   }
 
   copyText() {
@@ -152,7 +162,7 @@
     const exportObj = {
       queryInputs: this.omniboxInput_.queryInputs,
       displayInputs: this.omniboxInput_.displayInputs,
-      responses: this.omniboxOutput_.responses,
+      responsesHistory: this.omniboxOutput_.responsesHistory,
     };
     const fileName = `omnibox_debug_export_${exportObj.queryInputs.inputText}_${
         new Date().toISOString()}.json`;
diff --git a/chrome/browser/resources/omnibox/omnibox_input.css b/chrome/browser/resources/omnibox/omnibox_input.css
index d0dfb63..05ce78f4 100644
--- a/chrome/browser/resources/omnibox/omnibox_input.css
+++ b/chrome/browser/resources/omnibox/omnibox_input.css
@@ -62,6 +62,7 @@
 }
 
 input[type=text]:hover,
+input[type=number]:hover,
 select:hover {
   background-color: var(--text-hover-color);
 }
@@ -72,6 +73,7 @@
 }
 
 input[type=text]:focus,
+input[type=number]:focus,
 select:focus,
 .button:active {
   background-color: var(--text-active-color);
@@ -86,15 +88,20 @@
 /* text input */
 
 input[type=text],
+input[type=number],
 select {
   background-color: var(--text-inactive-color);
   border: 2px solid var(--text-inactive-color);
   border-radius: 5px;
 }
 
-input[type=text] {
+input[type=text],
+input[type=number] {
   box-sizing: border-box;
   padding: 3px 7px;
+}
+
+input[type=text] {
   width: 100%;
 }
 
@@ -133,6 +140,29 @@
   vertical-align: top;
 }
 
+/* response-selection-container */
+
+.response-selection-container,
+.checkbox-container,
+.button,
+.warning-text {
+  align-items: center;
+  border-radius: 5px;
+  display: inline-flex;
+  min-height: var(--row-height);
+  padding-inline-end: var(--input-alignment-indentation);
+  padding-inline-start: var(--input-alignment-indentation);
+  user-select: none;
+}
+
+.response-selection-container input {
+  flex-grow: 1;
+  margin-inline-end: 5px;
+  margin-inline-start: 5px;
+  max-width: 80px;
+  min-width: 60px;
+}
+
 /* select */
 
 select {
@@ -147,18 +177,6 @@
 /* stylized checkbox */
 
 .checkbox-container,
-.button,
-.warning-text {
-  align-items: center;
-  border-radius: 5px;
-  display: inline-flex;
-  min-height: var(--row-height);
-  padding-inline-end: var(--input-alignment-indentation);
-  padding-inline-start: var(--input-alignment-indentation);
-  user-select: none;
-}
-
-.checkbox-container,
 .button {
   cursor: pointer;
 }
diff --git a/chrome/browser/resources/omnibox/omnibox_input.js b/chrome/browser/resources/omnibox/omnibox_input.js
index e8976e7..39b6b56 100644
--- a/chrome/browser/resources/omnibox/omnibox_input.js
+++ b/chrome/browser/resources/omnibox/omnibox_input.js
@@ -127,6 +127,11 @@
     this.$$('#input-text')
         .addEventListener(
             'input', this.positionCursorPositionIndicators_.bind(this));
+
+    this.$$('#response-selection')
+        .addEventListener('input', this.onResponseSelectionChanged_.bind(this));
+    this.$$('#response-selection')
+        .addEventListener('blur', this.onResponseSelectionBlur_.bind(this));
   }
 
   /**
@@ -218,6 +223,32 @@
     }
   }
 
+  /** @private */
+  onResponseSelectionChanged_() {
+    const {value, max} = this.$$('#response-selection');
+    this.$$('#history-warning').hidden = value === '0' || value === max;
+    this.dispatchEvent(new CustomEvent('response-select', {detail: value - 1}));
+  }
+
+  /** @private */
+  onResponseSelectionBlur_() {
+    const {value, min, max} = this.$$('#response-selection');
+    this.$$('#response-selection').value = Math.max(Math.min(value, max), min);
+    this.onResponseSelectionChanged_();
+  }
+
+  /** @param {number} value */
+  set responsesCount(value) {
+    if (this.$$('#response-selection').value ===
+        this.$$('#response-selection').max) {
+      this.$$('#response-selection').value = value;
+    }
+    this.$$('#response-selection').max = value;
+    this.$$('#response-selection').min = value ? 1 : 0;
+    this.$$('#responses-count').textContent = value;
+    this.onResponseSelectionBlur_();
+  }
+
   /** @return {boolean} */
   get connectWindowOmnibox() {
     return this.$$('#connect-window-omnibox').checked;
diff --git a/chrome/browser/resources/omnibox/omnibox_output.js b/chrome/browser/resources/omnibox/omnibox_output.js
index 4695615..ed4dbef 100644
--- a/chrome/browser/resources/omnibox/omnibox_output.js
+++ b/chrome/browser/resources/omnibox/omnibox_output.js
@@ -25,8 +25,10 @@
     constructor() {
       super('omnibox-output-template');
 
-      /** @type {!Array<!mojom.OmniboxResult>} */
-      this.responses = [];
+      /** @private {number} */
+      this.selectedResponseIndex_ = 0;
+      /** @type {!Array<!Array<!mojom.OmniboxResult>>} */
+      this.responsesHistory = [];
       /** @private {!Array<!OutputResultsGroup>} */
       this.resultsGroups_ = [];
       /** @private {!QueryInputs} */
@@ -54,16 +56,53 @@
       this.updateFilterHighlights_();
     }
 
-    clearAutocompleteResponses() {
-      this.responses = [];
-      this.resultsGroups_ = [];
-      clearChildren(this.$$('#contents'));
+    /** @param {!Array<!Array<!mojom.OmniboxResult>>} responsesHistory */
+    setResponsesHistory(responsesHistory) {
+      this.responsesHistory = responsesHistory;
+      this.dispatchEvent(new CustomEvent(
+          'responses-count-changed', {detail: responsesHistory.length}));
+      this.updateSelectedResponseIndex(this.selectedResponseIndex_);
+    }
+
+    /** @param {number} selection */
+    updateSelectedResponseIndex(selection) {
+      if (selection >= 0 && selection < this.responsesHistory.length) {
+        this.selectedResponseIndex_ = selection;
+        this.clearResultsGroups_();
+        this.responsesHistory[selection].forEach(
+            this.createResultsGroup_.bind(this));
+      }
+    }
+
+    prepareNewQuery() {
+      this.responsesHistory.push([]);
+      this.dispatchEvent(new CustomEvent(
+          'responses-count-changed', {detail: this.responsesHistory.length}));
     }
 
     /** @param {!mojom.OmniboxResult} response */
     addAutocompleteResponse(response) {
-      this.responses.push(response);
+      const lastIndex = this.responsesHistory.length - 1;
+      this.responsesHistory[lastIndex].push(response);
+      if (lastIndex === this.selectedResponseIndex_) {
+        this.createResultsGroup_(response);
+      }
+    }
 
+    /**
+     * Clears result groups from the UI.
+     * @private
+     */
+    clearResultsGroups_() {
+      this.resultsGroups_ = [];
+      clearChildren(this.$$('#contents'));
+    }
+
+    /**
+     * Creates and adds a result group to the UI.
+     * @private @param {!mojom.OmniboxResult} response
+     */
+    createResultsGroup_(response) {
       const resultsGroup =
           OutputResultsGroup.create(response, this.queryInputs_.cursorPosition);
       this.resultsGroups_.push(resultsGroup);
@@ -73,18 +112,13 @@
       this.updateFilterHighlights_();
     }
 
-    /** @param {!Array<!mojom.OmniboxResult>} responses */
-    setAutocompleteResponses(responses) {
-      this.clearAutocompleteResponses();
-      responses.forEach(this.addAutocompleteResponse.bind(this));
-    }
-
     /**
      * @param {string} url
      * @param {string} data
      */
     updateAnswerImage(url, data) {
-      this.matches.forEach(match => match.updateAnswerImage(url, data));
+      this.autocompleteMatches.forEach(
+          match => match.updateAnswerImage(url, data));
     }
 
     /**
@@ -113,12 +147,13 @@
 
     /** @private */
     updateFilterHighlights_() {
-      this.matches.forEach(match => match.filter(this.filterText_));
+      this.autocompleteMatches.forEach(match => match.filter(this.filterText_));
     }
 
     /** @return {!Array<!OutputMatch>} */
-    get matches() {
-      return this.resultsGroups_.flatMap(resultsGroup => resultsGroup.matches);
+    get autocompleteMatches() {
+      return this.resultsGroups_.flatMap(
+          resultsGroup => resultsGroup.autocompleteMatches);
     }
 
     /** @return {string} */
@@ -260,7 +295,8 @@
               !showDetails && !column.displayAlways);
 
       // Show certain columns only if they showDetails is true.
-      this.matches.forEach(match => match.updateVisibility(showDetails));
+      this.autocompleteMatches.forEach(
+          match => match.updateVisibility(showDetails));
     }
 
     /**
@@ -274,10 +310,10 @@
     }
 
     /** @return {!Array<!OutputMatch>} */
-    get matches() {
+    get autocompleteMatches() {
       return [this.combinedResults]
           .concat(this.individualResultsList)
-          .flatMap(results => results.matches);
+          .flatMap(results => results.autocompleteMatches);
     }
 
     /** @return {!Array<string>} */
@@ -321,24 +357,25 @@
       super();
       this.classList.add('body');
       /** @type {!Array<!OutputMatch>} */
-      this.matches = [];
+      this.autocompleteMatches = [];
     }
 
     /** @param {!Array<!mojom.AutocompleteMatch>} results */
     set results(results) {
-      this.matches.forEach(match => match.remove());
-      this.matches = results.map(OutputMatch.create);
-      this.matches.forEach(this.appendChild.bind(this));
+      this.autocompleteMatches.forEach(match => match.remove());
+      this.autocompleteMatches = results.map(OutputMatch.create);
+      this.autocompleteMatches.forEach(this.appendChild.bind(this));
     }
 
     /** @return {?string} */
     get innerHeaderText() {
-      return this.matches[0].providerName;
+      return this.autocompleteMatches[0].providerName;
     }
 
     /** @return {boolean} */
     get hasAdditionalProperties() {
-      return this.matches.some(match => match.hasAdditionalProperties);
+      return this.autocompleteMatches.some(
+          match => match.hasAdditionalProperties);
     }
   }
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js b/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js
index a2d5d44..f3769e2 100644
--- a/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js
+++ b/chrome/browser/resources/pdf/elements/viewer-ink-host/externs.js
@@ -2,9 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * @typedef {{
+ *   x: number,
+ *   y: number
+ * }}
+ */
+let Point;
+
+/**
+ * @typedef {{
+ *   width: number,
+ *   height: number
+ * }}
+ */
+let Size;
+
 class Viewport {
-  /** @return {{width: number, height: number}} */
-  getDocumentDimensions() {}
+  /**
+   * @param {number} zoom
+   * @return {{width: number, height: number}}
+   */
+  getDocumentDimensions(zoom) {}
+
+  /**
+   * @param {!Point} point
+   * @return {boolean}
+   */
+  isPointInsidePage(point) {}
+
+  /** @return {!Point} */
+  get position() {}
+
+  /** @return {!Size} */
+  get size() {}
+
+  /** @return {number} */
+  get zoom() {}
 }
 
 /** @type {Object} */
@@ -14,4 +48,4 @@
 Viewport.PAGE_SHADOW.top;
 
 /** @type {number} */
-Viewport.PAGE_SHADOW.left;
\ No newline at end of file
+Viewport.PAGE_SHADOW.left;
diff --git a/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js b/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
index 0eb9bd55..64c3d5e0 100644
--- a/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
+++ b/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
@@ -37,6 +37,20 @@
   lastZoom_: null,
 
   /**
+   * Used to conditionally allow a 'touchstart' event to cause
+   * a gesture. If we receive a 'touchstart' with this timestamp
+   * we will skip calling `preventDefault()`.
+   * @private {?number}
+   */
+  allowTouchStartTimeStamp_: null,
+
+  /** @private {boolean} */
+  penMode_: false,
+
+  /** @type {Viewport} */
+  viewport: null,
+
+  /**
    * Whether we should suppress pointer events due to a gesture,
    * eg. pinch-zoom.
    *
@@ -53,6 +67,11 @@
     touchstart: 'onTouchStart_',
   },
 
+  /** Turns off pen mode if it is active. */
+  resetPenMode() {
+    this.penMode_ = false;
+  },
+
   /** @param {AnnotationTool} tool */
   setAnnotationTool(tool) {
     this.tool_ = tool;
@@ -85,8 +104,10 @@
 
   /** @param {TouchEvent} e */
   onTouchStart_: function(e) {
-    // TODO(dstockwell): prevent this conditionally when in "pen mode"
-    e.preventDefault();
+    if (e.timeStamp !== this.allowTouchStartTimeStamp_) {
+      e.preventDefault();
+    }
+    this.allowTouchStartTimeStamp_ = null;
   },
 
   /** @param {PointerEvent} e */
@@ -95,6 +116,10 @@
       return;
     }
 
+    if (e.pointerType == 'pen') {
+      this.penMode_ = true;
+    }
+
     if (this.activePointer_) {
       if (this.activePointer_.pointerType == 'touch' &&
           e.pointerType == 'touch') {
@@ -109,6 +134,20 @@
       return;
     }
 
+    if (!this.viewport.isPointInsidePage({x: e.clientX, y: e.clientY}) &&
+        (e.pointerType == 'touch' || e.pointerType == 'pen')) {
+      // If a touch or pen is outside the page, we allow pan gestures to start.
+      this.allowTouchStartTimeStamp_ = e.timeStamp;
+      return;
+    }
+
+    if (e.pointerType == 'touch' && this.penMode_) {
+      // If we see a touch after having seen a pen, we allow touches to start
+      // pan gestures anywhere and suppress all touches from drawing.
+      this.allowTouchStartTimeStamp_ = e.timeStamp;
+      return;
+    }
+
     this.activePointer_ = e;
     this.dispatchPointerEvent_(e);
   },
@@ -155,10 +194,9 @@
    *
    * @param {string} fileName The name of the PDF file.
    * @param {!ArrayBuffer} data The contents of the PDF document.
-   * @param {!Viewport} viewport
    * @return {!Promise} void value.
    */
-  load: async function(fileName, data, viewport) {
+  load: async function(fileName, data) {
     this.fileName_ = fileName;
     this.state_ = State.LOADING;
     this.$.frame.src = 'ink/index.html';
@@ -166,7 +204,7 @@
     this.ink_ = await this.$.frame.contentWindow.initInk();
     this.ink_.setPDF(data);
     this.state_ = State.ACTIVE;
-    this.viewportChanged(viewport);
+    this.viewportChanged();
     // TODO(dstockwell): we shouldn't need this extra flush.
     await this.ink_.flush();
     await this.ink_.flush();
@@ -176,10 +214,11 @@
     this.style.visibility = 'visible';
   },
 
-  viewportChanged: function(viewport) {
+  viewportChanged: function() {
     if (this.state_ != State.ACTIVE) {
       return;
     }
+    const viewport = this.viewport;
     const pos = viewport.position;
     const size = viewport.size;
     const zoom = viewport.zoom;
diff --git a/chrome/browser/resources/pdf/manifest.json b/chrome/browser/resources/pdf/manifest.json
index 1188c0a..6c3b296b 100644
--- a/chrome/browser/resources/pdf/manifest.json
+++ b/chrome/browser/resources/pdf/manifest.json
@@ -11,7 +11,8 @@
     "chrome://resources/",
     "contentSettings",
     "metricsPrivate",
-    "resourcesPrivate"
+    "resourcesPrivate",
+    {"fileSystem": ["write"]}
   ],
   "mime_types": [
     "application/pdf"
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index bc6662ca..c1aa135 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -1146,12 +1146,13 @@
       fileName = fileName + '.pdf';
     }
 
-    const a = document.createElement('a');
-    a.download = fileName;
-    const blob = new Blob([result.dataToSave], {type: 'application/pdf'});
-    a.href = URL.createObjectURL(blob);
-    a.click();
-    URL.revokeObjectURL(a.href);
+    chrome.fileSystem.chooseEntry(
+        {type: 'saveFile', suggestedName: fileName}, entry => {
+          entry.createWriter(writer => {
+            writer.write(
+                new Blob([result.dataToSave], {type: 'application/pdf'}));
+          });
+        });
 
     // Saving in Annotation mode is destructive: crbug.com/919364
     this.exitAnnotationMode_();
@@ -1258,7 +1259,7 @@
 
   /** @override */
   viewportChanged() {
-    this.inkHost_.viewportChanged(this.viewport_);
+    this.inkHost_.viewportChanged();
   }
 
   /** @override */
@@ -1271,8 +1272,9 @@
     if (!this.inkHost_) {
       this.inkHost_ = document.createElement('viewer-ink-host');
       $('content').appendChild(this.inkHost_);
+      this.inkHost_.viewport = this.viewport_;
     }
-    return this.inkHost_.load(filename, data, this.viewport_);
+    return this.inkHost_.load(filename, data);
   }
 
   /** @override */
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index f6159e2..dce8d53 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -610,6 +610,31 @@
   },
 
   /**
+   * @param {Point} point
+   * @return {boolean} Whether |point| (in screen coordinates) is inside a page
+   */
+  isPointInsidePage(point) {
+    const zoom = this.zoom;
+    const size = this.size;
+    const position = this.position;
+    const page = this.getPageAtY_((position.y + point.y) / zoom);
+    const pageWidth = this.pageDimensions_[page].width * zoom;
+    const documentWidth = this.getDocumentDimensions().width * zoom;
+
+    const outerWidth = Math.max(size.width, documentWidth);
+
+    if (pageWidth >= outerWidth) {
+      return true;
+    }
+
+    const x = point.x + position.x;
+
+    const minX = (outerWidth - pageWidth) / 2;
+    const maxX = outerWidth - minX;
+    return x >= minX && x <= maxX;
+  },
+
+  /**
    * Returns the page with the greatest proportion of its height in the current
    * viewport.
    *
diff --git a/chrome/browser/resources/settings/about_page/management_page.html b/chrome/browser/resources/settings/about_page/management_page.html
index 8fcc356..791421a 100644
--- a/chrome/browser/resources/settings/about_page/management_page.html
+++ b/chrome/browser/resources/settings/about_page/management_page.html
@@ -12,9 +12,53 @@
         margin-inline-start: var(--cr-controlled-by-spacing);
       }
 
+      h2 {
+        @apply --cr-title-text;
+        font-weight: 600;
+      }
+
       ul {
         list-style-type : none;
+        padding: 0;
+        text-align: start;
       }
+
+      #reportingInfoList div {
+        align-items: center;
+        display: flex;
+        margin-bottom: 2em;
+      }
+
+      #reportingInfoList div + div {
+        margin-top: 2em;
+      }
+
+      #reportingInfoList iron-icon {
+        margin-inline-end: 10px;
+        width: 24px;
+      }
+
+      .table-head {
+        font-weight: 500;
+      }
+
+      .extension-name {
+        width: 30%;
+      }
+
+      .extension-permissions {
+        width: 70%;
+      }
+
+      .content-indented {
+        margin-inline-start: 20px;
+      }
+
+      .subtitle {
+        margin-bottom: 1em;
+        margin-top: 1em;
+      }
+
     </style>
       <!--  This is where page content gets dynamically added. -->
 
@@ -23,45 +67,49 @@
 
       <div class="settings-box block single-column" id="policies" hidden>
         <h2>$i18n{managementDeviceReporting}</h2>
-        <div id="deviceConfiguration">
-          $i18n{managementDeviceConfiguration}
+        <div class="content-indented subtitle" id="deviceConfiguration">
+            $i18n{managementDeviceConfiguration}
         </div>
-        <ul id="reportingInfoList">
-          <li id="reportingDevice" hidden>
+        <div id="reportingInfoList">
+          <div class="content-indented" id="reportingDevice" hidden>
             <iron-icon icon="settings:computer"></iron-icon>
             <span id="reportingDeviceText"> </span>
-          </li>
-          <li id="reportingSecurity" hidden>
+          </div>
+          <div class="content-indented" id="reportingSecurity" hidden>
             <iron-icon icon="settings:security"></iron-icon>
             <span id="reportingSecurityText"> </span>
-          </li>
-          <li id="reportingUserActivity" hidden>
+          </div>
+          <div class="content-indented" id="reportingUserActivity" hidden>
             <iron-icon icon="settings:person"></iron-icon>
             <span id="reportingUserActivityText"> </span>
-          </li>
-          <li id="reportingWeb" hidden>
+          </div>
+          <div class="content-indented" id="reportingWeb" hidden>
             <iron-icon icon="settings:public"></iron-icon>
             <span id="reportingWebText"> </span>
-          </li>
-        </ul>
+          </div>
+        </div>
       </div>
 
       <div class="settings-box two-line single-column" id="extensions" hidden>
         <h2>$i18n{managementExtensionReporting}</h2>
-        <div id="extensionsInstalled">
+        <div class="content-indented subtitle" id="extensionsInstalled">
           $i18n{managementExtensionsInstalled}
         </div>
-        <table id="extensionsTable">
+        <table class="content-indented" id="extensionsTable">
           <tr>
-            <th>$i18n{managementExtensionName}</th>
-            <th>$i18n{managementExtensionPermissions}</th>
+              <td class="extension-name table-head">
+                $i18n{managementExtensionName}
+              </td>
+              <td class="extension-permissions table-head">
+                $i18n{managementExtensionPermissions}
+              </td>
           </tr>
         </table>
       </div>
 
       <div class="settings-box two-line single-column" id="trustRoots" hidden>
-          <h2 class="section-title">$i18n{managementLocalTrustRoots}</h2>
-          <div id="trustRootsConfiguration"></div>
+          <h2>$i18n{managementLocalTrustRoots}</h2>
+          <div class="content-indented" id="trustRootsConfiguration"></div>
       </div>
   </template>
   <script src="management_page.js"></script>
diff --git a/chrome/browser/resources/settings/about_page/management_page.js b/chrome/browser/resources/settings/about_page/management_page.js
index 1b9084c..a9f0d33 100644
--- a/chrome/browser/resources/settings/about_page/management_page.js
+++ b/chrome/browser/resources/settings/about_page/management_page.js
@@ -137,8 +137,10 @@
 
         const row = table.insertRow();
         const nameCell = row.insertCell();
+        nameCell.className = 'extension-name';
         // insertCell(-1) inserts at the last position.
         const permissionsCell = row.insertCell(-1);
+        permissionsCell.className = 'extension-permissions';
         nameCell.textContent = extension.name;
         permissionsCell.appendChild(permissionsList);
       }
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index 3c19eba..1fe90fc0 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -128,17 +128,15 @@
       }
     </style>
     <div id="banner" hidden="[[syncStatus.signedIn]]"></div>
-    <div class="settings-box first two-line" id="promo-header"
-        hidden="[[syncStatus.signedIn]]">
+    <div class$="settings-box first
+                 [[getPromoHeaderClass_(subLabel_)]]"
+        id="promo-header" hidden="[[syncStatus.signedIn]]">
       <div class="start settings-box-text">
         <div id="promo-title">
           [[getLabel_(promoLabelWithAccount,
               promoLabelWithNoAccount, shownAccount_)]]
         </div>
-        <div class="secondary">
-          [[getLabel_(promoSecondaryLabelWithAccount,
-              promoSecondaryLabelWithNoAccount, shownAccount_)]]
-        </div>
+        <div class="secondary">[[subLabel_]]</div>
       </div>
       <div id="promo-separator" class="separator"
           hidden="[[shouldShowAvatarRow_]]"></div>
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.js b/chrome/browser/resources/settings/people_page/sync_account_control.js
index 986b947..fb274b65 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.js
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.js
@@ -78,7 +78,14 @@
       computed: 'computeShouldShowAvatarRow_(storedAccounts_, syncStatus,' +
           'storedAccounts_.length, syncStatus.signedIn)',
       observer: 'onShouldShowAvatarRowChange_',
-    }
+    },
+
+    /** @private */
+    subLabel_: {
+      type: String,
+      computed: 'computeSubLabel_(promoSecondaryLabelWithAccount,' +
+          'promoSecondaryLabelWithNoAccount, shownAccount_)',
+    },
   },
 
   observers: [
@@ -161,6 +168,23 @@
   },
 
   /**
+   * @return {string}
+   * @private
+   */
+  computeSubLabel_: function() {
+    return this.getLabel_(this.promoSecondaryLabelWithAccount,
+                          this.promoSecondaryLabelWithNoAccount);
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getPromoHeaderClass_: function() {
+    return !!this.subLabel_ ? 'two-line': '';
+  },
+
+  /**
    * @param {string} label
    * @param {string} name
    * @return {string}
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index fe6f964..dee7306 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -102,17 +102,14 @@
         syncStatus.signinAllowed)]]">
       <settings-sync-account-control embedded-in-subpage
           sync-status="[[syncStatus]]"
-          promo-label-with-account="$i18n{peopleSignInPrompt}"
-          promo-label-with-no-account="$i18n{peopleSignInPrompt}"
-          promo-secondary-label-with-account=
+          promo-label-with-account=
               "$i18n{peopleSignInPromptSecondaryWithAccount}"
-          promo-secondary-label-with-no-account=
+          promo-label-with-no-account=
               "$i18n{peopleSignInPromptSecondaryWithNoAccount}">
       </settings-sync-account-control>
     </template>
 </if>
-
-    <div class="settings-box first" hidden="[[!syncStatus.managed]]">
+    <div class="settings-box first" hidden="[[!syncDisabledByAdmin_]]">
       <iron-icon id="disabled-by-admin-icon" icon="cr20:domain"></iron-icon>
       <div class="middle settings-box-text">
         $i18n{syncDisabledByAdministrator}
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index 54f400c..7a84d9c 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -118,6 +118,13 @@
     },
 
     /** @private */
+    syncDisabledByAdmin_: {
+      type: Boolean,
+      value: false,
+      computed: 'computeSyncDisabledByAdmin_(syncStatus.managed)',
+    },
+
+    /** @private */
     syncSectionDisabled_: {
       type: Boolean,
       value: false,
@@ -226,6 +233,14 @@
               settings.StatusAction.ENTER_PASSPHRASE));
   },
 
+  /**
+   * @return {boolean}
+   * @private
+   */
+  computeSyncDisabledByAdmin_: function() {
+    return this.syncStatus != undefined && !!this.syncStatus.managed;
+  },
+
   /** @protected */
   currentRouteChanged: function() {
     if (settings.getCurrentRoute() == settings.routes.SYNC) {
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index 69d8130..9a8766ca 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -42,7 +42,6 @@
     ppdManufacturer: '',
     ppdModel: '',
     printerAddress: '',
-    printerAutoconf: false,
     printerDescription: '',
     printerId: '',
     printerManufacturer: '',
@@ -50,6 +49,12 @@
     printerMakeAndModel: '',
     printerName: '',
     printerPPDPath: '',
+    printerPpdReference: {
+      userSuppliedPpdUrl: '',
+      effectiveMakeAndModel: '',
+      autoconf: false,
+    },
+    printerPpdReferenceResolved: false,
     printerProtocol: 'ipp',
     printerQueue: '',
     printerStatus: '',
@@ -374,14 +379,19 @@
    * @private
    * */
   onPrinterFound_: function(info) {
-    this.newPrinter.printerAutoconf = info.autoconf;
     this.newPrinter.printerManufacturer = info.manufacturer;
     this.newPrinter.printerModel = info.model;
     this.newPrinter.printerMakeAndModel = info.makeAndModel;
+    this.newPrinter.printerPpdReference.userSuppliedPpdUrl =
+        info.ppdRefUserSuppliedPpdUrl;
+    this.newPrinter.printerPpdReference.effectiveMakeAndModel =
+        info.ppdRefEffectiveMakeAndModel;
+    this.newPrinter.printerPpdReference.autoconf = info.autoconf;
+    this.newPrinter.printerPpdReferenceResolved = info.ppdReferenceResolved;
 
     // Add the printer if it's configurable. Otherwise, forward to the
     // manufacturer dialog.
-    if (this.newPrinter.printerAutoconf) {
+    if (this.newPrinter.printerPpdReferenceResolved) {
       this.addPrinter_();
     } else {
       this.switchToManufacturerDialog_();
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html
index 695ce6d..d90c4acf 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_elements.html
@@ -32,7 +32,7 @@
       #dialog {
         --cr-dialog-body-container: {
           /* Force a bottom border regardless of scroll state. */
-          border-bottom: 1px solid var(--paper-grey-300) !important;
+          border-bottom: 1px solid var(--paper-grey-300);
         };
       }
       #dialog [slot=body] {
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
index 8de6a74..8bd134c 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
@@ -12,7 +12,6 @@
  *   ppdManufacturer: string,
  *   ppdModel: string,
  *   printerAddress: string,
- *   printerAutoconf: boolean,
  *   printerDescription: string,
  *   printerId: string,
  *   printerManufacturer: string,
@@ -20,10 +19,20 @@
  *   printerMakeAndModel: string,
  *   printerName: string,
  *   printerPPDPath: string,
+ *   printerPpdReference: {
+ *     userSuppliedPpdUrl: string,
+ *     effectiveMakeAndModel: string,
+ *     autoconf: boolean,
+ *   },
+ *   printerPpdReferenceResolved: boolean,
  *   printerProtocol: string,
  *   printerQueue: string,
  *   printerStatus: string,
  * }}
+ *
+ * Note: |printerPPDPath| refers to a PPD retrieved from the user at the
+ * add-printer-manufacturer-model-dialog. |printerPpdReference| refers to either
+ * information retrieved from the printer or resolved via ppd_provider.
  */
 let CupsPrinterInfo;
 
@@ -55,7 +64,10 @@
  *   manufacturer: string,
  *   model: string,
  *   makeAndModel: string,
- *   autoconf: boolean
+ *   autoconf: boolean,
+ *   ppdRefUserSuppliedPpdUrl: string,
+ *   ppdRefEffectiveMakeAndModel: string,
+ *   ppdReferenceResolved: boolean
  * }}
  */
 let PrinterMakeModel;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index d4bd053..7b68ea3 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -96,6 +96,11 @@
             </p>
           </div>
         </template>
+        <cr-link-row label="$i18n{syncAndNonPersonalizedServices}"
+           sub-label="$i18n{syncAndGoogleServicesPrivacyDescription}"
+           icon-class="subpage-arrow" on-click="onSyncAndGoogleServicesClick_"
+           hidden="[[!unifiedConsentEnabled_]]">
+        </cr-link-row>
 <if expr="not chromeos">
         <settings-toggle-button id="signinAllowedToggle"
             pref="{{prefs.signin.allowed_on_next_startup}}"
@@ -182,10 +187,6 @@
                 aria-describedby="clearBrowsingDataSecondary"></button>
           </paper-icon-button-light>
         </div>
-        <div class="settings-box" on-click="onMoreSettingsBoxClicked_"
-            hidden="[[!unifiedConsentEnabled_]]">
-          <div class="start">$i18nRaw{syncAndPersonalizationLink}</div>
-        </div>
       </div>
 <if expr="use_nss_certs">
       <template is="dom-if" route-path="/certificates">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js
index b153a7a..62a2ad7 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -308,17 +308,11 @@
     // </if>
   },
 
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onMoreSettingsBoxClicked_: function(e) {
-    if (e.target.tagName === 'A') {
-      e.preventDefault();
-      // Navigate to sync page, and remove (privacy related) search text to
-      // avoid the sync page from being hidden.
-      settings.navigateTo(settings.routes.SYNC, null, true);
-    }
+  /** @private */
+  onSyncAndGoogleServicesClick_: function() {
+    // Navigate to sync page, and remove (privacy related) search text to
+    // avoid the sync page from being hidden.
+    settings.navigateTo(settings.routes.SYNC, null, true);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html
index 6c363ae0..9983196 100644
--- a/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -49,7 +49,7 @@
     /* Spacing between policy (controlledBy) indicator and control. */
     --settings-controlled-by-spacing: var(--cr-controlled-by-spacing);
 
-    --settings-input-max-width: 264px;
+    --settings-input-max-width: var(--cr-default-input-max-width);
 
     --iron-icon-fill-color: var(--google-grey-refresh-700);
     --iron-icon-height: var(--cr-icon-size);
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index 90d9457..685ad1a 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -134,9 +134,7 @@
       pairs.push([R.SITE_SETTINGS_PAYMENT_HANDLER, 'paymentHandler']);
     }
 
-    pairs.forEach(pair => {
-      const route = pair[0];
-      const id = pair[1];
+    pairs.forEach(([route, id]) => {
       this.focusConfig.set(route.path, () => this.async(() => {
         cr.ui.focusWithoutInk(assert(this.$$(`#${id}`)));
       }));
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
index e1d119e..72d3853 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
@@ -260,7 +260,7 @@
       this.appList_.forEach(app => this.updateBookmark_(app));
     } else {
       this.appProxy.getAppList().then(list => {
-        this.appList_ = list;
+        this.appList_ = /** @type(!Array<!nux.AppItem>) */ (list);
         this.appList_.forEach((app, index) => {
           if (this.singleSelect) {
             // Default select the first item.
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 7c9f6f9..055004c9 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -75,6 +75,7 @@
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "url/gurl.h"
 
@@ -568,11 +569,9 @@
     config_data.SetBoolean("isAccessibleBrowser",
                            content::BrowserAccessibilityState::GetInstance()
                                ->IsAccessibleBrowser());
-
-    bool is_dark_mode = base::FeatureList::IsEnabled(features::kDarkMode) ||
-                        base::CommandLine::ForCurrentProcess()->HasSwitch(
-                            switches::kForceDarkMode);
-    config_data.SetBoolean("isDarkModeEnabled", is_dark_mode);
+    config_data.SetBoolean(
+        "isDarkModeEnabled",
+        ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled());
 
     // Serialize the dictionary.
     std::string js_text;
diff --git a/chrome/browser/search/most_visited_iframe_source.cc b/chrome/browser/search/most_visited_iframe_source.cc
index 919a1db2..e5d47db 100644
--- a/chrome/browser/search/most_visited_iframe_source.cc
+++ b/chrome/browser/search/most_visited_iframe_source.cc
@@ -33,6 +33,7 @@
 const char kEditCSSPath[] = "/edit.css";
 const char kEditJSPath[] = "/edit.js";
 const char kAddSvgPath[] = "/add_link.svg";
+const char kAddWhiteSvgPath[] = "/add_link_white.svg";
 const char kEditMenuSvgPath[] = "/edit_menu.svg";
 
 // Used in the single-iframe version and the edit custom links dialog iframe.
@@ -99,6 +100,8 @@
     SendJSWithOrigin(IDR_CUSTOM_LINKS_EDIT_JS, wc_getter, callback);
   } else if (path == kAddSvgPath) {
     SendResource(IDR_CUSTOM_LINKS_ADD_SVG, callback);
+  } else if (path == kAddWhiteSvgPath) {
+    SendResource(IDR_CUSTOM_LINKS_ADD_WHITE_SVG, callback);
   } else if (path == kEditMenuSvgPath) {
     SendResource(IDR_CUSTOM_LINKS_EDIT_MENU_SVG, callback);
   } else if (path == kAnimationsCSSPath) {
@@ -118,6 +121,7 @@
          path == kTitleCSSPath || path == kTitleJSPath || path == kUtilJSPath ||
          path == kCommonCSSPath || path == kEditHTMLPath ||
          path == kEditCSSPath || path == kEditJSPath || path == kAddSvgPath ||
-         path == kEditMenuSvgPath || path == kAnimationsCSSPath ||
-         path == kAnimationsJSPath || path == kLocalNTPUtilsJSPath;
+         path == kAddWhiteSvgPath || path == kEditMenuSvgPath ||
+         path == kAnimationsCSSPath || path == kAnimationsJSPath ||
+         path == kLocalNTPUtilsJSPath;
 }
diff --git a/chrome/browser/search/ntp_icon_source.cc b/chrome/browser/search/ntp_icon_source.cc
index 1b5e246..2f0817f 100644
--- a/chrome/browser/search/ntp_icon_source.cc
+++ b/chrome/browser/search/ntp_icon_source.cc
@@ -48,6 +48,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/native_theme/native_theme.h"
 #include "url/gurl.h"
 
 namespace {
@@ -211,9 +212,12 @@
   canvas.DrawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc);
 
   // Draw the gray background.
-  constexpr SkColor kFaviconBackground = gfx::kGoogleGrey100;
+  SkColor favicon_bg =
+      ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()
+          ? gfx::kGoogleGrey900
+          : gfx::kGoogleGrey100;
   DrawCircleInCanvas(&canvas, icon_size, /*offset=*/0,
-                     /*background_color=*/kFaviconBackground);
+                     /*background_color=*/favicon_bg);
   DrawFavicon(favicon, &canvas, icon_size);
 
   // If necessary, draw the colored fallback monogram.
diff --git a/chrome/browser/search/search_suggest/search_suggest_service_unittest.cc b/chrome/browser/search/search_suggest/search_suggest_service_unittest.cc
index 2599500..923e828 100644
--- a/chrome/browser/search/search_suggest/search_suggest_service_unittest.cc
+++ b/chrome/browser/search/search_suggest/search_suggest_service_unittest.cc
@@ -8,16 +8,11 @@
 #include <utility>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/optional.h"
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/search/search_suggest/search_suggest_data.h"
 #include "chrome/browser/search/search_suggest/search_suggest_loader.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/test_signin_client.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
-#include "google_apis/gaia/gaia_constants.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -51,9 +46,7 @@
 
 class SearchSuggestServiceTest : public testing::Test {
  public:
-  SearchSuggestServiceTest()
-      : signin_client_(&pref_service_),
-        identity_env_(&test_url_loader_factory_) {
+  SearchSuggestServiceTest() : identity_env_(&test_url_loader_factory_) {
     // GaiaCookieManagerService calls static methods of AccountTrackerService
     // which access prefs.
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
@@ -87,8 +80,6 @@
 
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  TestSigninClient signin_client_;
-  FakeOAuth2TokenService token_service_;
   identity::IdentityTestEnvironment identity_env_;
 
   // Owned by the service.
diff --git a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
index d2ab6d9..3a3e5fa7e 100644
--- a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
+++ b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
@@ -10,6 +10,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "services/network/test/test_url_loader_factory.h"
 
 std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerService(
     content::BrowserContext* context) {
@@ -17,6 +18,16 @@
       /*create_fake_url_loader_factory_for_cookie_requests=*/false, context);
 }
 
+std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithURLLoader(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context) {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return std::make_unique<FakeGaiaCookieManagerService>(
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+      ChromeSigninClientFactory::GetForProfile(profile),
+      test_url_loader_factory);
+}
+
 std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithOptions(
     bool create_fake_url_loader_factory_for_cookie_requests,
     content::BrowserContext* context) {
diff --git a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
index 89085cbf..7644b0b 100644
--- a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
+++ b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
@@ -13,10 +13,21 @@
 class BrowserContext;
 }
 
+namespace network {
+class TestURLLoaderFactory;
+}
+
 // Helper functions to be used with KeyedService::SetTestingFactory().
 std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerService(
     content::BrowserContext* context);
 
+// Builds a FakeGaiaCookieManagerService which uses the provided
+// |test_url_loader_factory| for cookie-related requests.
+std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithURLLoader(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context);
+
+// TODO(https://crbug.com/907782): Remove all references and delete this method.
 std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithOptions(
     bool create_fake_url_loader_factory_for_cookie_requests,
     content::BrowserContext* context);
diff --git a/chrome/browser/signin/signin_error_notifier_ash.cc b/chrome/browser/signin/signin_error_notifier_ash.cc
index a04d1f8..f0d44488 100644
--- a/chrome/browser/signin/signin_error_notifier_ash.cc
+++ b/chrome/browser/signin/signin_error_notifier_ash.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/signin/signin_error_notifier_ash.h"
 
+#include <memory>
+
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "base/logging.h"
@@ -17,16 +19,20 @@
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user_manager.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -34,9 +40,11 @@
 
 namespace {
 
-const char kProfileSigninNotificationId[] = "chrome://settings/signin/";
+constexpr char kProfileSigninNotificationId[] = "chrome://settings/signin/";
+constexpr char kSecondaryAccountNotificationIdSuffix[] = "secondary-account";
 
-void HandleNotificationClick(base::Optional<int> button_index) {
+void HandleDeviceAccountReauthNotificationClick(
+    base::Optional<int> button_index) {
   chrome::AttemptUserExit();
 }
 
@@ -45,10 +53,15 @@
 SigninErrorNotifier::SigninErrorNotifier(SigninErrorController* controller,
                                          Profile* profile)
     : error_controller_(controller),
-      profile_(profile) {
+      profile_(profile),
+      identity_manager_(IdentityManagerFactory::GetForProfile(profile_)),
+      weak_factory_(this) {
   // Create a unique notification ID for this profile.
-  notification_id_ =
+  device_account_notification_id_ =
       kProfileSigninNotificationId + profile->GetProfileUserName();
+  secondary_account_notification_id_ =
+      std::string(kProfileSigninNotificationId) +
+      kSecondaryAccountNotificationIdSuffix;
 
   error_controller_->AddObserver(this);
   OnErrorChanged();
@@ -67,7 +80,10 @@
 void SigninErrorNotifier::OnErrorChanged() {
   if (!error_controller_->HasError()) {
     NotificationDisplayService::GetForProfile(profile_)->Close(
-        NotificationHandler::Type::TRANSIENT, notification_id_);
+        NotificationHandler::Type::TRANSIENT, device_account_notification_id_);
+    NotificationDisplayService::GetForProfile(profile_)->Close(
+        NotificationHandler::Type::TRANSIENT,
+        secondary_account_notification_id_);
     return;
   }
 
@@ -82,6 +98,23 @@
       return;
   }
 
+  if (!chromeos::switches::IsAccountManagerEnabled()) {
+    // If this flag is disabled, Chrome OS does not have a concept of Secondary
+    // Accounts. Preserve existing behavior.
+    HandleDeviceAccountError();
+    return;
+  }
+
+  const std::string error_account_id = error_controller_->error_account_id();
+  if (error_account_id ==
+      identity_manager_->GetPrimaryAccountInfo().account_id) {
+    HandleDeviceAccountError();
+  } else {
+    HandleSecondaryAccountError(error_account_id);
+  }
+}
+
+void SigninErrorNotifier::HandleDeviceAccountError() {
   // Add an accept button to sign the user out.
   message_center::RichNotificationData data;
   data.buttons.push_back(message_center::ButtonInfo(
@@ -97,13 +130,14 @@
 
   std::unique_ptr<message_center::Notification> notification =
       ash::CreateSystemNotification(
-          message_center::NOTIFICATION_TYPE_SIMPLE, notification_id_,
+          message_center::NOTIFICATION_TYPE_SIMPLE,
+          device_account_notification_id_,
           l10n_util::GetStringUTF16(IDS_SIGNIN_ERROR_BUBBLE_VIEW_TITLE),
-          GetMessageBody(),
+          GetMessageBody(false /* is_secondary_account_error */),
           l10n_util::GetStringUTF16(IDS_SIGNIN_ERROR_DISPLAY_SOURCE),
-          GURL(notification_id_), notifier_id, data,
+          GURL(device_account_notification_id_), notifier_id, data,
           new message_center::HandleNotificationClickDelegate(
-              base::Bind(&HandleNotificationClick)),
+              base::BindRepeating(&HandleDeviceAccountReauthNotificationClick)),
           ash::kNotificationWarningIcon,
           message_center::SystemNotificationWarningLevel::WARNING);
   notification->SetSystemPriority();
@@ -113,7 +147,55 @@
       NotificationHandler::Type::TRANSIENT, *notification);
 }
 
-base::string16 SigninErrorNotifier::GetMessageBody() const {
+void SigninErrorNotifier::HandleSecondaryAccountError(
+    const std::string& account_id) {
+  message_center::NotifierId notifier_id(
+      message_center::NotifierType::SYSTEM_COMPONENT,
+      kProfileSigninNotificationId);
+  // Set |profile_id| for multi-user notification blocker. Note the primary user
+  // account id is used to identify the profile for the blocker so it is used
+  // instead of the secondary user account id.
+  notifier_id.profile_id =
+      multi_user_util::GetAccountIdFromProfile(profile_).GetUserEmail();
+
+  std::unique_ptr<message_center::Notification> notification =
+      ash::CreateSystemNotification(
+          message_center::NOTIFICATION_TYPE_SIMPLE,
+          secondary_account_notification_id_,
+          l10n_util::GetStringUTF16(
+              IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_TITLE),
+          GetMessageBody(true /* is_secondary_account_error */),
+          l10n_util::GetStringUTF16(
+              IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_DISPLAY_SOURCE),
+          GURL(secondary_account_notification_id_), notifier_id,
+          message_center::RichNotificationData(),
+          new message_center::HandleNotificationClickDelegate(
+              base::BindRepeating(
+                  &SigninErrorNotifier::
+                      HandleSecondaryAccountReauthNotificationClick,
+                  weak_factory_.GetWeakPtr())),
+          ash::kNotificationSettingsIcon,
+          message_center::SystemNotificationWarningLevel::NORMAL);
+  notification->SetSystemPriority();
+
+  // Update or add the notification.
+  NotificationDisplayService::GetForProfile(profile_)->Display(
+      NotificationHandler::Type::TRANSIENT, *notification);
+}
+
+void SigninErrorNotifier::HandleSecondaryAccountReauthNotificationClick(
+    base::Optional<int> button_index) {
+  chrome::SettingsWindowManager::GetInstance()->ShowChromePageForProfile(
+      profile_, GURL("chrome://settings/accountManager"));
+}
+
+base::string16 SigninErrorNotifier::GetMessageBody(
+    bool is_secondary_account_error) const {
+  if (is_secondary_account_error) {
+    return l10n_util::GetStringUTF16(
+        IDS_SIGNIN_ERROR_SECONDARY_ACCOUNT_BUBBLE_VIEW_MESSAGE);
+  }
+
   switch (error_controller_->auth_error().state()) {
     // TODO(rogerta): use account id in error messages.
 
diff --git a/chrome/browser/signin/signin_error_notifier_ash.h b/chrome/browser/signin/signin_error_notifier_ash.h
index 6421613e..7764731 100644
--- a/chrome/browser/signin/signin_error_notifier_ash.h
+++ b/chrome/browser/signin/signin_error_notifier_ash.h
@@ -9,12 +9,17 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/signin_error_controller.h"
 
 class Profile;
 
+namespace identity {
+class IdentityManager;
+}  // namespace identity.
+
 // Shows signin-related errors as notifications in Ash.
 class SigninErrorNotifier : public SigninErrorController::Observer,
                             public KeyedService {
@@ -29,17 +34,37 @@
   void OnErrorChanged() override;
 
  private:
-  base::string16 GetMessageBody() const;
+  // Handles errors for the Device Account.
+  // Displays a notification asking the user to Sign Out.
+  void HandleDeviceAccountError();
+
+  // Handles errors for Secondary Accounts.
+  // Displays a notification that allows users to open crOS Account Manager UI.
+  // |account_id| is the account identifier (used by the Token Service chain)
+  // for the Secondary Account which received an error.
+  void HandleSecondaryAccountError(const std::string& account_id);
+
+  // Handles clicks on the Secondary Account reauth notification. See
+  // |message_center::HandleNotificationClickDelegate|.
+  void HandleSecondaryAccountReauthNotificationClick(
+      base::Optional<int> button_index);
+
+  base::string16 GetMessageBody(bool is_secondary_account_error) const;
 
   // The error controller to query for error details.
   SigninErrorController* error_controller_;
 
   // The Profile this service belongs to.
-  Profile* profile_;
+  Profile* const profile_;
 
-  // Used to keep track of the message center notification.
-  std::string notification_id_;
+  // A non-owning pointer to IdentityManager.
+  identity::IdentityManager* const identity_manager_;
 
+  // Used to keep track of the message center notifications.
+  std::string device_account_notification_id_;
+  std::string secondary_account_notification_id_;
+
+  base::WeakPtrFactory<SigninErrorNotifier> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(SigninErrorNotifier);
 };
 
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
index 03f519e8..a866ab9 100644
--- a/chrome/browser/signin/signin_promo.cc
+++ b/chrome/browser/signin/signin_promo.cc
@@ -42,6 +42,8 @@
 
 using content::WebContents;
 
+namespace signin {
+
 namespace {
 
 // The maximum number of times we want to show the sign in promo at startup.
@@ -75,40 +77,10 @@
   return profile->GetPrefs()->GetBoolean(prefs::kSignInPromoUserSkipped);
 }
 
-// Returns the sign in promo URL with the given arguments in the query.
-// |access_point| indicates where the sign in is being initiated.
-// |reason| indicates the purpose of using this URL.
-// |auto_close| whether to close the sign in promo automatically when done.
-GURL GetPromoURL(signin_metrics::AccessPoint access_point,
-                 signin_metrics::Reason reason,
-                 bool auto_close) {
-  CHECK_LT(static_cast<int>(access_point),
-           static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_MAX));
-  CHECK_NE(static_cast<int>(access_point),
-           static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN));
-  CHECK_LT(static_cast<int>(reason),
-           static_cast<int>(signin_metrics::Reason::REASON_MAX));
-  CHECK_NE(static_cast<int>(reason),
-           static_cast<int>(signin_metrics::Reason::REASON_UNKNOWN_REASON));
-
-  GURL url(chrome::kChromeUIChromeSigninURL);
-  url = net::AppendQueryParameter(
-      url, signin::kSignInPromoQueryKeyAccessPoint,
-      base::IntToString(static_cast<int>(access_point)));
-  url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyReason,
-                                  base::IntToString(static_cast<int>(reason)));
-  if (auto_close) {
-    url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyAutoClose,
-                                    "1");
-  }
-  return url;
-}
-
-GURL GetReauthURL(signin_metrics::AccessPoint access_point,
-                  signin_metrics::Reason reason,
-                  const std::string& email,
-                  bool auto_close) {
-  GURL url = GetPromoURL(access_point, reason, auto_close);
+GURL GetEmbeddedReauthURLInternal(signin_metrics::AccessPoint access_point,
+                                  signin_metrics::Reason reason,
+                                  const std::string& email) {
+  GURL url = GetEmbeddedPromoURL(access_point, reason, /*auto_close=*/true);
   url = net::AppendQueryParameter(url, "email", email);
   url = net::AppendQueryParameter(url, "validateEmail", "1");
   return net::AppendQueryParameter(url, "readOnlyEmail", "1");
@@ -116,8 +88,6 @@
 
 }  // namespace
 
-namespace signin {
-
 const char kSignInPromoQueryKeyAccessPoint[] = "access_point";
 const char kSignInPromoQueryKeyAutoClose[] = "auto_close";
 const char kSignInPromoQueryKeyContinue[] = "continue";
@@ -213,41 +183,87 @@
   return GURL(url);
 }
 
-GURL GetPromoURLForTab(signin_metrics::AccessPoint access_point,
-                       signin_metrics::Reason reason,
-                       bool auto_close) {
-  return GetPromoURL(access_point, reason, auto_close);
+#if defined(OS_CHROMEOS)
+GURL GetEmbeddedPromoURLForTab(signin_metrics::AccessPoint access_point,
+                               signin_metrics::Reason reason,
+                               bool auto_close) {
+  return GetEmbeddedPromoURL(access_point, reason, auto_close);
 }
 
-GURL GetPromoURLForDialog(signin_metrics::AccessPoint access_point,
+GURL GetEmbeddedSigninURLFromBubbleViewMode(
+    Profile* profile,
+    profiles::BubbleViewMode mode,
+    signin_metrics::AccessPoint access_point) {
+  switch (mode) {
+    case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
+      return GetEmbeddedPromoURL(
+          access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
+          false /* auto_close */);
+      break;
+    case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
+      return GetEmbeddedPromoURL(
+          access_point, signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT,
+          false /* auto_close */);
+      break;
+    case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: {
+      const SigninErrorController* error_controller =
+          SigninErrorControllerFactory::GetForProfile(profile);
+      CHECK(error_controller);
+      DCHECK(error_controller->HasError());
+      AccountInfo info =
+          AccountTrackerServiceFactory::GetForProfile(profile)->GetAccountInfo(
+              error_controller->error_account_id());
+      return GetEmbeddedReauthURLInternal(
+          access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
+          info.email);
+      break;
+    }
+    default:
+      NOTREACHED() << "Called with invalid mode=" << mode;
+      return GURL();
+  }
+}
+#endif
+
+GURL GetEmbeddedPromoURL(signin_metrics::AccessPoint access_point,
+                         signin_metrics::Reason reason,
+                         bool auto_close) {
+  CHECK_LT(static_cast<int>(access_point),
+           static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_MAX));
+  CHECK_NE(static_cast<int>(access_point),
+           static_cast<int>(signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN));
+  CHECK_LT(static_cast<int>(reason),
+           static_cast<int>(signin_metrics::Reason::REASON_MAX));
+  CHECK_NE(static_cast<int>(reason),
+           static_cast<int>(signin_metrics::Reason::REASON_UNKNOWN_REASON));
+
+  GURL url(chrome::kChromeUIChromeSigninURL);
+  url = net::AppendQueryParameter(
+      url, signin::kSignInPromoQueryKeyAccessPoint,
+      base::IntToString(static_cast<int>(access_point)));
+  url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyReason,
+                                  base::IntToString(static_cast<int>(reason)));
+  if (auto_close) {
+    url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyAutoClose,
+                                    "1");
+  }
+  return url;
+}
+
+GURL GetEmbeddedReauthURL(signin_metrics::AccessPoint access_point,
                           signin_metrics::Reason reason,
-                          bool auto_close) {
-  return GetPromoURL(access_point, reason, auto_close);
-}
-
-GURL GetReauthURLForDialog(signin_metrics::AccessPoint access_point,
-                           signin_metrics::Reason reason,
-                           Profile* profile,
-                           const std::string& account_id) {
-  AccountInfo info = AccountTrackerServiceFactory::GetForProfile(profile)
-                         ->GetAccountInfo(account_id);
-  return GetReauthURL(access_point, reason, info.email, true /* auto_close */);
-}
-
-GURL GetReauthURLForTab(signin_metrics::AccessPoint access_point,
-                        signin_metrics::Reason reason,
-                        Profile* profile,
-                        const std::string& account_id) {
+                          Profile* profile,
+                          const std::string& account_id) {
   AccountInfo info =
       AccountTrackerServiceFactory::GetForProfile(profile)->GetAccountInfo(
           account_id);
-  return GetReauthURL(access_point, reason, info.email, true /* auto_close */);
+  return GetEmbeddedReauthURLInternal(access_point, reason, info.email);
 }
 
-GURL GetReauthURLWithEmailForDialog(signin_metrics::AccessPoint access_point,
-                                    signin_metrics::Reason reason,
-                                    const std::string& email) {
-  return GetReauthURL(access_point, reason, email, true /* auto_close */);
+GURL GetEmbeddedReauthURLWithEmail(signin_metrics::AccessPoint access_point,
+                                   signin_metrics::Reason reason,
+                                   const std::string& email) {
+  return GetEmbeddedReauthURLInternal(access_point, reason, email);
 }
 
 GURL GetSigninURLForDice(Profile* profile, const std::string& email) {
@@ -267,37 +283,7 @@
   return GURL("chrome-guest://chrome-signin/?");
 }
 
-GURL GetSigninURLFromBubbleViewMode(Profile* profile,
-                                    profiles::BubbleViewMode mode,
-                                    signin_metrics::AccessPoint access_point) {
-  switch (mode) {
-    case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
-      return GetPromoURLForDialog(
-          access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-          false /* auto_close */);
-      break;
-    case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
-      return GetPromoURLForDialog(
-          access_point, signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT,
-          false /* auto_close */);
-      break;
-    case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: {
-      const SigninErrorController* error_controller =
-          SigninErrorControllerFactory::GetForProfile(profile);
-      CHECK(error_controller);
-      DCHECK(error_controller->HasError());
-      return GetReauthURLForDialog(
-          access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
-          profile, error_controller->error_account_id());
-      break;
-    }
-    default:
-      NOTREACHED() << "Called with invalid mode=" << mode;
-      return GURL();
-  }
-}
-
-signin_metrics::AccessPoint GetAccessPointForPromoURL(const GURL& url) {
+signin_metrics::AccessPoint GetAccessPointForEmbeddedPromoURL(const GURL& url) {
   std::string value;
   if (!net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyAccessPoint,
                                   &value)) {
@@ -317,7 +303,7 @@
   return static_cast<signin_metrics::AccessPoint>(access_point);
 }
 
-signin_metrics::Reason GetSigninReasonForPromoURL(const GURL& url) {
+signin_metrics::Reason GetSigninReasonForEmbeddedPromoURL(const GURL& url) {
   std::string value;
   if (!net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyReason, &value))
     return signin_metrics::Reason::REASON_UNKNOWN_REASON;
@@ -333,7 +319,7 @@
   return static_cast<signin_metrics::Reason>(reason);
 }
 
-bool IsAutoCloseEnabledInURL(const GURL& url) {
+bool IsAutoCloseEnabledInEmbeddedURL(const GURL& url) {
   std::string value;
   if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyAutoClose, &value)) {
     int enabled = 0;
diff --git a/chrome/browser/signin/signin_promo.h b/chrome/browser/signin/signin_promo.h
index f1f5a78..bfab448 100644
--- a/chrome/browser/signin/signin_promo.h
+++ b/chrome/browser/signin/signin_promo.h
@@ -41,43 +41,40 @@
 // Gets the sign in landing page URL.
 GURL GetLandingURL(signin_metrics::AccessPoint access_point);
 
+#if defined(OS_CHROMEOS)
 // Returns the sign in promo URL that can be used in a full browser tab with
 // the given arguments in the query.
 // |access_point| indicates where the sign in is being initiated.
 // |reason| indicates the purpose of using this URL.
 // |auto_close| whether to close the sign in promo automatically when done.
-GURL GetPromoURLForTab(signin_metrics::AccessPoint access_point,
-                       signin_metrics::Reason reason,
-                       bool auto_close);
+// DEPRECATED: the embedded signin flow is not intended to be used in a full
+// tab.
+GURL GetEmbeddedPromoURLForTab(signin_metrics::AccessPoint access_point,
+                               signin_metrics::Reason reason,
+                               bool auto_close);
+
+// Gets the signin URL to be used to display the sign in flow for |mode| in
+// |profile|.
+GURL GetEmbeddedSigninURLFromBubbleViewMode(
+    Profile* profile,
+    profiles::BubbleViewMode mode,
+    signin_metrics::AccessPoint access_point);
+#endif
 
 // Returns the sign in promo URL that can be used in a modal dialog with
 // the given arguments in the query.
 // |access_point| indicates where the sign in is being initiated.
 // |reason| indicates the purpose of using this URL.
 // |auto_close| whether to close the sign in promo automatically when done.
-GURL GetPromoURLForDialog(signin_metrics::AccessPoint access_point,
-                          signin_metrics::Reason reason,
-                          bool auto_close);
-
-// Returns a sign in promo URL specifically for reauthenticating |account_id|
-// that can be used in a full browser tab.
-GURL GetReauthURLForTab(signin_metrics::AccessPoint access_point,
-                        signin_metrics::Reason reason,
-                        Profile* profile,
-                        const std::string& account_id);
-
-// Returns a sign in promo URL specifically for reauthenticating |account_id|
-// that can be used in a modal dialog.
-GURL GetReauthURLForDialog(signin_metrics::AccessPoint access_point,
-                           signin_metrics::Reason reason,
-                           Profile* profile,
-                           const std::string& account_id);
+GURL GetEmbeddedPromoURL(signin_metrics::AccessPoint access_point,
+                         signin_metrics::Reason reason,
+                         bool auto_close);
 
 // Returns a sign in promo URL specifically for reauthenticating |email| that
 // can be used in a modal dialog.
-GURL GetReauthURLWithEmailForDialog(signin_metrics::AccessPoint access_point,
-                                    signin_metrics::Reason reason,
-                                    const std::string& email);
+GURL GetEmbeddedReauthURLWithEmail(signin_metrics::AccessPoint access_point,
+                                   signin_metrics::Reason reason,
+                                   const std::string& email);
 
 // Returns the URL to be used to add an account when DICE is enabled.
 // If email is not empty, then it will pass email as hint to the page so that it
@@ -87,20 +84,14 @@
 // Gets the partition URL for the embedded sign in frame/webview.
 GURL GetSigninPartitionURL();
 
-// Gets the signin URL to be used to display the sign in flow for |mode| in
-// |profile|.
-GURL GetSigninURLFromBubbleViewMode(Profile* profile,
-                                    profiles::BubbleViewMode mode,
-                                    signin_metrics::AccessPoint access_point);
-
 // Gets the access point from the query portion of the sign in promo URL.
-signin_metrics::AccessPoint GetAccessPointForPromoURL(const GURL& url);
+signin_metrics::AccessPoint GetAccessPointForEmbeddedPromoURL(const GURL& url);
 
 // Gets the sign in reason from the query portion of the sign in promo URL.
-signin_metrics::Reason GetSigninReasonForPromoURL(const GURL& url);
+signin_metrics::Reason GetSigninReasonForEmbeddedPromoURL(const GURL& url);
 
 // Returns true if the auto_close parameter in the given URL is set to true.
-bool IsAutoCloseEnabledInURL(const GURL& url);
+bool IsAutoCloseEnabledInEmbeddedURL(const GURL& url);
 
 // Forces UseWebBasedSigninFlow() to return true when set; used in tests only.
 void ForceWebBasedSigninFlowForTesting(bool force);
diff --git a/chrome/browser/signin/signin_promo_unittest.cc b/chrome/browser/signin/signin_promo_unittest.cc
index abb794f..89804bd28 100644
--- a/chrome/browser/signin/signin_promo_unittest.cc
+++ b/chrome/browser/signin/signin_promo_unittest.cc
@@ -9,35 +9,32 @@
 
 namespace signin {
 
-class SigninPromoTest : public ::testing::Test {};
-
-TEST_F(SigninPromoTest, TestPromoURL) {
+TEST(SigninPromoTest, TestForceSigninURL) {
   GURL expected_url_1(
       "chrome://chrome-signin/?access_point=0&reason=0&auto_close=1");
   EXPECT_EQ(expected_url_1,
-            GetPromoURLForDialog(
+            GetEmbeddedPromoURL(
                 signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
                 signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, true));
   GURL expected_url_2("chrome://chrome-signin/?access_point=15&reason=3");
   EXPECT_EQ(expected_url_2,
-            GetPromoURLForDialog(
+            GetEmbeddedPromoURL(
                 signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO,
                 signin_metrics::Reason::REASON_UNLOCK, false));
 }
 
-TEST_F(SigninPromoTest, TestReauthURL) {
+TEST(SigninPromoTest, TestReauthURL) {
   GURL expected_url_1(
       "chrome://chrome-signin/"
-      "?access_point=0&reason=0&auto_close=1&email=example%40domain.com"
+      "?access_point=0&reason=3&auto_close=1&email=example%40domain.com"
       "&validateEmail=1&readOnlyEmail=1");
   EXPECT_EQ(expected_url_1,
-            GetReauthURLWithEmailForDialog(
+            GetEmbeddedReauthURLWithEmail(
                 signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
-                signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-                "example@domain.com"));
+                signin_metrics::Reason::REASON_UNLOCK, "example@domain.com"));
 }
 
-TEST_F(SigninPromoTest, TestLandingURL) {
+TEST(SigninPromoTest, TestLandingURL) {
   GURL expected_url_1(
       "chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/"
       "success.html?access_point=1&source=13");
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 2953478..6309174 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/signin/signin_global_error.h"
@@ -30,7 +29,6 @@
 #include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/identity_utils.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/user_manager/user_manager.h"
@@ -178,8 +176,8 @@
           account.account_id);
   if (needs_reauth_before_enable_sync) {
     browser->signin_view_controller()->ShowDiceSigninTab(
-        profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser, access_point,
-        promo_action, account.email);
+        browser, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
+        access_point, promo_action, account.email);
     return;
   }
 
@@ -224,15 +222,16 @@
     default_account_id = identity_manager->GetPrimaryAccountId();
   } else {
     // Fetch accounts in the Gaia cookies.
-    GaiaCookieManagerService* cookie_manager_service =
-        GaiaCookieManagerServiceFactory::GetForProfile(profile);
-    std::vector<gaia::ListedAccount> cookie_accounts;
-    bool cookie_accounts_valid =
-        cookie_manager_service->ListAccounts(&cookie_accounts, nullptr);
+    auto accounts_in_cookie_jar_info =
+        identity_manager->GetAccountsInCookieJar();
+    std::vector<AccountInfo> signed_in_accounts =
+        accounts_in_cookie_jar_info.signed_in_accounts;
     UMA_HISTOGRAM_BOOLEAN("Profile.DiceUI.GaiaAccountsStale",
-                          !cookie_accounts_valid);
-    if (cookie_accounts_valid && !cookie_accounts.empty())
-      default_account_id = cookie_accounts[0].id;
+                          !accounts_in_cookie_jar_info.accounts_are_fresh);
+
+    if (accounts_in_cookie_jar_info.accounts_are_fresh &&
+        !signed_in_accounts.empty())
+      default_account_id = signed_in_accounts[0].account_id;
   }
 
   // Fetch account information for each id and make sure that the first account
diff --git a/chrome/browser/status_icons/status_icon_menu_model.cc b/chrome/browser/status_icons/status_icon_menu_model.cc
index 2d46f71..6deae7c 100644
--- a/chrome/browser/status_icons/status_icon_menu_model.cc
+++ b/chrome/browser/status_icons/status_icon_menu_model.cc
@@ -25,12 +25,6 @@
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-// StatusIconMenuModel::Delegate, public:
-
-void StatusIconMenuModel::Delegate::CommandIdHighlighted(int command_id) {
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // StatusIconMenuModel, public:
 
 StatusIconMenuModel::StatusIconMenuModel(Delegate* delegate)
@@ -169,11 +163,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // StatusIconMenuModel, private:
 
-void StatusIconMenuModel::CommandIdHighlighted(int command_id) {
-  if (delegate_)
-    delegate_->CommandIdHighlighted(command_id);
-}
-
 void StatusIconMenuModel::ExecuteCommand(int command_id, int event_flags) {
   if (delegate_)
     delegate_->ExecuteCommand(command_id, event_flags);
diff --git a/chrome/browser/status_icons/status_icon_menu_model.h b/chrome/browser/status_icons/status_icon_menu_model.h
index c1514ee..556f531f 100644
--- a/chrome/browser/status_icons/status_icon_menu_model.h
+++ b/chrome/browser/status_icons/status_icon_menu_model.h
@@ -28,10 +28,6 @@
  public:
   class Delegate {
    public:
-    // Notifies the delegate that the item with the specified command id was
-    // visually highlighted within the menu.
-    virtual void CommandIdHighlighted(int command_id);
-
     // Performs the action associates with the specified command id.
     // The passed |event_flags| are the flags from the event which issued this
     // command and they can be examined to find modifier keys.
@@ -97,7 +93,6 @@
 
  private:
   // Overridden from ui::SimpleMenuModel::Delegate:
-  void CommandIdHighlighted(int command_id) override;
   void ExecuteCommand(int command_id, int event_flags) override;
 
   struct ItemState;
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
index 114d5bc..d98b547 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -170,6 +170,17 @@
 }
 #endif  // !OS_CHROMEOS
 
+void ProfileSyncServiceHarness::EnterSyncPausedStateForPrimaryAccount() {
+  DCHECK(service_->IsSyncFeatureActive());
+  identity::SetInvalidRefreshTokenForPrimaryAccount(
+      IdentityManagerFactory::GetForProfile(profile_));
+}
+
+void ProfileSyncServiceHarness::ExitSyncPausedStateForPrimaryAccount() {
+  identity::SetRefreshTokenForPrimaryAccount(
+      IdentityManagerFactory::GetForProfile(profile_));
+}
+
 bool ProfileSyncServiceHarness::SetupSync() {
   bool result = SetupSync(syncer::UserSelectableTypes());
   if (!result) {
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.h b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
index 1922b26c..f29fa1c 100644
--- a/chrome/browser/sync/test/integration/profile_sync_service_harness.h
+++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.h
@@ -53,6 +53,11 @@
   void SignOutPrimaryAccount();
 #endif  // !OS_CHROMEOS
 
+  // Enters/exits the "Sync paused" state, which in real life happens if a
+  // syncing user signs out of the content area.
+  void EnterSyncPausedStateForPrimaryAccount();
+  void ExitSyncPausedStateForPrimaryAccount();
+
   // Enables and configures sync for all available datatypes. Returns true only
   // after sync has been fully initialized and authenticated, and we are ready
   // to process changes.
diff --git a/chrome/browser/sync/test/integration/secondary_account_helper.cc b/chrome/browser/sync/test/integration/secondary_account_helper.cc
index dc50be6..f1e8782 100644
--- a/chrome/browser/sync/test/integration/secondary_account_helper.cc
+++ b/chrome/browser/sync/test/integration/secondary_account_helper.cc
@@ -27,20 +27,23 @@
 
 namespace {
 
-void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+void OnWillCreateBrowserContextServices(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context) {
   GaiaCookieManagerServiceFactory::GetInstance()->SetTestingFactory(
       context,
-      base::BindRepeating(
-          &BuildFakeGaiaCookieManagerServiceWithOptions,
-          /*create_fake_url_loader_factory_for_cookie_requests=*/true));
+      base::BindRepeating(&BuildFakeGaiaCookieManagerServiceWithURLLoader,
+                          test_url_loader_factory));
 }
 
 }  // namespace
 
-ScopedFakeGaiaCookieManagerServiceFactory SetUpFakeGaiaCookieManagerService() {
+ScopedFakeGaiaCookieManagerServiceFactory SetUpFakeGaiaCookieManagerService(
+    network::TestURLLoaderFactory* test_url_loader_factory) {
   return BrowserContextDependencyManager::GetInstance()
       ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
-          base::BindRepeating(&OnWillCreateBrowserContextServices));
+          base::BindRepeating(&OnWillCreateBrowserContextServices,
+                              test_url_loader_factory));
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/sync/test/integration/secondary_account_helper.h b/chrome/browser/sync/test/integration/secondary_account_helper.h
index b0bdc98..ad21a70 100644
--- a/chrome/browser/sync/test/integration/secondary_account_helper.h
+++ b/chrome/browser/sync/test/integration/secondary_account_helper.h
@@ -17,6 +17,10 @@
 class BrowserContext;
 }  // namespace content
 
+namespace network {
+class TestURLLoaderFactory;
+}
+
 namespace secondary_account_helper {
 
 using ScopedFakeGaiaCookieManagerServiceFactory = std::unique_ptr<
@@ -26,7 +30,8 @@
 // called from SetUpInProcessBrowserTestFixture. The caller should hold on to
 // the returned object for the duration of the test, e.g. store it in a member
 // of the test fixture class.
-ScopedFakeGaiaCookieManagerServiceFactory SetUpFakeGaiaCookieManagerService();
+ScopedFakeGaiaCookieManagerServiceFactory SetUpFakeGaiaCookieManagerService(
+    network::TestURLLoaderFactory* test_url_loader_factory);
 
 #if defined(OS_CHROMEOS)
 // Sets up necessary fakes for fake network responses to work. Meant to be
diff --git a/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
index bcd852bb..cf1df96 100644
--- a/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_custom_passphrase_sync_test.cc
@@ -306,7 +306,8 @@
   EXPECT_TRUE(WaitForClientBookmarkWithTitle("scypt-encrypted bookmark"));
 }
 
-// See crbug.com/915219 about THREAD_SANITIZER.
+// See crbug.com/915219 about THREAD_SANITIZER. This data race is hard to avoid
+// as overriding g_feature_list after it has been used is needed for this test.
 #if defined(GOOGLE_CHROME_BUILD) || defined(THREAD_SANITIZER)
 // TODO(crbug.com/902297): Make the test pass in Chrome-branded builds.
 #define MAYBE_CannotDecryptScryptKeyEncryptedDataWhenScryptDisabled \
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 90a8a3a..46e26c1 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -39,13 +39,15 @@
 
   void SetUpInProcessBrowserTestFixture() override {
     fake_gaia_cookie_manager_factory_ =
-        secondary_account_helper::SetUpFakeGaiaCookieManagerService();
+        secondary_account_helper::SetUpFakeGaiaCookieManagerService(
+            &test_url_loader_factory_);
   }
 
   void SetUpOnMainThread() override {
 #if defined(OS_CHROMEOS)
     secondary_account_helper::InitNetwork();
 #endif  // defined(OS_CHROMEOS)
+    SyncTest::SetUpOnMainThread();
   }
 
   Profile* profile() { return GetProfile(0); }
diff --git a/chrome/browser/sync/test/integration/single_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/integration/single_client_typed_urls_sync_test.cc
index 5497c08..da9f007 100644
--- a/chrome/browser/sync/test/integration/single_client_typed_urls_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_typed_urls_sync_test.cc
@@ -92,13 +92,7 @@
   ASSERT_TRUE(CheckAllProfilesHaveSameTypedURLs());
 }
 
-// crbug.com/919612
-#if defined(THREAD_SANITIZER)
-#define MAYBE_DeleteNonTyped DISABLED_DeleteNonTyped
-#else
-#define MAYBE_DeleteNonTyped DeleteNonTyped
-#endif
-IN_PROC_BROWSER_TEST_F(SingleClientTypedUrlsSyncTest, MAYBE_DeleteNonTyped) {
+IN_PROC_BROWSER_TEST_F(SingleClientTypedUrlsSyncTest, DeleteNonTyped) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   history::URLRows urls = GetTypedUrlsFromClient(0);
   ASSERT_EQ(0U, urls.size());
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 0041827..c0744f2 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -321,16 +321,10 @@
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
 #if !defined(OS_CHROMEOS)
-#if defined(THREAD_SANITIZER)
-// https://crbug.com/917385
-#define MAYBE_DownloadAccountStorage_Card DISABLED_DownloadAccountStorage_Card
-#else
-#define MAYBE_DownloadAccountStorage_Card DownloadAccountStorage_Card
-#endif
 // The account storage requires USS, so we only test the USS implementation
 // here.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletWithAccountStorageSyncTest,
-                       MAYBE_DownloadAccountStorage_Card) {
+                       DownloadAccountStorage_Card) {
   ASSERT_TRUE(SetupClients());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   pdm->OnSyncServiceInitialized(GetSyncService(0));
@@ -1079,13 +1073,15 @@
 
   void SetUpInProcessBrowserTestFixture() override {
     fake_gaia_cookie_manager_factory_ =
-        secondary_account_helper::SetUpFakeGaiaCookieManagerService();
+        secondary_account_helper::SetUpFakeGaiaCookieManagerService(
+            &test_url_loader_factory_);
   }
 
   void SetUpOnMainThread() override {
 #if defined(OS_CHROMEOS)
     secondary_account_helper::InitNetwork();
 #endif  // defined(OS_CHROMEOS)
+    SyncTest::SetUpOnMainThread();
   }
 
   Profile* profile() { return GetProfile(0); }
@@ -1100,17 +1096,8 @@
 // ChromeOS doesn't support changes to the primary account after startup, so
 // these secondary-account-related tests don't apply.
 #if !defined(OS_CHROMEOS)
-#if defined(THREAD_SANITIZER)
-// Web Database thread and history DB thread access sqlite concurrently,
-// https://crbug.com/917380
-#define MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptIn \
-  DISABLED_SwitchesFromAccountToProfileStorageOnSyncOptIn
-#else
-#define MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptIn \
-  SwitchesFromAccountToProfileStorageOnSyncOptIn
-#endif
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSecondaryAccountSyncTest,
-                       MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptIn) {
+                       SwitchesFromAccountToProfileStorageOnSyncOptIn) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
@@ -1172,18 +1159,9 @@
   EXPECT_NE(nullptr, GetPaymentsCustomerData(profile_data).get());
 }
 
-#if defined(THREAD_SANITIZER)
-// Web Database thread and history DB thread access sqlite concurrently,
-// https://crbug.com/917380
-#define MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup \
-  DISABLED_SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup
-#else
-#define MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup \
-  SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup
-#endif
 IN_PROC_BROWSER_TEST_P(
     SingleClientWalletSecondaryAccountSyncTest,
-    MAYBE_SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup) {
+    SwitchesFromAccountToProfileStorageOnSyncOptInWithAdvancedSetup) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
@@ -1277,17 +1255,8 @@
 // 3. Enable Sync-the-feature again -> profile storage.
 // 4. StopAndClear() -> account storage.
 // 5. Enable Sync-the-feature again -> profile storage.
-// Flaky on TSan, see crbug.com/917380.
-#if defined(THREAD_SANITIZER)
-#define MAYBE_SwitchesBetweenAccountAndProfileStorageOnTogglingSync \
-  DISABLED_SwitchesBetweenAccountAndProfileStorageOnTogglingSync
-#else
-#define MAYBE_SwitchesBetweenAccountAndProfileStorageOnTogglingSync \
-  SwitchesBetweenAccountAndProfileStorageOnTogglingSync
-#endif
-IN_PROC_BROWSER_TEST_P(
-    SingleClientWalletWithAccountStorageSyncTest,
-    MAYBE_SwitchesBetweenAccountAndProfileStorageOnTogglingSync) {
+IN_PROC_BROWSER_TEST_P(SingleClientWalletWithAccountStorageSyncTest,
+                       SwitchesBetweenAccountAndProfileStorageOnTogglingSync) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
@@ -1413,11 +1382,11 @@
 
 INSTANTIATE_TEST_CASE_P(USS,
                         SingleClientWalletSyncTestWithoutAccountStorage,
-                        ::testing::Values(false));
+                        ::testing::Values(false, true));
 
 INSTANTIATE_TEST_CASE_P(USS,
                         SingleClientWalletWithAccountStorageSyncTest,
-                        ::testing::Values(false));
+                        ::testing::Values(false, true));
 
 INSTANTIATE_TEST_CASE_P(USS,
                         SingleClientWalletSyncTestWithDefaultFeatures,
@@ -1425,4 +1394,4 @@
 
 INSTANTIATE_TEST_CASE_P(USS,
                         SingleClientWalletSecondaryAccountSyncTest,
-                        ::testing::Values(false));
+                        ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc
index d593e9c..95afee4 100644
--- a/chrome/browser/sync/test/integration/sync_auth_test.cc
+++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -300,3 +300,54 @@
   std::string new_token = GetSyncService(0)->GetAccessTokenForTest();
   ASSERT_NE(old_token, new_token);
 }
+
+class NoAuthErrorChecker : public SingleClientStatusChangeChecker {
+ public:
+  explicit NoAuthErrorChecker(browser_sync::ProfileSyncService* service)
+      : SingleClientStatusChangeChecker(service) {}
+
+  // StatusChangeChecker implementation.
+  bool IsExitConditionSatisfied() override {
+    return service()->GetAuthError().state() == GoogleServiceAuthError::NONE;
+  }
+
+  std::string GetDebugMessage() const override {
+    return "Waiting for auth error to be cleared";
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SyncAuthTest, SyncPausedState) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  ASSERT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
+  ASSERT_EQ(GetSyncService(0)->GetTransportState(),
+            syncer::SyncService::TransportState::ACTIVE);
+  const syncer::ModelTypeSet active_types =
+      GetSyncService(0)->GetActiveDataTypes();
+  ASSERT_FALSE(active_types.Empty());
+
+  // Enter the "Sync paused" state.
+  GetClient(0)->EnterSyncPausedStateForPrimaryAccount();
+  ASSERT_TRUE(GetSyncService(0)->GetAuthError().IsPersistentError());
+  ASSERT_TRUE(AttemptToTriggerAuthError());
+
+  // While Sync itself is still considered active, the active data types should
+  // now be empty.
+  EXPECT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
+  EXPECT_EQ(GetSyncService(0)->GetTransportState(),
+            syncer::SyncService::TransportState::ACTIVE);
+  EXPECT_TRUE(GetSyncService(0)->GetActiveDataTypes().Empty());
+
+  // Clear the "Sync paused" state again.
+  GetClient(0)->ExitSyncPausedStateForPrimaryAccount();
+  // SyncService will clear its auth error state only once it gets a valid
+  // access token again, so wait for that to happen.
+  NoAuthErrorChecker(GetSyncService(0)).Wait();
+  ASSERT_FALSE(GetSyncService(0)->GetAuthError().IsPersistentError());
+
+  // Now the active data types should be back.
+  EXPECT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
+  EXPECT_EQ(GetSyncService(0)->GetTransportState(),
+            syncer::SyncService::TransportState::ACTIVE);
+  EXPECT_EQ(GetSyncService(0)->GetActiveDataTypes(), active_types);
+}
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 2e3cf95..f1bbc5f 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -265,11 +265,6 @@
   // Mock the Mac Keychain service.  The real Keychain can block on user input.
   OSCryptMocker::SetUp();
 
-  // Start up a sync test server if one is needed and setup mock gaia responses.
-  // Note: This must be done prior to the call to SetupClients() because we want
-  // the mock gaia responses to be available before GaiaUrls is initialized.
-  SetUpTestServerIfRequired();
-
   // Yield control back to the InProcessBrowserTest framework.
   InProcessBrowserTest::SetUp();
 }
@@ -937,6 +932,11 @@
 }
 
 void SyncTest::SetUpOnMainThread() {
+  // Start up a sync test server if one is needed and setup mock gaia responses.
+  // Note: This must be done prior to the call to SetupClients() because we want
+  // the mock gaia responses to be available before GaiaUrls is initialized.
+  SetUpTestServerIfRequired();
+
   if (!UsingExternalServers())
     SetupMockGaiaResponsesForProfile(ProfileManager::GetActiveUserProfile());
 
@@ -1072,7 +1072,9 @@
       LOG(FATAL) << "Failed to set up local python sync and XMPP servers";
     SetupMockGaiaResponses();
   } else if (server_type_ == IN_PROCESS_FAKE_SERVER) {
-    fake_server_ = std::make_unique<fake_server::FakeServer>();
+    base::FilePath user_data_dir;
+    base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+    fake_server_ = std::make_unique<fake_server::FakeServer>(user_data_dir);
     SetupMockGaiaResponses();
   } else {
     LOG(FATAL) << "Don't know which server environment to run test in.";
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index eb4aa17..266371fe 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -43,7 +43,7 @@
 // To disable a test from running on Chromium waterfalls, you would still use
 // the default DISABLED_test_name macro. To disable it from running as an E2E
 // test outside Chromium waterfalls you would need to remove the E2E* macro.
-#define MACRO_CONCAT(prefix, test_name) prefix ## _ ## test_name
+#define MACRO_CONCAT(prefix, test_name) prefix##_##test_name
 #define E2E_ONLY(test_name) MACRO_CONCAT(DISABLED_E2ETest, test_name)
 #define E2E_ENABLED(test_name) MACRO_CONCAT(test_name, E2ETest)
 
@@ -328,6 +328,9 @@
   // The FakeServer used in tests with server type IN_PROCESS_FAKE_SERVER.
   std::unique_ptr<fake_server::FakeServer> fake_server_;
 
+  // The factory used to mock out GAIA signin.
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
  protected:
   virtual void BeforeSetupClient(int index);
 
@@ -501,9 +504,6 @@
   // Used to start and stop the local test server.
   base::Process test_server_;
 
-  // The factory used to mock out GAIA signin.
-  network::TestURLLoaderFactory test_url_loader_factory_;
-
   // The shared URLLoaderFactory backed by |test_url_loader_factory_|.
   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
       test_shared_url_loader_factory_;
diff --git a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
index 606c7f0..860af47 100644
--- a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
@@ -82,10 +82,23 @@
   EXPECT_TRUE(WaitForBookmarksToMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientCustomPassphraseSyncTest,
-                       ClientsCanSyncDataWhenScryptEncryptionNotEnabled) {
-  ScopedScryptFeatureToggler toggler(/*force_disabled=*/false,
-                                     /*use_for_new_passphrases=*/false);
+class TwoClientCustomPassphraseSyncTestWithScryptEncryptionNotEnabled
+    : public TwoClientCustomPassphraseSyncTest {
+ public:
+  TwoClientCustomPassphraseSyncTestWithScryptEncryptionNotEnabled()
+      : toggler_(/*force_disabled=*/false,
+                 /*use_for_new_passphrases=*/false) {}
+  ~TwoClientCustomPassphraseSyncTestWithScryptEncryptionNotEnabled() override {}
+
+ private:
+  ScopedScryptFeatureToggler toggler_;
+  DISALLOW_COPY_AND_ASSIGN(
+      TwoClientCustomPassphraseSyncTestWithScryptEncryptionNotEnabled);
+};
+
+IN_PROC_BROWSER_TEST_F(
+    TwoClientCustomPassphraseSyncTestWithScryptEncryptionNotEnabled,
+    ClientsCanSyncData) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -104,8 +117,23 @@
   EXPECT_TRUE(WaitForBookmarksToMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientCustomPassphraseSyncTest,
-                       ClientsCanSyncDataWhenScryptEncryptionEnabledInBoth) {
+class TwoClientCustomPassphraseSyncTestWithScryptEncryptionEnabled
+    : public TwoClientCustomPassphraseSyncTest {
+ public:
+  TwoClientCustomPassphraseSyncTestWithScryptEncryptionEnabled()
+      : toggler_(/*force_disabled=*/false,
+                 /*use_for_new_passphrases=*/true) {}
+  ~TwoClientCustomPassphraseSyncTestWithScryptEncryptionEnabled() override {}
+
+ private:
+  ScopedScryptFeatureToggler toggler_;
+  DISALLOW_COPY_AND_ASSIGN(
+      TwoClientCustomPassphraseSyncTestWithScryptEncryptionEnabled);
+};
+
+IN_PROC_BROWSER_TEST_F(
+    TwoClientCustomPassphraseSyncTestWithScryptEncryptionEnabled,
+    ClientsCanSyncData) {
   ScopedScryptFeatureToggler toggler(/*force_disabled=*/false,
                                      /*use_for_new_passphrases=*/true);
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -127,7 +155,8 @@
 }
 
 #if defined(THREAD_SANITIZER)
-// https://crbug.com/915219
+// https://crbug.com/915219. This data race is hard to avoid as overriding
+// g_feature_list after it has been used is needed for this test.
 #define MAYBE_ClientsCanSyncDataWhenScryptEncryptionEnabledInOne \
   DISABLED_ClientsCanSyncDataWhenScryptEncryptionEnabledInOne
 #else
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
index 243a3d32..27d6b41 100644
--- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -48,6 +48,10 @@
   bool TestUsesSelfNotifications() override { return false; }
 
  protected:
+  // TODO(crbug.com/915219): This leads to a data race and thus all tests here
+  // are disabled on TSan. It is hard to avoid as overriding g_feature_list
+  // after it has been used is needed for this test (by setting up each client
+  // with a different ScopedFeatureList).
   void BeforeSetupClient(int index) override {
     const bool should_enable_pseudo_uss =
         index == 0 ? std::get<0>(GetParam()) : std::get<1>(GetParam());
diff --git a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
index 8185a97..5c199ca 100644
--- a/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_typed_urls_sync_test.cc
@@ -228,15 +228,7 @@
   EXPECT_TRUE(CheckSyncHasMetadataForURLID(0, url_id_on_first_client));
 }
 
-#if defined(THREAD_SANITIZER)
-// https://crbug.com/917385
-#define MAYBE_AddThenExpireThenAddAgain DISABLED_AddThenExpireThenAddAgain
-#else
-#define MAYBE_AddThenExpireThenAddAgain AddThenExpireThenAddAgain
-#endif
-
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
-                       MAYBE_AddThenExpireThenAddAgain) {
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddThenExpireThenAddAgain) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   base::Time now = base::Time::Now();
@@ -288,15 +280,7 @@
   EXPECT_TRUE(CheckSyncHasURLMetadata(0, url));
 }
 
-#if defined(THREAD_SANITIZER)
-// https://crbug.com/917385
-#define MAYBE_AddThenExpireVisitByVisit DISABLED_AddThenExpireVisitByVisit
-#else
-#define MAYBE_AddThenExpireVisitByVisit AddThenExpireVisitByVisit
-#endif
-
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
-                       MAYBE_AddThenExpireVisitByVisit) {
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddThenExpireVisitByVisit) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   base::Time now = base::Time::Now();
@@ -492,14 +476,7 @@
   ASSERT_TRUE(ProfilesHaveSameTypedURLsChecker().Wait());
 }
 
-// crbug.com/919090
-#if defined(THREAD_SANITIZER)
-#define MAYBE_AddOneDeleteOtherAddAgain DISABLED_AddOneDeleteOtherAddAgain
-#else
-#define MAYBE_AddOneDeleteOtherAddAgain AddOneDeleteOtherAddAgain
-#endif
-IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest,
-                       MAYBE_AddOneDeleteOtherAddAgain) {
+IN_PROC_BROWSER_TEST_F(TwoClientTypedUrlsSyncTest, AddOneDeleteOtherAddAgain) {
   const base::string16 kHistoryUrl(
       ASCIIToUTF16("http://www.add-delete-add-history.google.com/"));
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
diff --git a/chrome/browser/sync_file_system/drive_backend/callback_helper_unittest.cc b/chrome/browser/sync_file_system/drive_backend/callback_helper_unittest.cc
index a3b6386..1dcbd43cf 100644
--- a/chrome/browser/sync_file_system/drive_backend/callback_helper_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/callback_helper_unittest.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
 
 #include "base/location.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -42,7 +42,7 @@
 }  // namespace
 
 TEST(DriveBackendCallbackHelperTest, BasicTest) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
 
   bool called = false;
   RelayCallbackToCurrentThread(
@@ -62,7 +62,7 @@
 }
 
 TEST(DriveBackendCallbackHelperTest, RunOnOtherThreadTest) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::Thread thread("WorkerThread");
   thread.Start();
 
@@ -90,7 +90,7 @@
 }
 
 TEST(DriveBackendCallbackHelperTest, PassNullFunctionTest) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::Closure closure = RelayCallbackToCurrentThread(
       FROM_HERE,
       base::Closure());
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
index d3b60d78..6517f12 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
@@ -12,10 +12,11 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
+
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
@@ -624,7 +625,7 @@
 
  private:
   base::ScopedTempDir database_dir_;
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   std::unique_ptr<leveldb::Env> in_memory_env_;
   std::unique_ptr<MetadataDatabase> metadata_database_;
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_task_manager_unittest.cc b/chrome/browser/sync_file_system/drive_backend/sync_task_manager_unittest.cc
index 0485311..37e9e168 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_task_manager_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_task_manager_unittest.cc
@@ -13,9 +13,9 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/sync_file_system/drive_backend/sync_task.h"
 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
@@ -296,7 +296,7 @@
 }  // namespace
 
 TEST(SyncTaskManagerTest, ScheduleTask) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   TaskManagerClient client(0 /* maximum_background_task */);
   int callback_count = 0;
   SyncStatusCode callback_status = SYNC_STATUS_OK;
@@ -316,7 +316,7 @@
 }
 
 TEST(SyncTaskManagerTest, ScheduleTwoTasks) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   TaskManagerClient client(0 /* maximum_background_task */);
   int callback_count = 0;
   SyncStatusCode callback_status = SYNC_STATUS_OK;
@@ -339,7 +339,7 @@
 }
 
 TEST(SyncTaskManagerTest, ScheduleIdleTask) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   TaskManagerClient client(0 /* maximum_background_task */);
 
   client.ScheduleTaskIfIdle(kStatus1);
@@ -353,7 +353,7 @@
 }
 
 TEST(SyncTaskManagerTest, ScheduleIdleTaskWhileNotIdle) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   TaskManagerClient client(0 /* maximum_background_task */);
   int callback_count = 0;
   SyncStatusCode callback_status = SYNC_STATUS_OK;
@@ -375,7 +375,7 @@
 }
 
 TEST(SyncTaskManagerTest, ScheduleAndCancelSyncTask) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
 
   int callback_count = 0;
   SyncStatusCode status = SYNC_STATUS_UNKNOWN;
@@ -404,7 +404,7 @@
 }
 
 TEST(SyncTaskManagerTest, ScheduleTaskAtPriority) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
                                0 /* maximum_background_task */,
                                base::ThreadTaskRunnerHandle::Get());
@@ -465,7 +465,7 @@
 }
 
 TEST(SyncTaskManagerTest, BackgroundTask_Sequential) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
                                10 /* maximum_background_task */,
                                base::ThreadTaskRunnerHandle::Get());
@@ -497,7 +497,7 @@
 }
 
 TEST(SyncTaskManagerTest, BackgroundTask_Parallel) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
                                10 /* maximum_background_task */,
                                base::ThreadTaskRunnerHandle::Get());
@@ -529,7 +529,7 @@
 }
 
 TEST(SyncTaskManagerTest, BackgroundTask_Throttled) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
                                2 /* maximum_background_task */,
                                base::ThreadTaskRunnerHandle::Get());
@@ -561,7 +561,7 @@
 }
 
 TEST(SyncTaskManagerTest, UpdateTaskBlocker) {
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
                                10 /* maximum_background_task */,
                                base::ThreadTaskRunnerHandle::Get());
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 9343539..b8f6ae2 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -82,11 +82,6 @@
   // but this is very subject to change. Additionally, dark mode incognito may
   // end up having a different look. For now, just call into GetIncognitoColor
   // for convenience, but maintain a separate interface.
-
-  // NTP background is an exception since the NTP is different in incognito.
-  if (id == ThemeProperties::COLOR_NTP_BACKGROUND) {
-    return base::nullopt;
-  }
   return GetIncognitoColor(id);
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3f7d9a8..0fd706c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -851,8 +851,6 @@
       "exclusive_access/keyboard_lock_controller.h",
       "exclusive_access/mouse_lock_controller.cc",
       "exclusive_access/mouse_lock_controller.h",
-      "fast_unload_controller.cc",
-      "fast_unload_controller.h",
       "find_bar/find_bar_controller.cc",
       "find_bar/find_bar_controller.h",
       "find_bar/find_bar_platform_helper.cc",
@@ -1009,6 +1007,8 @@
       "tab_contents/tab_contents_iterator.h",
       "tab_modal_confirm_dialog_delegate.cc",
       "tab_modal_confirm_dialog_delegate.h",
+      "tabs/existing_tab_group_sub_menu_model.cc",
+      "tabs/existing_tab_group_sub_menu_model.h",
       "tabs/hover_tab_selector.cc",
       "tabs/hover_tab_selector.h",
       "tabs/pinned_tab_codec.cc",
@@ -1078,6 +1078,8 @@
       "webui/app_launcher_login_handler.h",
       "webui/app_management/app_management_page_handler.cc",
       "webui/app_management/app_management_page_handler.h",
+      "webui/app_management/app_management_shelf_delegate_chromeos.cc",
+      "webui/app_management/app_management_shelf_delegate_chromeos.h",
       "webui/app_management/app_management_ui.cc",
       "webui/app_management/app_management_ui.h",
       "webui/browsing_history_handler.cc",
diff --git a/chrome/browser/ui/android/autofill/password_generation_popup_view_android.cc b/chrome/browser/ui/android/autofill/password_generation_popup_view_android.cc
index e5e5771..eb68f01 100644
--- a/chrome/browser/ui/android/autofill/password_generation_popup_view_android.cc
+++ b/chrome/browser/ui/android/autofill/password_generation_popup_view_android.cc
@@ -25,12 +25,6 @@
     PasswordGenerationPopupController* controller)
     : controller_(controller) {}
 
-void PasswordGenerationPopupViewAndroid::SavedPasswordsLinkClicked(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  if (controller_)
-    controller_->OnSavedPasswordsLinkClicked();
-}
 
 void PasswordGenerationPopupViewAndroid::Dismissed(
     JNIEnv* env,
@@ -105,8 +99,7 @@
       env, java_object_, controller_->IsRTL(),
       controller_->state() ==
           PasswordGenerationPopupController::kOfferGeneration,
-      password, suggestion, help, controller_->HelpTextLinkRange().start(),
-      controller_->HelpTextLinkRange().end());
+      password, suggestion, help);
 }
 
 void PasswordGenerationPopupViewAndroid::PasswordSelectionUpdated() {}
diff --git a/chrome/browser/ui/android/autofill/password_generation_popup_view_android.h b/chrome/browser/ui/android/autofill/password_generation_popup_view_android.h
index 74b8280..7adf727 100644
--- a/chrome/browser/ui/android/autofill/password_generation_popup_view_android.h
+++ b/chrome/browser/ui/android/autofill/password_generation_popup_view_android.h
@@ -22,11 +22,6 @@
   explicit PasswordGenerationPopupViewAndroid(
       PasswordGenerationPopupController* controller);
 
-  // Called from JNI when the "saved passwords" link was clicked.
-  void SavedPasswordsLinkClicked(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-
   // Called from JNI when the popup was dismissed.
   void Dismissed(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
 
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
index 3812d75..335abae 100644
--- a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
+++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
@@ -159,11 +159,13 @@
     if (value && delegate->IsTranslatableLanguageByPrefs()) {
       action_flags_ |= FLAG_NEVER_LANGUAGE;
       delegate->ToggleTranslatableLanguageByPrefs();
+      RemoveSelf();
     }
   } else if (option == TranslateUtils::OPTION_NEVER_TRANSLATE_SITE) {
     if (value && !delegate->IsSiteBlacklisted()) {
       action_flags_ |= FLAG_NEVER_SITE;
       delegate->ToggleSiteBlacklist();
+      RemoveSelf();
     }
   } else {
     DCHECK(false);
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc
index 0d8a66a..6390570 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -53,20 +53,7 @@
     const apps::AppUpdate& app_update)
     : ChromeAppListItem(profile, app_update.AppId()),
       app_type_(app_update.AppType()) {
-  SetName(app_update.Name());
-  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile);
-  if (proxy) {
-    // TODO(crbug.com/826982): if another AppUpdate is observed, we should call
-    // LoadIcon again. The question (see the TODO in
-    // AppServiceAppModelBuilder::OnAppUpdate) is who should be the observer:
-    // the AppModelBuilder or the AppItem?
-    proxy->LoadIcon(app_update.AppId(),
-                    apps::mojom::IconCompression::kUncompressed,
-                    app_list::AppListConfig::instance().grid_icon_dimension(),
-                    base::BindOnce(&AppServiceAppItem::OnLoadIcon,
-                                   weak_ptr_factory_.GetWeakPtr()));
-  }
-
+  OnAppUpdate(app_update, true);
   if (sync_item && sync_item->item_ordinal.IsValid()) {
     UpdateFromSync(sync_item);
   } else {
@@ -79,6 +66,28 @@
 
 AppServiceAppItem::~AppServiceAppItem() = default;
 
+void AppServiceAppItem::OnAppUpdate(const apps::AppUpdate& app_update) {
+  OnAppUpdate(app_update, false);
+}
+
+void AppServiceAppItem::OnAppUpdate(const apps::AppUpdate& app_update,
+                                    bool in_constructor) {
+  if (in_constructor || app_update.NameChanged()) {
+    SetName(app_update.Name());
+  }
+
+  if (in_constructor || app_update.IconKeyChanged()) {
+    apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile());
+    if (proxy) {
+      proxy->LoadIcon(app_update.AppId(),
+                      apps::mojom::IconCompression::kUncompressed,
+                      app_list::AppListConfig::instance().grid_icon_dimension(),
+                      base::BindOnce(&AppServiceAppItem::OnLoadIcon,
+                                     weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+}
+
 void AppServiceAppItem::Activate(int event_flags) {
   Launch(event_flags, apps::mojom::LaunchSource::kFromAppListGrid);
 }
diff --git a/chrome/browser/ui/app_list/app_service_app_item.h b/chrome/browser/ui/app_list/app_service_app_item.h
index fadbd7d..15e8bfa 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.h
+++ b/chrome/browser/ui/app_list/app_service_app_item.h
@@ -33,7 +33,11 @@
                     const apps::AppUpdate& app_update);
   ~AppServiceAppItem() override;
 
+  void OnAppUpdate(const apps::AppUpdate& app_update);
+
  private:
+  void OnAppUpdate(const apps::AppUpdate& app_update, bool in_constructor);
+
   // ChromeAppListItem overrides:
   void Activate(int event_flags) override;
   const char* GetItemType() const override;
diff --git a/chrome/browser/ui/app_list/app_service_app_model_builder.cc b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
index 37392dc..555c4621 100644
--- a/chrome/browser/ui/app_list/app_service_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
@@ -27,17 +27,32 @@
 }
 
 void AppServiceAppModelBuilder::OnAppUpdate(const apps::AppUpdate& update) {
-  // TODO(crbug.com/826982): look for (and update) existing AppServiceAppItem's
-  // for the update's AppId, instead of always creating new ones.
-  //
-  // Do we want to have one AppModelBuilder that observes the Cache (and
-  // forwards updates such as icon changes on to each AppItem)?? Or should
-  // every AppItem be its own observer??
+  ChromeAppListItem* item = GetAppItem(update.AppId());
+  bool show = (update.Readiness() == apps::mojom::Readiness::kReady) &&
+              (update.ShowInLauncher() == apps::mojom::OptionalBool::kTrue);
 
-  if (update.ShowInLauncher() != apps::mojom::OptionalBool::kTrue) {
-    return;
+  if (item) {
+    if (show) {
+      DCHECK(item->GetItemType() == AppServiceAppItem::kItemType);
+      static_cast<AppServiceAppItem*>(item)->OnAppUpdate(update);
+
+      // TODO(crbug.com/826982): drop the check for kExtension or kWeb, and
+      // call UpdateItem unconditionally?
+      apps::mojom::AppType app_type = update.AppType();
+      if ((app_type == apps::mojom::AppType::kExtension) ||
+          (app_type == apps::mojom::AppType::kWeb)) {
+        app_list::AppListSyncableService* serv = service();
+        if (serv) {
+          serv->UpdateItem(item);
+        }
+      }
+
+    } else {
+      RemoveApp(update.AppId(), false /* unsynced_change */);
+    }
+
+  } else if (show) {
+    InsertApp(std::make_unique<AppServiceAppItem>(
+        profile(), model_updater(), GetSyncItem(update.AppId()), update));
   }
-
-  InsertApp(std::make_unique<AppServiceAppItem>(
-      profile(), model_updater(), GetSyncItem(update.AppId()), update));
 }
diff --git a/chrome/browser/ui/app_list/plugin_vm/plugin_vm_app_item.cc b/chrome/browser/ui/app_list/plugin_vm/plugin_vm_app_item.cc
index e95aecb..0808975 100644
--- a/chrome/browser/ui/app_list/plugin_vm/plugin_vm_app_item.cc
+++ b/chrome/browser/ui/app_list/plugin_vm/plugin_vm_app_item.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/app_list/plugin_vm/plugin_vm_app_item.h"
 
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -34,7 +35,12 @@
 }
 
 void PluginVmAppItem::Activate(int event_flags) {
-  // TODO (aoldemeier, http://crbug.com/904848, http://crbug.com/904853)
+  if (plugin_vm::IsPluginVmConfigured(profile())) {
+    // TODO(http://crbug.com/904853): Start PluginVm.
+  } else {
+    // TODO(http://crbug.com/904852): Show dialog that sets up PluginVm
+    // environment.
+  }
   // Manually close app_list view because focus is not changed on PluginVm app
   // start, and current view remains active.
   GetController()->DismissView();
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index 6e20ff20..9881ad3 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -257,12 +257,15 @@
 
 namespace {
 
-class AppServiceDataSource : public AppSearchProvider::DataSource {
+class AppServiceDataSource : public AppSearchProvider::DataSource,
+                             public apps::AppRegistryCache::Observer {
  public:
   AppServiceDataSource(Profile* profile, AppSearchProvider* owner)
       : AppSearchProvider::DataSource(profile, owner) {
-    // TODO(crbug.com/826982): observe the cache for apps being installed and
-    // uninstalled, and in the callback, call RefreshAppsAndUpdateResultsXxx().
+    apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile);
+    if (proxy) {
+      Observe(&proxy->Cache());
+    }
   }
 
   ~AppServiceDataSource() override = default;
@@ -303,6 +306,15 @@
   }
 
  private:
+  // apps::AppRegistryCache::Observer overrides:
+  void OnAppUpdate(const apps::AppUpdate& update) override {
+    if (update.Readiness() == apps::mojom::Readiness::kReady) {
+      owner()->RefreshAppsAndUpdateResultsDeferred();
+    } else {
+      owner()->RefreshAppsAndUpdateResults();
+    }
+  }
+
   DISALLOW_COPY_AND_ASSIGN(AppServiceDataSource);
 };
 
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 3eefff91..f514cf1 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -37,7 +37,8 @@
 
 ChromeShellDelegate::~ChromeShellDelegate() = default;
 
-bool ChromeShellDelegate::CanShowWindowForUser(aura::Window* window) const {
+bool ChromeShellDelegate::CanShowWindowForUser(
+    const aura::Window* window) const {
   return ::CanShowWindowForUser(window,
                                 base::BindRepeating(&GetActiveBrowserContext));
 }
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 27ce5304..6f0f7436 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -14,7 +14,7 @@
   ~ChromeShellDelegate() override;
 
   // ash::ShellDelegate:
-  bool CanShowWindowForUser(aura::Window* window) const override;
+  bool CanShowWindowForUser(const aura::Window* window) const override;
   std::unique_ptr<ash::ScreenshotDelegate> CreateScreenshotDelegate() override;
   ash::AccessibilityDelegate* CreateAccessibilityDelegate() override;
   void OpenKeyboardShortcutHelpPage() const override;
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_web_contents.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_web_contents.cc
index bf06ba6..5bbc19a 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_web_contents.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_web_contents.cc
@@ -263,8 +263,9 @@
 }
 
 void ChromeKeyboardWebContents::MaybeRunLoadCallback() {
-  if (!load_callback_.is_null() && !contents_size_.IsEmpty() &&
-      !token_.is_empty()) {
+  // Note: |contents_size_| may still be empty, in which case
+  // AshKeyboardUI::AshKeyboardView::OnWindowBoundsChanged should get called
+  // with the correct contents size.
+  if (!load_callback_.is_null() && !token_.is_empty())
     std::move(load_callback_).Run(token_, contents_size_);
-  }
 }
diff --git a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
index eee1af8..4e2f4b9 100644
--- a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
@@ -430,24 +430,3 @@
   EXPECT_EQ(controller->GetStateForTest(),
             keyboard::KeyboardControllerState::INITIAL);
 }
-
-// A test for crbug.com/734534. Only for classic Ash.
-IN_PROC_BROWSER_TEST_F(KeyboardControllerWebContentTest,
-                       DoesNotCrashWhenParentDoesNotExist) {
-  if (::features::IsUsingWindowService())
-    return;
-  auto* controller = keyboard::KeyboardController::Get();
-
-  controller->LoadKeyboardWindowInBackground();
-
-  aura::Window* view = controller->GetKeyboardWindow();
-  EXPECT_TRUE(view);
-
-  // Remove the keyboard window parent.
-  EXPECT_TRUE(view->parent());
-  controller->DeactivateKeyboard();
-  EXPECT_FALSE(view->parent());
-
-  // Change window size to trigger OnWindowBoundsChanged.
-  view->SetBounds(gfx::Rect(0, 0, 1200, 800));
-}
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h
index f3b3217..2827e450 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h
@@ -76,7 +76,7 @@
 
   // See who owns this window. The return value is the user account id or an
   // empty AccountId if not assigned yet.
-  virtual const AccountId& GetWindowOwner(aura::Window* window) const = 0;
+  virtual const AccountId& GetWindowOwner(const aura::Window* window) const = 0;
 
   // Allows to show an owned window for another users. If the window is not
   // owned, this call will return immediately. (The FileManager for example
@@ -102,7 +102,7 @@
   // Get the user on which the window is currently shown. If an empty string is
   // passed back the window will be presented for every user.
   virtual const AccountId& GetUserPresentingWindow(
-      aura::Window* window) const = 0;
+      const aura::Window* window) const = 0;
 
   // Adds user to monitor starting and running V1/V2 application windows.
   // Returns immediately if the user (identified by a |profile|) is already
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
index eb530db..cab8688 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
@@ -269,9 +269,13 @@
 }
 
 const AccountId& MultiUserWindowManagerClientImpl::GetWindowOwner(
-    aura::Window* window) const {
-  WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
-  return it != window_to_entry_.end() ? it->second->owner() : EmptyAccountId();
+    const aura::Window* window) const {
+  for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
+       it != window_to_entry_.end(); it++) {
+    if (it->first == window)
+      return it->second->owner();
+  }
+  return EmptyAccountId();
 }
 
 void MultiUserWindowManagerClientImpl::ShowWindowForUser(
@@ -315,14 +319,16 @@
 }
 
 const AccountId& MultiUserWindowManagerClientImpl::GetUserPresentingWindow(
-    aura::Window* window) const {
-  WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
+    const aura::Window* window) const {
+  for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
+       it != window_to_entry_.end(); it++) {
+    if (it->first == window)
+      // We ask the object for its desktop.
+      return it->second->show_for_user();
+  }
   // If the window is not owned by anyone it is shown on all desktops and we
   // return the empty string.
-  if (it == window_to_entry_.end())
-    return EmptyAccountId();
-  // Otherwise we ask the object for its desktop.
-  return it->second->show_for_user();
+  return EmptyAccountId();
 }
 
 void MultiUserWindowManagerClientImpl::AddUser(
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
index 42ad000..ec5d4fb3 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
@@ -55,7 +55,7 @@
   // MultiUserWindowManager overrides:
   void SetWindowOwner(aura::Window* window,
                       const AccountId& account_id) override;
-  const AccountId& GetWindowOwner(aura::Window* window) const override;
+  const AccountId& GetWindowOwner(const aura::Window* window) const override;
   void ShowWindowForUser(aura::Window* window,
                          const AccountId& account_id) override;
   bool AreWindowsSharedAmongUsers() const override;
@@ -63,7 +63,8 @@
       std::set<AccountId>* account_ids) const override;
   bool IsWindowOnDesktopOfUser(aura::Window* window,
                                const AccountId& account_id) const override;
-  const AccountId& GetUserPresentingWindow(aura::Window* window) const override;
+  const AccountId& GetUserPresentingWindow(
+      const aura::Window* window) const override;
   void AddUser(content::BrowserContext* context) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
index d53caaa..0aa2228 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
@@ -100,7 +100,7 @@
  public:
   TestShellDelegateChromeOS() {}
 
-  bool CanShowWindowForUser(aura::Window* window) const override {
+  bool CanShowWindowForUser(const aura::Window* window) const override {
     return ::CanShowWindowForUser(window,
                                   base::BindRepeating(&GetActiveContext));
   }
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.cc
index 0d377b1..72c61b91 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.cc
@@ -19,7 +19,7 @@
 }
 
 const AccountId& MultiUserWindowManagerClientStub::GetWindowOwner(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   return EmptyAccountId();
 }
 
@@ -43,7 +43,7 @@
 }
 
 const AccountId& MultiUserWindowManagerClientStub::GetUserPresentingWindow(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   return EmptyAccountId();
 }
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.h
index fed067f9..be0f18b 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_stub.h
@@ -24,7 +24,7 @@
   // MultiUserWindowManager overrides:
   void SetWindowOwner(aura::Window* window,
                       const AccountId& account_id) override;
-  const AccountId& GetWindowOwner(aura::Window* window) const override;
+  const AccountId& GetWindowOwner(const aura::Window* window) const override;
   void ShowWindowForUser(aura::Window* window,
                          const AccountId& account_id) override;
   bool AreWindowsSharedAmongUsers() const override;
@@ -32,7 +32,8 @@
       std::set<AccountId>* account_ids) const override;
   bool IsWindowOnDesktopOfUser(aura::Window* window,
                                const AccountId& account_id) const override;
-  const AccountId& GetUserPresentingWindow(aura::Window* window) const override;
+  const AccountId& GetUserPresentingWindow(
+      const aura::Window* window) const override;
   void AddUser(content::BrowserContext* context) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
diff --git a/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.cc b/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.cc
index 41f74b8..8de3233f 100644
--- a/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.cc
+++ b/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.cc
@@ -37,7 +37,7 @@
 }
 
 const AccountId& TestMultiUserWindowManagerClient::GetWindowOwner(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   // No matter which window will get queried - all browsers belong to the
   // original browser's user.
   return browser_owner_;
@@ -81,7 +81,7 @@
 }
 
 const AccountId& TestMultiUserWindowManagerClient::GetUserPresentingWindow(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   if (window == browser_window_)
     return desktop_owner_;
   if (created_window_ && window == created_window_)
diff --git a/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.h b/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.h
index ce10ec457..90d3900b 100644
--- a/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.h
+++ b/chrome/browser/ui/ash/multi_user/test_multi_user_window_manager_client.h
@@ -26,7 +26,7 @@
   // MultiUserWindowManager overrides:
   void SetWindowOwner(aura::Window* window,
                       const AccountId& account_id) override;
-  const AccountId& GetWindowOwner(aura::Window* window) const override;
+  const AccountId& GetWindowOwner(const aura::Window* window) const override;
   void ShowWindowForUser(aura::Window* window,
                          const AccountId& account_id) override;
   bool AreWindowsSharedAmongUsers() const override;
@@ -34,7 +34,8 @@
       std::set<AccountId>* account_ids) const override;
   bool IsWindowOnDesktopOfUser(aura::Window* window,
                                const AccountId& account_id) const override;
-  const AccountId& GetUserPresentingWindow(aura::Window* window) const override;
+  const AccountId& GetUserPresentingWindow(
+      const aura::Window* window) const override;
   void AddUser(content::BrowserContext* profile) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
diff --git a/chrome/browser/ui/ash/session_util.cc b/chrome/browser/ui/ash/session_util.cc
index ee2a84d..b9344a5 100644
--- a/chrome/browser/ui/ash/session_util.cc
+++ b/chrome/browser/ui/ash/session_util.cc
@@ -23,8 +23,9 @@
 // profile of the user who owns the window or the profile of the desktop on
 // which the window is positioned (for teleported windows) is returned, based on
 // |presenting|.
-content::BrowserContext* GetBrowserContextForWindow(aura::Window* window,
-                                                    bool presenting) {
+const content::BrowserContext* GetBrowserContextForWindow(
+    const aura::Window* window,
+    bool presenting) {
   DCHECK(window);
   auto* client = MultiUserWindowManagerClient::GetInstance();
   // Speculative fix for multi-profile crash. crbug.com/661821
@@ -47,7 +48,7 @@
 }
 
 bool CanShowWindowForUser(
-    aura::Window* window,
+    const aura::Window* window,
     const GetActiveBrowserContextCallback& get_context_callback) {
   DCHECK(window);
   if (user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1u) {
diff --git a/chrome/browser/ui/ash/session_util.h b/chrome/browser/ui/ash/session_util.h
index 0d95a7b..0b0f9a0 100644
--- a/chrome/browser/ui/ash/session_util.h
+++ b/chrome/browser/ui/ash/session_util.h
@@ -30,7 +30,7 @@
 // scenario. This is passed in because it can differ in tests vs. production.
 // See for example MultiUserWindowManagerTestChromeOS.
 bool CanShowWindowForUser(
-    aura::Window* window,
+    const aura::Window* window,
     const GetActiveBrowserContextCallback& get_context_callback);
 
 gfx::ImageSkia GetAvatarImageForContext(content::BrowserContext* context);
diff --git a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc
index a0d34f1..273ad22d 100644
--- a/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc
+++ b/chrome/browser/ui/bloated_renderer/bloated_renderer_tab_helper.cc
@@ -132,25 +132,13 @@
   }
 
   if (CanReloadBloatedTab()) {
-    const size_t expected_page_count = 1u;
-    const bool skip_unload_handlers = true;
-    content::RenderProcessHost* renderer =
-        web_contents()->GetMainFrame()->GetProcess();
-    if (renderer->FastShutdownIfPossible(expected_page_count,
-                                         skip_unload_handlers)) {
-      const bool check_for_repost = true;
-      // Clear the state and the saved navigation id.
-      state_ = State::kRequestingReload;
-      saved_navigation_id_ = 0;
-      web_contents()->GetController().Reload(content::ReloadType::NORMAL,
-                                             check_for_repost);
-      DCHECK_EQ(State::kStartedNavigation, state_);
-      RecordBloatedRendererHandling(
-          BloatedRendererHandlingInBrowser::kReloaded);
-    } else {
-      RecordBloatedRendererHandling(
-          BloatedRendererHandlingInBrowser::kCannotShutdown);
-    }
+    const bool check_for_repost = true;
+    // Clear the state and the saved navigation id.
+    state_ = State::kRequestingReload;
+    saved_navigation_id_ = 0;
+    web_contents()->GetController().Reload(content::ReloadType::NORMAL,
+                                           check_for_repost);
+    RecordBloatedRendererHandling(BloatedRendererHandlingInBrowser::kReloaded);
   } else {
     RecordBloatedRendererHandling(
         BloatedRendererHandlingInBrowser::kCannotReload);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 07a7246..19d7970 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -123,7 +123,6 @@
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/mouse_lock_controller.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
-#include "chrome/browser/ui/fast_unload_controller.h"
 #include "chrome/browser/ui/find_bar/find_bar.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
@@ -146,7 +145,6 @@
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
-#include "chrome/browser/ui/unload_controller.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/browser/ui/window_sizer/window_sizer.h"
@@ -263,12 +261,6 @@
   return BrowserWindow::CreateBrowserWindow(std::move(browser), user_gesture);
 }
 
-// Is the fast tab unload experiment enabled?
-bool IsFastTabUnloadEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableFastUnload);
-}
-
 const extensions::Extension* GetExtensionForOrigin(
     Profile* profile,
     const GURL& security_origin) {
@@ -407,6 +399,7 @@
       initial_show_state_(params.initial_show_state),
       initial_workspace_(params.initial_workspace),
       is_session_restore_(params.is_session_restore),
+      unload_controller_(this),
       content_setting_bubble_model_delegate_(
           new BrowserContentSettingBubbleModelDelegate(this)),
       location_bar_model_delegate_(new BrowserLocationBarModelDelegate(this)),
@@ -427,12 +420,6 @@
   CHECK(!profile_->IsSystemProfile())
       << "The system profile should never have a real browser.";
 
-  // TODO(jeremy): Move to initializer list once flag is removed.
-  if (IsFastTabUnloadEnabled())
-    fast_unload_controller_.reset(new FastUnloadController(this));
-  else
-    unload_controller_.reset(new UnloadController(this));
-
   tab_strip_model_->AddObserver(this);
 
   location_bar_model_.reset(new LocationBarModelImpl(
@@ -737,54 +724,34 @@
   if (result == WarnBeforeClosingResult::kDoNotClose)
     return false;
 
-  if (IsFastTabUnloadEnabled())
-    return fast_unload_controller_->ShouldCloseWindow();
-  return unload_controller_->ShouldCloseWindow();
+  return unload_controller_.ShouldCloseWindow();
 }
 
 bool Browser::TryToCloseWindow(
     bool skip_beforeunload,
     const base::Callback<void(bool)>& on_close_confirmed) {
   cancel_download_confirmation_state_ = RESPONSE_RECEIVED;
-  if (IsFastTabUnloadEnabled()) {
-    return fast_unload_controller_->TryToCloseWindow(skip_beforeunload,
-                                                     on_close_confirmed);
-  }
-  return unload_controller_->TryToCloseWindow(skip_beforeunload,
-                                              on_close_confirmed);
+  return unload_controller_.TryToCloseWindow(skip_beforeunload,
+                                             on_close_confirmed);
 }
 
 void Browser::ResetTryToCloseWindow() {
   cancel_download_confirmation_state_ = NOT_PROMPTED;
-  if (IsFastTabUnloadEnabled())
-    fast_unload_controller_->ResetTryToCloseWindow();
-  else
-    unload_controller_->ResetTryToCloseWindow();
-}
-
-bool Browser::HasCompletedUnloadProcessing() const {
-  DCHECK(IsFastTabUnloadEnabled());
-  return fast_unload_controller_->HasCompletedUnloadProcessing();
+  unload_controller_.ResetTryToCloseWindow();
 }
 
 bool Browser::IsAttemptingToCloseBrowser() const {
-  if (IsFastTabUnloadEnabled())
-    return fast_unload_controller_->is_attempting_to_close_browser();
-  return unload_controller_->is_attempting_to_close_browser();
+  return unload_controller_.is_attempting_to_close_browser();
 }
 
 bool Browser::ShouldRunUnloadListenerBeforeClosing(
     content::WebContents* web_contents) {
-  if (IsFastTabUnloadEnabled())
-    return fast_unload_controller_->ShouldRunUnloadEventsHelper(web_contents);
-  return unload_controller_->ShouldRunUnloadEventsHelper(web_contents);
+  return unload_controller_.ShouldRunUnloadEventsHelper(web_contents);
 }
 
 bool Browser::RunUnloadListenerBeforeClosing(
     content::WebContents* web_contents) {
-  if (IsFastTabUnloadEnabled())
-    return fast_unload_controller_->RunUnloadEventsHelper(web_contents);
-  return unload_controller_->RunUnloadEventsHelper(web_contents);
+  return unload_controller_.RunUnloadEventsHelper(web_contents);
 }
 
 void Browser::SetIsInTabDragging(bool is_in_tab_dragging) {
@@ -826,8 +793,7 @@
 
   BrowserList::NotifyBrowserCloseStarted(this);
 
-  if (!IsFastTabUnloadEnabled())
-    tab_strip_model_->CloseAllTabs();
+  tab_strip_model_->CloseAllTabs();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1162,9 +1128,7 @@
 }
 
 bool Browser::TabsNeedBeforeUnloadFired() {
-  if (IsFastTabUnloadEnabled())
-    return fast_unload_controller_->TabsNeedBeforeUnloadFired();
-  return unload_controller_->TabsNeedBeforeUnloadFired();
+  return unload_controller_.TabsNeedBeforeUnloadFired();
 }
 
 bool Browser::PreHandleGestureEvent(content::WebContents* source,
@@ -1458,13 +1422,7 @@
 }
 
 void Browser::CloseContents(WebContents* source) {
-  bool can_close_contents;
-  if (IsFastTabUnloadEnabled())
-    can_close_contents = fast_unload_controller_->CanCloseContents(source);
-  else
-    can_close_contents = unload_controller_->CanCloseContents(source);
-
-  if (can_close_contents)
+  if (unload_controller_.CanCloseContents(source))
     chrome::CloseWebContents(this, source, true);
 }
 
@@ -1519,14 +1477,8 @@
         proceed, proceed_to_fire_unload))
     return;
 
-  if (IsFastTabUnloadEnabled()) {
-    *proceed_to_fire_unload =
-        fast_unload_controller_->BeforeUnloadFiredForContents(web_contents,
-                                                              proceed);
-  } else {
-    *proceed_to_fire_unload =
-        unload_controller_->BeforeUnloadFired(web_contents, proceed);
-  }
+  *proceed_to_fire_unload =
+      unload_controller_.BeforeUnloadFired(web_contents, proceed);
 }
 
 bool Browser::ShouldFocusLocationBarByDefault(WebContents* source) {
@@ -2518,11 +2470,7 @@
     case WarnBeforeClosingResult::kDoNotClose:
       // Reset UnloadController::is_attempting_to_close_browser_ so that we
       // don't prompt every time any tab is closed. http://crbug.com/305516
-      if (IsFastTabUnloadEnabled())
-        fast_unload_controller_->CancelWindowClose();
-      else
-        unload_controller_->CancelWindowClose();
-      break;
+      unload_controller_.CancelWindowClose();
   }
 }
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 26510e18..a9ff3fec 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -31,6 +31,7 @@
 #include "chrome/browser/ui/profile_chooser_constants.h"
 #include "chrome/browser/ui/signin_view_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "chrome/browser/ui/unload_controller.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/omnibox/browser/location_bar_model.h"
@@ -65,14 +66,12 @@
 class BrowserLocationBarModelDelegate;
 class BrowserLiveTabContext;
 class BrowserWindow;
-class FastUnloadController;
 class FindBarController;
 class Profile;
 class ScopedKeepAlive;
 class StatusBubble;
 class TabStripModel;
 class TabStripModelDelegate;
-class UnloadController;
 
 namespace chrome {
 class BrowserCommandController;
@@ -400,9 +399,6 @@
   // It starts beforeunload/unload processing as a side-effect.
   bool TabsNeedBeforeUnloadFired();
 
-  // Returns true if all tabs' beforeunload/unload events have fired.
-  bool HasCompletedUnloadProcessing() const;
-
   bool IsAttemptingToCloseBrowser() const;
 
   // Invoked when the window containing us is closing. Performs the necessary
@@ -1028,8 +1024,7 @@
   // Tracks when this browser is being created by session restore.
   bool is_session_restore_;
 
-  std::unique_ptr<UnloadController> unload_controller_;
-  std::unique_ptr<FastUnloadController> fast_unload_controller_;
+  UnloadController unload_controller_;
 
   std::unique_ptr<ChromeBubbleManager> bubble_manager_;
 
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index f117127..65298069 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -727,7 +727,7 @@
     // retrieve the login scope token without touching any profiles. This
     // option is only available on Windows for use with Google Credential
     // Provider for Windows.
-    return signin::GetSigninReasonForPromoURL(url) ==
+    return signin::GetSigninReasonForEmbeddedPromoURL(url) ==
            signin_metrics::Reason::REASON_FETCH_LST_ONLY;
 #else
     return false;
diff --git a/chrome/browser/ui/browser_tab_strip_model_delegate.cc b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
index 823a189..f68aa2a 100644
--- a/chrome/browser/ui/browser_tab_strip_model_delegate.cc
+++ b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/fast_unload_controller.h"
 #include "chrome/browser/ui/tab_helpers.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/unload_controller.h"
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index b092a3e..bbcb6e7 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -455,6 +455,9 @@
   virtual std::string GetWorkspace() const = 0;
   virtual bool IsVisibleOnAllWorkspaces() const = 0;
 
+  // Shows the platform specific emoji picker.
+  virtual void ShowEmojiPanel() = 0;
+
  protected:
   friend class BrowserCloseManager;
   friend class BrowserView;
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index b35591a..e2e857a4 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -73,12 +73,6 @@
   ShowSingletonTabOverwritingNTP(browser, std::move(params));
 }
 
-void NavigateToSingletonTab(Browser* browser, const GURL& url) {
-  NavigateParams params(GetSingletonTabNavigateParams(browser, url));
-  params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
-  ShowSingletonTabOverwritingNTP(browser, std::move(params));
-}
-
 // Shows either the help app or the appropriate help page for |source|. If
 // |browser| is NULL and the help page is used (vs the app), the help page is
 // shown in the last active browser. If there is no such browser, a new browser
@@ -396,45 +390,22 @@
 
 #if defined(OS_CHROMEOS)
   // ChromeOS always loads the chrome://chrome-signin in a tab.
-  const bool show_full_tab_chrome_signin_page = true;
+  GURL url = signin::GetEmbeddedPromoURLForTab(
+      access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
+      false);
+  NavigateParams params(GetSingletonTabNavigateParams(browser, url));
+  params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
+  ShowSingletonTabOverwritingNTP(browser, std::move(params));
+  DCHECK_GT(browser->tab_strip_model()->count(), 0);
 #else
-  // When Desktop Identity Consistency (aka DICE) is not enabled, Chrome uses
-  // a modal sign-in dialog for signing in. This sign-in modal dialog is
-  // presented as a tab-modal dialog (which is automatically dismissed when
-  // the page navigates). Displaying the dialog on a new tab that loads any
-  // page will lead to it being dismissed as soon as the new tab is loaded.
-  // So the sign-in dialog must only be presented on top of an existing tab.
-  //
-  // If ScopedTabbedBrowserDisplayer had to create a (non-incognito) Browser*,
-  // it won't have any tabs yet. Fallback to the full-tab sign-in flow in this
-  // case.
-  const bool show_full_tab_chrome_signin_page =
-      !signin::DiceMethodGreaterOrEqual(
-          AccountConsistencyModeManager::GetMethodForProfile(
-              browser->profile()),
-          signin::AccountConsistencyMethod::kDiceMigration) &&
-      browser->tab_strip_model()->empty();
-#endif  // defined(OS_CHROMEOS)
-  if (show_full_tab_chrome_signin_page) {
-    NavigateToSingletonTab(
-        browser,
-        signin::GetPromoURLForTab(
-            access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-            false));
-    DCHECK_GT(browser->tab_strip_model()->count(), 0);
-  } else {
-#if defined(OS_CHROMEOS)
-    NOTREACHED();
-#else
-    profiles::BubbleViewMode bubble_view_mode =
-        IdentityManagerFactory::GetForProfile(original_profile)
-                ->HasPrimaryAccount()
-            ? profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
-            : profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN;
-    browser->signin_view_controller()->ShowSignin(bubble_view_mode, browser,
-                                                  access_point);
+  profiles::BubbleViewMode bubble_view_mode =
+      IdentityManagerFactory::GetForProfile(original_profile)
+              ->HasPrimaryAccount()
+          ? profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH
+          : profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN;
+  browser->signin_view_controller()->ShowSignin(bubble_view_mode, browser,
+                                                access_point);
 #endif
-  }
 }
 
 void ShowBrowserSigninOrSettings(Browser* browser,
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index bcf93e4..a24db146 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -245,8 +245,10 @@
     contents = params.navigated_or_inserted_contents;
   }
 
-  web_app::WebAppTabHelperBase::FromWebContents(contents)->SetAppId(
-      extension->id());
+  web_app::WebAppTabHelperBase* tab_helper =
+      web_app::WebAppTabHelperBase::FromWebContents(contents);
+  DCHECK(tab_helper);
+  tab_helper->SetAppId(extension->id());
 
 #if defined(OS_CHROMEOS)
   // In ash, LAUNCH_FULLSCREEN launches in the OpenApplicationWindow function
@@ -392,8 +394,10 @@
   extensions::HostedAppBrowserController::SetAppPrefsForWebContents(
       browser->hosted_app_controller(), web_contents);
   if (extension) {
-    web_app::WebAppTabHelperBase::FromWebContents(web_contents)
-        ->SetAppId(extension->id());
+    web_app::WebAppTabHelperBase* tab_helper =
+        web_app::WebAppTabHelperBase::FromWebContents(web_contents);
+    DCHECK(tab_helper);
+    tab_helper->SetAppId(extension->id());
   }
 
   browser->window()->Show();
diff --git a/chrome/browser/ui/fast_unload_controller.cc b/chrome/browser/ui/fast_unload_controller.cc
deleted file mode 100644
index 5b5b22f..0000000
--- a/chrome/browser/ui/fast_unload_controller.cc
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright 2013 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/fast_unload_controller.h"
-
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/devtools/devtools_window.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/buildflags/buildflags.h"
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
-#endif  // (ENABLE_EXTENSIONS)
-
-////////////////////////////////////////////////////////////////////////////////
-// FastUnloadController, public:
-
-FastUnloadController::FastUnloadController(Browser* browser)
-    : browser_(browser),
-      tab_needing_before_unload_ack_(NULL),
-      is_attempting_to_close_browser_(false),
-      weak_factory_(this) {
-  browser_->tab_strip_model()->AddObserver(this);
-}
-
-FastUnloadController::~FastUnloadController() {
-  browser_->tab_strip_model()->RemoveObserver(this);
-  web_contents_waiting_for_deletion_.clear();
-}
-
-bool FastUnloadController::CanCloseContents(content::WebContents* contents) {
-  // Don't try to close the tab when the whole browser is being closed, since
-  // that avoids the fast shutdown path where we just kill all the renderers.
-  return !is_attempting_to_close_browser_ ||
-      is_calling_before_unload_handlers();
-}
-
-bool FastUnloadController::ShouldRunUnloadEventsHelper(
-    content::WebContents* contents) {
-  // If |contents| is being inspected, devtools needs to intercept beforeunload
-  // events.
-  return DevToolsWindow::GetInstanceForInspectedWebContents(contents) != NULL;
-}
-
-bool FastUnloadController::RunUnloadEventsHelper(
-    content::WebContents* contents) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // Don't run for extensions that are disabled or uninstalled; the tabs will
-  // be killed if they make any network requests, and the extension shouldn't
-  // be doing any work if it's removed.
-  GURL url = contents->GetLastCommittedURL();
-  if (url.SchemeIs(extensions::kExtensionScheme) &&
-      !extensions::ExtensionRegistry::Get(browser_->profile())
-           ->enabled_extensions()
-           .GetExtensionOrAppByURL(url)) {
-    return false;
-  }
-#endif  // (ENABLE_EXTENSIONS)
-
-  // Special case for when we quit an application. The Devtools window can
-  // close if it's beforeunload event has already fired which will happen due
-  // to the interception of it's content's beforeunload.
-  if (browser_->is_devtools() &&
-      DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(browser_))
-    return false;
-
-  // If there's a devtools window attached to |contents|,
-  // we would like devtools to call its own beforeunload handlers first,
-  // and then call beforeunload handlers for |contents|.
-  // See DevToolsWindow::InterceptPageBeforeUnload for details.
-  if (DevToolsWindow::InterceptPageBeforeUnload(contents)) {
-    return true;
-  }
-  // If the WebContents is not connected yet, then there's no unload
-  // handler we can fire even if the WebContents has an unload listener.
-  // One case where we hit this is in a tab that has an infinite loop
-  // before load.
-  if (contents->NeedToFireBeforeUnload()) {
-    // If the page has unload listeners, then we tell the renderer to fire
-    // them. Once they have fired, we'll get a message back saying whether
-    // to proceed closing the page or not, which sends us back to this method
-    // with the NeedToFireBeforeUnload bit cleared.
-    contents->DispatchBeforeUnload(false /* auto_cancel */);
-    return true;
-  }
-  return false;
-}
-
-bool FastUnloadController::BeforeUnloadFiredForContents(
-    content::WebContents* contents,
-    bool proceed) {
-  if (!proceed)
-    DevToolsWindow::OnPageCloseCanceled(contents);
-
-  if (!is_attempting_to_close_browser_) {
-    if (!proceed) {
-      contents->SetClosedByUserGesture(false);
-    } else {
-      // No more dialogs are possible, so remove the tab and finish
-      // running unload listeners asynchrounously.
-      browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents);
-      DetachWebContents(contents);
-    }
-    return proceed;
-  }
-
-  if (!proceed) {
-    CancelWindowClose();
-    contents->SetClosedByUserGesture(false);
-    return false;
-  }
-
-  if (tab_needing_before_unload_ack_ == contents) {
-    // Now that beforeunload has fired, queue the tab to fire unload.
-    tab_needing_before_unload_ack_ = NULL;
-    tabs_needing_unload_.insert(contents);
-    ProcessPendingTabs(false);
-    // We want to handle firing the unload event ourselves since we want to
-    // fire all the beforeunload events before attempting to fire the unload
-    // events should the user cancel closing the browser.
-    return false;
-  }
-
-  return true;
-}
-
-bool FastUnloadController::ShouldCloseWindow() {
-  if (HasCompletedUnloadProcessing())
-    return true;
-
-  // Special case for when we quit an application. The Devtools window can
-  // close if it's beforeunload event has already fired which will happen due
-  // to the interception of it's content's beforeunload.
-  if (browser_->is_devtools() &&
-      DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(browser_)) {
-    return true;
-  }
-
-  // The behavior followed here varies based on the current phase of the
-  // operation and whether a batched shutdown is in progress.
-  //
-  // If there are tabs with outstanding beforeunload handlers:
-  // 1. If a batched shutdown is in progress: return false.
-  //    This is to prevent interference with batched shutdown already in
-  //    progress.
-  // 2. Otherwise: start sending beforeunload events and return false.
-  //
-  // Otherwise, If there are no tabs with outstanding beforeunload handlers:
-  // 3. If a batched shutdown is in progress: start sending unload events and
-  //    return false.
-  // 4. Otherwise: return true.
-  is_attempting_to_close_browser_ = true;
-  // Cases 1 and 4.
-  bool need_beforeunload_fired = TabsNeedBeforeUnloadFired();
-  if (need_beforeunload_fired == is_calling_before_unload_handlers())
-    return !need_beforeunload_fired;
-
-  // Cases 2 and 3.
-  on_close_confirmed_.Reset();
-  ProcessPendingTabs(false);
-  return false;
-}
-
-bool FastUnloadController::TryToCloseWindow(
-    bool skip_beforeunload,
-    const base::Callback<void(bool)>& on_close_confirmed) {
-  // The devtools browser gets its beforeunload events as the results of
-  // intercepting events from the inspected tab, so don't send them here as
-  // well.
-  if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired())
-    return false;
-
-  on_close_confirmed_ = on_close_confirmed;
-  is_attempting_to_close_browser_ = true;
-  ProcessPendingTabs(skip_beforeunload);
-  return !skip_beforeunload;
-}
-
-void FastUnloadController::ResetTryToCloseWindow() {
-  if (!is_calling_before_unload_handlers())
-    return;
-  CancelWindowClose();
-}
-
-bool FastUnloadController::TabsNeedBeforeUnloadFired() {
-  if (!tabs_needing_before_unload_.empty() ||
-      tab_needing_before_unload_ack_ != NULL)
-    return true;
-
-  if (!is_calling_before_unload_handlers() && !tabs_needing_unload_.empty())
-    return false;
-
-  for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) {
-    content::WebContents* contents =
-        browser_->tab_strip_model()->GetWebContentsAt(i);
-    bool should_fire_beforeunload = contents->NeedToFireBeforeUnload() ||
-        DevToolsWindow::NeedsToInterceptBeforeUnload(contents);
-    if (!ContainsKey(tabs_needing_unload_, contents) &&
-        !ContainsKey(tabs_needing_unload_ack_, contents) &&
-        tab_needing_before_unload_ack_ != contents &&
-        should_fire_beforeunload)
-      tabs_needing_before_unload_.insert(contents);
-  }
-  return !tabs_needing_before_unload_.empty();
-}
-
-bool FastUnloadController::HasCompletedUnloadProcessing() const {
-  return is_attempting_to_close_browser_ &&
-      tabs_needing_before_unload_.empty() &&
-      tab_needing_before_unload_ack_ == NULL &&
-      tabs_needing_unload_.empty() &&
-      tabs_needing_unload_ack_.empty();
-}
-
-void FastUnloadController::CancelTabNeedingBeforeUnloadAck() {
-  if (tab_needing_before_unload_ack_ != NULL) {
-    DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_);
-    tab_needing_before_unload_ack_ = NULL;
-  }
-}
-
-void FastUnloadController::CancelWindowClose() {
-  // Closing of window can be canceled from a beforeunload handler.
-  DCHECK(is_attempting_to_close_browser_);
-  tabs_needing_before_unload_.clear();
-  CancelTabNeedingBeforeUnloadAck();
-  for (auto it = tabs_needing_unload_.begin(); it != tabs_needing_unload_.end();
-       it++) {
-    content::WebContents* contents = *it;
-    DevToolsWindow::OnPageCloseCanceled(contents);
-  }
-  tabs_needing_unload_.clear();
-
-  // No need to clear tabs_needing_unload_ack_. Those tabs are already detached.
-
-  if (is_calling_before_unload_handlers()) {
-    base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_;
-    on_close_confirmed_.Reset();
-    on_close_confirmed.Run(false);
-  }
-
-  is_attempting_to_close_browser_ = false;
-
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
-      content::Source<Browser>(browser_),
-      content::NotificationService::NoDetails());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FastUnloadController, content::WebContentsDelegate implementation:
-
-bool FastUnloadController::ShouldSuppressDialogs(content::WebContents* source) {
-  return true;
-}
-
-void FastUnloadController::CloseContents(content::WebContents* source) {
-  auto it = web_contents_waiting_for_deletion_.find(source);
-  DCHECK(it != web_contents_waiting_for_deletion_.end());
-  web_contents_waiting_for_deletion_.erase(it);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FastUnloadController, content::NotificationObserver implementation:
-
-void FastUnloadController::Observe(
-      int type,
-      const content::NotificationSource& source,
-      const content::NotificationDetails& details) {
-  DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, type);
-
-  registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
-                    source);
-  ClearUnloadState(content::Source<content::WebContents>(source).ptr());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FastUnloadController, TabStripModelObserver implementation:
-
-void FastUnloadController::OnTabStripModelChanged(
-    TabStripModel* tab_strip_model,
-    const TabStripModelChange& change,
-    const TabStripSelectionChange& selection) {
-  if (change.type() != TabStripModelChange::kInserted &&
-      change.type() != TabStripModelChange::kRemoved &&
-      change.type() != TabStripModelChange::kReplaced)
-    return;
-
-  for (const auto& delta : change.deltas()) {
-    content::WebContents* new_contents = nullptr;
-    content::WebContents* old_contents = nullptr;
-    if (change.type() == TabStripModelChange::kInserted) {
-      new_contents = delta.insert.contents;
-    } else if (change.type() == TabStripModelChange::kReplaced) {
-      new_contents = delta.replace.new_contents;
-      old_contents = delta.replace.old_contents;
-    } else {
-      old_contents = delta.remove.contents;
-    }
-
-    if (old_contents)
-      TabDetachedImpl(old_contents);
-    if (new_contents)
-      TabAttachedImpl(new_contents);
-  }
-}
-
-void FastUnloadController::TabStripEmpty() {
-  // Set is_attempting_to_close_browser_ here, so that extensions, etc, do not
-  // attempt to add tabs to the browser before it closes.
-  is_attempting_to_close_browser_ = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// FastUnloadController, private:
-
-void FastUnloadController::TabAttachedImpl(content::WebContents* contents) {
-  // If the tab crashes in the beforeunload or unload handler, it won't be
-  // able to ack. But we know we can close it.
-  registrar_.Add(
-      this,
-      content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
-      content::Source<content::WebContents>(contents));
-}
-
-void FastUnloadController::TabDetachedImpl(content::WebContents* contents) {
-  if (tabs_needing_unload_ack_.find(contents) !=
-      tabs_needing_unload_ack_.end()) {
-    // Tab needs unload to complete.
-    // It will send |NOTIFICATION_WEB_CONTENTS_DISCONNECTED| when done.
-    return;
-  }
-
-  // If WEB_CONTENTS_DISCONNECTED was received then the notification may have
-  // already been unregistered.
-  const content::NotificationSource& source =
-      content::Source<content::WebContents>(contents);
-  if (registrar_.IsRegistered(this,
-                              content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
-                              source)) {
-    registrar_.Remove(this,
-                      content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
-                      source);
-  }
-
-  if (is_attempting_to_close_browser_)
-    ClearUnloadState(contents);
-}
-
-bool FastUnloadController::DetachWebContents(content::WebContents* contents) {
-  int index = browser_->tab_strip_model()->GetIndexOfWebContents(contents);
-  if (index != TabStripModel::kNoTab &&
-      contents->NeedToFireBeforeUnload()) {
-    tabs_needing_unload_ack_.insert(contents);
-    web_contents_waiting_for_deletion_[contents] =
-        browser_->tab_strip_model()->DetachWebContentsAt(index);
-    contents->SetDelegate(this);
-    return true;
-  }
-  return false;
-}
-
-void FastUnloadController::ProcessPendingTabs(bool skip_beforeunload) {
-  if (!is_attempting_to_close_browser_) {
-    // Because we might invoke this after a delay it's possible for the value of
-    // is_attempting_to_close_browser_ to have changed since we scheduled the
-    // task.
-    return;
-  }
-
-  if (tab_needing_before_unload_ack_ != NULL) {
-    if (skip_beforeunload) {
-      // Cancel and skip the ongoing before unload event.
-      tabs_needing_before_unload_.insert(tab_needing_before_unload_ack_);
-      CancelTabNeedingBeforeUnloadAck();
-    } else {
-      // Wait for |BeforeUnloadFiredForContents| before proceeding.
-      return;
-    }
-  }
-
-  // Process a beforeunload handler.
-  if (!tabs_needing_before_unload_.empty()) {
-    if (skip_beforeunload) {
-      tabs_needing_unload_.insert(tabs_needing_before_unload_.begin(),
-                                  tabs_needing_before_unload_.end());
-      tabs_needing_before_unload_.clear();
-    } else {
-      auto it = tabs_needing_before_unload_.begin();
-      content::WebContents* contents = *it;
-      tabs_needing_before_unload_.erase(it);
-      // Null check render_view_host here as this gets called on a PostTask and
-      // the tab's render_view_host may have been nulled out.
-      if (contents->GetRenderViewHost()) {
-        tab_needing_before_unload_ack_ = contents;
-
-        // If there's a devtools window attached to |contents|,
-        // we would like devtools to call its own beforeunload handlers first,
-        // and then call beforeunload handlers for |contents|.
-        // See DevToolsWindow::InterceptPageBeforeUnload for details.
-        if (!DevToolsWindow::InterceptPageBeforeUnload(contents))
-          contents->DispatchBeforeUnload(false /* auto_cancel */);
-      } else {
-        ProcessPendingTabs(skip_beforeunload);
-      }
-      return;
-    }
-  }
-
-  if (is_calling_before_unload_handlers()) {
-    base::OnceCallback<void(bool)> on_close_confirmed = on_close_confirmed_;
-    // Reset |on_close_confirmed_| in case the callback tests
-    // |is_calling_before_unload_handlers()|, we want to return that calling
-    // is complete.
-    if (tabs_needing_unload_.empty())
-      on_close_confirmed_.Reset();
-    if (!skip_beforeunload)
-      std::move(on_close_confirmed).Run(true);
-    return;
-  }
-
-  // Process all the unload handlers. (The beforeunload handlers have finished.)
-  if (!tabs_needing_unload_.empty()) {
-    browser_->OnWindowClosing();
-
-    // Run unload handlers detached since no more interaction is possible.
-    auto it = tabs_needing_unload_.begin();
-    while (it != tabs_needing_unload_.end()) {
-      auto current = it++;
-      content::WebContents* contents = *current;
-      tabs_needing_unload_.erase(current);
-      // Null check render_view_host here as this gets called on a PostTask
-      // and the tab's render_view_host may have been nulled out.
-      if (contents->GetRenderViewHost()) {
-        DetachWebContents(contents);
-        contents->ClosePage();
-      }
-    }
-
-    // Get the browser hidden.
-    if (browser_->tab_strip_model()->empty()) {
-      browser_->TabStripEmpty();
-    } else {
-      browser_->tab_strip_model()->CloseAllTabs();  // tabs not needing unload
-    }
-    return;
-  }
-
-  if (HasCompletedUnloadProcessing()) {
-    browser_->OnWindowClosing();
-
-    // Get the browser closed.
-    if (browser_->tab_strip_model()->empty()) {
-      browser_->TabStripEmpty();
-    } else {
-      // There may be tabs if the last tab needing beforeunload crashed.
-      browser_->tab_strip_model()->CloseAllTabs();
-    }
-    return;
-  }
-}
-
-void FastUnloadController::ClearUnloadState(content::WebContents* contents) {
-  if (tabs_needing_unload_ack_.erase(contents) > 0) {
-    if (HasCompletedUnloadProcessing())
-      PostTaskForProcessPendingTabs();
-    return;
-  }
-
-  if (!is_attempting_to_close_browser_)
-    return;
-
-  if (tab_needing_before_unload_ack_ == contents) {
-    tab_needing_before_unload_ack_ = NULL;
-    PostTaskForProcessPendingTabs();
-    return;
-  }
-
-  if (tabs_needing_before_unload_.erase(contents) > 0 ||
-      tabs_needing_unload_.erase(contents) > 0) {
-    if (tab_needing_before_unload_ack_ == NULL)
-      PostTaskForProcessPendingTabs();
-  }
-}
-
-void FastUnloadController::PostTaskForProcessPendingTabs() {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&FastUnloadController::ProcessPendingTabs,
-                                weak_factory_.GetWeakPtr(), false));
-}
diff --git a/chrome/browser/ui/fast_unload_controller.h b/chrome/browser/ui/fast_unload_controller.h
deleted file mode 100644
index 2b5f37e3..0000000
--- a/chrome/browser/ui/fast_unload_controller.h
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2013 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_FAST_UNLOAD_CONTROLLER_H_
-#define CHROME_BROWSER_UI_FAST_UNLOAD_CONTROLLER_H_
-
-#include <memory>
-#include <set>
-#include <unordered_map>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string_piece.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/web_contents_delegate.h"
-
-class Browser;
-class TabStripModel;
-
-namespace content {
-class NotificationSource;
-class NotificationDetails;
-class WebContents;
-}
-
-// FastUnloadController manages closing tabs and windows -- especially in
-// regards to beforeunload handlers (have proceed/cancel dialogs) and
-// unload handlers (have no user interaction).
-//
-// Typical flow of closing a tab:
-//  1. Browser calls CanCloseContents().
-//     If true, browser calls contents::CloseWebContents().
-//  2. WebContents notifies us via its delegate and BeforeUnloadFired()
-//     that the beforeunload handler was run. If the user allowed the
-//     close to continue, we detached the tab and hold onto it while the
-//     close finishes.
-//
-// Typical flow of closing a window:
-//  1. BrowserView::CanClose() calls TabsNeedBeforeUnloadFired().
-//     If beforeunload/unload handlers need to run, FastUnloadController returns
-//     true and calls ProcessPendingTabs() (private method).
-//  2. For each tab with a beforeunload/unload handler, ProcessPendingTabs()
-//        calls |web_contents->GetRenderViewHost()->FirePageBeforeUnload()|.
-//  3. If the user allowed the close to continue, we detach all the tabs with
-//     unload handlers, remove them from the tab strip, and finish closing
-//     the tabs in the background.
-//  4. The browser gets notified that the tab strip is empty and calls
-//     CloseFrame where the empty tab strip causes the window to hide.
-//     Once the detached tabs finish, the browser calls CloseFrame again and
-//     the window is finally closed.
-//
-class FastUnloadController : public content::NotificationObserver,
-                             public TabStripModelObserver,
-                             public content::WebContentsDelegate {
- public:
-  explicit FastUnloadController(Browser* browser);
-  ~FastUnloadController() override;
-
-  // Returns true if |contents| can be cleanly closed. When |browser_| is being
-  // closed, this function will return false to indicate |contents| should not
-  // be cleanly closed, since the fast shutdown path will just kill its
-  // renderer.
-  bool CanCloseContents(content::WebContents* contents);
-
-  // Returns true if we need to run unload events for the |contents|.
-  bool ShouldRunUnloadEventsHelper(content::WebContents* contents);
-
-  // Helper function to run beforeunload listeners on a WebContents.
-  // Returns true if |contents| beforeunload listeners were invoked.
-  bool RunUnloadEventsHelper(content::WebContents* contents);
-
-  // Called when a BeforeUnload handler is fired for |contents|. |proceed|
-  // indicates the user's response to the Y/N BeforeUnload handler dialog. If
-  // this parameter is false, any pending attempt to close the whole browser
-  // will be canceled. Returns true if Unload handlers should be fired. When the
-  // |browser_| is being closed, Unload handlers for any particular WebContents
-  // will not be run until every WebContents being closed has a chance to run
-  // its BeforeUnloadHandler.
-  bool BeforeUnloadFiredForContents(content::WebContents* contents,
-                                    bool proceed);
-
-  bool is_attempting_to_close_browser() const {
-    return is_attempting_to_close_browser_;
-  }
-
-  // Called in response to a request to close |browser_|'s window. Returns true
-  // when there are no remaining beforeunload handlers to be run.
-  bool ShouldCloseWindow();
-
-  // Begins the process of confirming whether the associated browser can be
-  // closed. Beforeunload events won't be fired if |skip_beforeunload|
-  // true.
-  bool TryToCloseWindow(bool skip_beforeunload,
-                        const base::Callback<void(bool)>& on_close_confirmed);
-
-  // Clears the results of any beforeunload confirmation dialogs triggered by a
-  // TryToCloseWindow call.
-  void ResetTryToCloseWindow();
-
-  // Returns true if |browser_| has any tabs that have BeforeUnload handlers
-  // that have not been fired. This method is non-const because it builds a list
-  // of tabs that need their BeforeUnloadHandlers fired.
-  // TODO(beng): This seems like it could be private but it is used by
-  //             AreAllBrowsersCloseable() in application_lifetime.cc. It seems
-  //             very similar to ShouldCloseWindow() and some consolidation
-  //             could be pursued.
-  bool TabsNeedBeforeUnloadFired();
-
-  // Returns true if all tabs' beforeunload/unload events have fired.
-  bool HasCompletedUnloadProcessing() const;
-
-  // Clears all the state associated with processing tabs' beforeunload/unload
-  // events since the user cancelled closing the window.
-  void CancelWindowClose();
-
- private:
-  // Overridden from content::WebContentsDelegate
-  bool ShouldSuppressDialogs(content::WebContents* source) override;
-  void CloseContents(content::WebContents* source) override;
-
-  // Overridden from content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
-  // Overridden from TabStripModelObserver:
-  void OnTabStripModelChanged(
-      TabStripModel* tab_strip_model,
-      const TabStripModelChange& change,
-      const TabStripSelectionChange& selection) override;
-  void TabStripEmpty() override;
-
-  void TabAttachedImpl(content::WebContents* contents);
-  void TabDetachedImpl(content::WebContents* contents);
-
-  // Detach |contents| and wait for it to finish closing.
-  // The close must be inititiated outside of this method.
-  // Returns true if it succeeds.
-  bool DetachWebContents(content::WebContents* contents);
-
-  // Processes the next tab that needs its beforeunload/unload event fired.
-  void ProcessPendingTabs(bool skip_beforeunload);
-
-  // Cleans up state appropriately when we are trying to close the
-  // browser or close a tab in the background. We also use this in the
-  // cases where a tab crashes or hangs even if the
-  // beforeunload/unload haven't successfully fired.
-  void ClearUnloadState(content::WebContents* contents);
-
-  // Helper for |ClearUnloadState| to unwind stack before proceeding.
-  void PostTaskForProcessPendingTabs();
-
-  // Log a step of the unload processing.
-  void LogUnloadStep(const base::StringPiece& step_name,
-                     content::WebContents* contents) const;
-
-  void CancelTabNeedingBeforeUnloadAck();
-
-  bool is_calling_before_unload_handlers() {
-    return !on_close_confirmed_.is_null();
-  }
-
-  Browser* const browser_;
-
-  content::NotificationRegistrar registrar_;
-
-  typedef std::set<content::WebContents*> WebContentsSet;
-
-  // Tracks tabs that need their beforeunload event started.
-  // Only gets populated when we try to close the browser.
-  WebContentsSet tabs_needing_before_unload_;
-
-  // Tracks the tab that needs its beforeunload event result.
-  // Only gets populated when we try to close the browser.
-  content::WebContents* tab_needing_before_unload_ack_;
-
-  // Tracks tabs that need their unload event started.
-  // Only gets populated when we try to close the browser.
-  WebContentsSet tabs_needing_unload_;
-
-  // Tracks tabs that need to finish running their unload event.
-  // Populated both when closing individual tabs and when closing the browser.
-  WebContentsSet tabs_needing_unload_ack_;
-
-  // Whether we are processing the beforeunload and unload events of each tab
-  // in preparation for closing the browser. FastUnloadController owns this
-  // state rather than Browser because unload handlers are the only reason that
-  // a Browser window isn't just immediately closed.
-  bool is_attempting_to_close_browser_;
-
-  // A callback to call to report whether the user chose to close all tabs of
-  // |browser_| that have beforeunload event handlers. This is set only if we
-  // are currently confirming that the browser is closable.
-  base::Callback<void(bool)> on_close_confirmed_;
-
-  // This class must wait for a call to WebContentsDelegate::CloseContents
-  // before the WebContents can be deleted.
-  std::unordered_map<content::WebContents*,
-                     std::unique_ptr<content::WebContents>>
-      web_contents_waiting_for_deletion_;
-
-  base::WeakPtrFactory<FastUnloadController> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(FastUnloadController);
-};
-
-#endif  // CHROME_BROWSER_UI_FAST_UNLOAD_CONTROLLER_H_
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 75bb90ad..9972ecb 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -895,7 +895,14 @@
       continue;
 
     auto chosen_objects = context->GetGrantedObjects(origin, origin);
-    for (std::unique_ptr<base::DictionaryValue>& object : chosen_objects) {
+    for (std::unique_ptr<ChooserContextBase::Object>& object : chosen_objects) {
+      // Ignore policy allowed devices until the UI is able to display them
+      // properly.
+      // TODO(https://crbug.com/854329): Remove this condition when the UI is
+      // capable of displaying policy chooser objects.
+      if (object->source == content_settings::SETTING_SOURCE_POLICY)
+        continue;
+
       chosen_object_info_list.push_back(
           std::make_unique<PageInfoUI::ChosenObjectInfo>(ui_info,
                                                          std::move(object)));
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index a55f80ca..4d8dd6e0 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -218,8 +218,8 @@
 
 PageInfoUI::ChosenObjectInfo::ChosenObjectInfo(
     const PageInfo::ChooserUIInfo& ui_info,
-    std::unique_ptr<base::DictionaryValue> object)
-    : ui_info(ui_info), object(std::move(object)) {}
+    std::unique_ptr<ChooserContextBase::Object> chooser_object)
+    : ui_info(ui_info), chooser_object(std::move(chooser_object)) {}
 
 PageInfoUI::ChosenObjectInfo::~ChosenObjectInfo() {}
 
@@ -428,7 +428,7 @@
 base::string16 PageInfoUI::ChosenObjectToUIString(
     const ChosenObjectInfo& object) {
   base::string16 name;
-  object.object->GetString(object.ui_info.ui_name_key, &name);
+  object.chooser_object->value.GetString(object.ui_info.ui_name_key, &name);
   return name;
 }
 
diff --git a/chrome/browser/ui/page_info/page_info_ui.h b/chrome/browser/ui/page_info/page_info_ui.h
index b8354852..d3e14362 100644
--- a/chrome/browser/ui/page_info/page_info_ui.h
+++ b/chrome/browser/ui/page_info/page_info_ui.h
@@ -11,6 +11,7 @@
 
 #include "base/strings/string16.h"
 #include "build/build_config.h"
+#include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/ui/page_info/page_info.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -91,16 +92,17 @@
     bool is_incognito;
   };
 
-  // |ChosenObjectInfo| contains information about a single |object| of a
-  // chooser |type| that the current website has been granted access to.
+  // |ChosenObjectInfo| contains information about a single |chooser_object| of
+  // a chooser |type| that the current website has been granted access to.
   struct ChosenObjectInfo {
-    ChosenObjectInfo(const PageInfo::ChooserUIInfo& ui_info,
-                     std::unique_ptr<base::DictionaryValue> object);
+    ChosenObjectInfo(
+        const PageInfo::ChooserUIInfo& ui_info,
+        std::unique_ptr<ChooserContextBase::Object> chooser_object);
     ~ChosenObjectInfo();
     // |ui_info| for this chosen object type.
     const PageInfo::ChooserUIInfo& ui_info;
-    // The opaque |object| representing the thing the user selected.
-    std::unique_ptr<base::DictionaryValue> object;
+    // The opaque |chooser_object| representing the thing the user selected.
+    std::unique_ptr<ChooserContextBase::Object> chooser_object;
   };
 
   // |IdentityInfo| contains information about the site's identity and
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index e13bbc24..6866d6a 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/at_exit.h"
@@ -407,7 +408,8 @@
 
   ASSERT_EQ(1u, last_chosen_object_info().size());
   const PageInfoUI::ChosenObjectInfo* info = last_chosen_object_info()[0].get();
-  page_info()->OnSiteChosenObjectDeleted(info->ui_info, *info->object);
+  page_info()->OnSiteChosenObjectDeleted(info->ui_info,
+                                         info->chooser_object->value);
 
   EXPECT_FALSE(store->HasDevicePermission(url(), url(), *device_info));
   EXPECT_EQ(0u, last_chosen_object_info().size());
diff --git a/chrome/browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc b/chrome/browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc
index 83be6e4..fbaa21c 100644
--- a/chrome/browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc
+++ b/chrome/browser/ui/passwords/google_password_manager_navigation_throttle_browsertest.cc
@@ -24,8 +24,8 @@
 #include "url/url_canon_stdstring.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "components/signin/core/browser/signin_manager_base.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #endif
 
 namespace {
@@ -78,10 +78,10 @@
 
     std::string username;
 #if defined(OS_CHROMEOS)
-    // In browser tests, the profile may already by authenticated with stub
+    // In browser tests, the profile may already be authenticated with stub
     // account |user_manager::kStubUserEmail|.
-    AccountInfo info = SigninManagerFactory::GetForProfile(profile)
-                           ->GetAuthenticatedAccountInfo();
+    AccountInfo info =
+        IdentityManagerFactory::GetForProfile(profile)->GetPrimaryAccountInfo();
     username = info.email;
 #endif
     if (username.empty())
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller.h b/chrome/browser/ui/passwords/password_generation_popup_controller.h
index d724199e..1716633 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller.h
@@ -8,10 +8,6 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/autofill/autofill_popup_view_delegate.h"
 
-namespace gfx {
-class Range;
-}
-
 class PasswordGenerationPopupController
     : public autofill::AutofillPopupViewDelegate {
  public:
@@ -25,10 +21,6 @@
   // Called by the view when the password was accepted.
   virtual void PasswordAccepted() = 0;
 
-  // Called by the view when the saved passwords link is clicked.
-  // TODO(crbug.com/862269): Remove when "Smart Lock" is gone.
-  virtual void OnSavedPasswordsLinkClicked() = 0;
-
   // Accessors
   virtual GenerationState state() const = 0;
   virtual bool password_selected() const = 0;
@@ -37,8 +29,6 @@
   // Translated strings
   virtual base::string16 SuggestedText() = 0;
   virtual const base::string16& HelpText() = 0;
-  // TODO(crbug.com/862269): Remove when "Smart Lock" is gone.
-  virtual gfx::Range HelpTextLinkRange() = 0;
 
  protected:
   ~PasswordGenerationPopupController() override = default;
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
index e16693e..cf4f6ac 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -37,7 +37,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/range/range.h"
 #include "ui/gfx/text_utils.h"
 
 #if defined(OS_ANDROID)
@@ -224,10 +223,6 @@
   Hide();
 }
 
-void PasswordGenerationPopupControllerImpl::OnSavedPasswordsLinkClicked() {
-  NOTREACHED();
-}
-
 void PasswordGenerationPopupControllerImpl::SetSelectionAtPoint(
     const gfx::Point& point) {
   PasswordSelected(view_->IsPointInPasswordBounds(point));
@@ -307,7 +302,3 @@
 const base::string16& PasswordGenerationPopupControllerImpl::HelpText() {
   return help_text_;
 }
-
-gfx::Range PasswordGenerationPopupControllerImpl::HelpTextLinkRange() {
-  return gfx::Range();
-}
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
index c43e02a..d94b750 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
@@ -100,7 +100,6 @@
   void SelectionCleared() override;
   bool HasSelection() const override;
   void PasswordAccepted() override;
-  void OnSavedPasswordsLinkClicked() override;
   gfx::NativeView container_view() const override;
   gfx::Rect popup_bounds() const override;
   const gfx::RectF& element_bounds() const override;
@@ -117,7 +116,6 @@
   const base::string16& password() const override;
   base::string16 SuggestedText() override;
   const base::string16& HelpText() override;
-  gfx::Range HelpTextLinkRange() override;
 
   base::WeakPtr<PasswordGenerationPopupControllerImpl> GetWeakPtr();
 
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index e8459c5..5a06c0a2 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -530,17 +530,16 @@
   // Get the total number of (non-empty) tiles from the iframe.
   int total_favicons = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
-      iframe, "document.querySelectorAll('.md-favicon').length",
-      &total_favicons));
+      iframe, "document.querySelectorAll('.md-icon').length", &total_favicons));
   // Also get how many of the tiles succeeded and failed in loading their
   // favicon images.
   int succeeded_favicons = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
-      iframe, "document.querySelectorAll('.md-favicon img').length",
+      iframe, "document.querySelectorAll('.md-icon img').length",
       &succeeded_favicons));
   int failed_favicons = 0;
   ASSERT_TRUE(instant_test_utils::GetIntFromJS(
-      iframe, "document.querySelectorAll('.md-favicon.failed-favicon').length",
+      iframe, "document.querySelectorAll('.md-icon.failed-favicon').length",
       &failed_favicons));
   // Check if only one add button exists in the frame. This will be included in
   // the total favicon count.
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc
index 08c12178..b957707d 100644
--- a/chrome/browser/ui/signin_view_controller.cc
+++ b/chrome/browser/ui/signin_view_controller.cc
@@ -119,30 +119,25 @@
 
 #if defined(OS_CHROMEOS)
   ShowModalSigninDialog(mode, browser, access_point);
-#else   // defined(OS_CHROMEOS)
+#else
   Profile* profile = browser->profile();
   signin::AccountConsistencyMethod account_consistency =
       AccountConsistencyModeManager::GetMethodForProfile(profile);
-  if (signin::DiceMethodGreaterOrEqual(
-          account_consistency,
-          signin::AccountConsistencyMethod::kDiceMigration)) {
-    std::string email;
-    if (GetSigninReasonFromMode(mode) ==
-        signin_metrics::Reason::REASON_REAUTHENTICATION) {
-      auto* manager = IdentityManagerFactory::GetForProfile(profile);
-      email = manager->GetPrimaryAccountInfo().email;
-    }
-    signin_metrics::PromoAction promo_action = GetPromoActionForNewAccount(
-        AccountTrackerServiceFactory::GetForProfile(profile),
-        account_consistency);
-    ShowDiceSigninTab(mode, browser, access_point, promo_action, email,
-                      redirect_url);
-  } else {
-    ShowModalSigninDialog(mode, browser, access_point);
+  std::string email;
+  signin_metrics::Reason signin_reason = GetSigninReasonFromMode(mode);
+  if (signin_reason == signin_metrics::Reason::REASON_REAUTHENTICATION) {
+    auto* manager = IdentityManagerFactory::GetForProfile(profile);
+    email = manager->GetPrimaryAccountInfo().email;
   }
-#endif  // defined(OS_CHROMEOS)
+  signin_metrics::PromoAction promo_action = GetPromoActionForNewAccount(
+      AccountTrackerServiceFactory::GetForProfile(profile),
+      account_consistency);
+  ShowDiceSigninTab(browser, signin_reason, access_point, promo_action, email,
+                    redirect_url);
+#endif
 }
 
+#if defined(OS_CHROMEOS)
 void SigninViewController::ShowModalSigninDialog(
     profiles::BubbleViewMode mode,
     Browser* browser,
@@ -159,6 +154,7 @@
   delegate_->AttachDialogManager();
   chrome::RecordDialogCreation(chrome::DialogIdentifier::SIGN_IN);
 }
+#endif  // defined(OS_CHROMEOS)
 
 void SigninViewController::ShowModalSyncConfirmationDialog(Browser* browser) {
   CloseModalSignin();
@@ -206,13 +202,12 @@
 
 #if !defined(OS_CHROMEOS)
 void SigninViewController::ShowDiceSigninTab(
-    profiles::BubbleViewMode mode,
     Browser* browser,
+    signin_metrics::Reason signin_reason,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action,
     const std::string& email,
     const GURL& redirect_url) {
-  signin_metrics::Reason signin_reason = GetSigninReasonFromMode(mode);
   GURL signin_url = signin::GetSigninURLForDice(browser->profile(), email);
   content::WebContents* active_contents = nullptr;
   if (access_point == signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE) {
diff --git a/chrome/browser/ui/signin_view_controller.h b/chrome/browser/ui/signin_view_controller.h
index 4dd0a897..1ae8a53 100644
--- a/chrome/browser/ui/signin_view_controller.h
+++ b/chrome/browser/ui/signin_view_controller.h
@@ -25,6 +25,7 @@
 namespace signin_metrics {
 enum class AccessPoint;
 enum class PromoAction;
+enum class Reason;
 }
 
 // Class responsible for showing and hiding all sign-in related UIs
@@ -49,8 +50,8 @@
 #if !defined(OS_CHROMEOS)
   // Shows the DICE-specific sign-in flow: opens a Gaia sign-in webpage in a new
   // tab attached to |browser|.
-  void ShowDiceSigninTab(profiles::BubbleViewMode mode,
-                         Browser* browser,
+  void ShowDiceSigninTab(Browser* browser,
+                         signin_metrics::Reason signin_reason,
                          signin_metrics::AccessPoint access_point,
                          signin_metrics::PromoAction promo_action,
                          const std::string& email,
@@ -86,6 +87,7 @@
  private:
   friend class login_ui_test_utils::SigninViewControllerTestUtil;
 
+#if defined(OS_CHROMEOS)
   // Shows the signin flow as a tab modal dialog attached to |browser|'s active
   // web contents.
   // |access_point| indicates the access point used to open the Gaia sign in
@@ -93,6 +95,7 @@
   void ShowModalSigninDialog(profiles::BubbleViewMode mode,
                              Browser* browser,
                              signin_metrics::AccessPoint access_point);
+#endif
 
   // Returns the web contents of the modal dialog.
   content::WebContents* GetModalDialogWebContentsForTesting();
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
index fa6cdc0..8720ef2 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
@@ -225,9 +225,9 @@
 
     auto base_url =
         reauth_email_.empty()
-            ? signin::GetPromoURLForDialog(access_point, reason, false)
-            : signin::GetReauthURLWithEmailForDialog(access_point, reason,
-                                                     reauth_email_);
+            ? signin::GetEmbeddedPromoURL(access_point, reason, false)
+            : signin::GetEmbeddedReauthURLWithEmail(access_point, reason,
+                                                    reauth_email_);
     if (!reauth_gaia_id_.empty()) {
       base_url = net::AppendQueryParameter(
           base_url, credential_provider::kValidateGaiaIdSigninPromoParameter,
diff --git a/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.cc b/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.cc
index f472311..da84646 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.cc
@@ -63,6 +63,7 @@
   DCHECK(full_name_.empty());
   bool has_error =
       !user_info->GetString("name", &full_name_) || full_name_.empty();
+  user_info->GetString("picture", &picture_url_);
   WriteResultsIfFinished(has_error);
 }
 
@@ -117,6 +118,10 @@
                         base::Value(mdm_id_token_));
     fetch_result.SetKey(credential_provider::kKeyFullname,
                         base::Value(full_name_));
+    if (!picture_url_.empty()) {
+      fetch_result.SetKey(credential_provider::kKeyPicture,
+                          base::Value(picture_url_));
+    }
     fetch_result.SetKey(credential_provider::kKeyTokenHandle,
                         base::Value(token_handle_));
   }
diff --git a/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.h b/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.h
index 7253bcd..32a6cf2 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.h
+++ b/chrome/browser/ui/startup/credential_provider_signin_info_fetcher_win.h
@@ -69,6 +69,7 @@
 
   std::string token_handle_;
   std::string full_name_;
+  std::string picture_url_;
   std::string mdm_id_token_;
 
   std::unique_ptr<OAuth2AccessTokenFetcher> scoped_access_token_fetcher_;
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 0749f22..70a4299 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -54,6 +54,7 @@
 #include "chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
 #include "chrome/browser/tab_contents/navigation_metrics_recorder.h"
+#include "chrome/browser/complex_tasks/task_tab_helper.h"
 #include "chrome/browser/tracing/navigation_tracing.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
@@ -98,7 +99,6 @@
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h"
 #include "chrome/browser/android/search_permissions/search_geolocation_disclosure_tab_helper.h"
-#include "chrome/browser/android/tasks/task_tab_helper.h"
 #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h"
 #include "chrome/browser/banners/app_banner_manager_android.h"
 #include "chrome/browser/ui/android/context_menu_helper.h"
@@ -261,6 +261,7 @@
           profile));
   TabSpecificContentSettings::CreateForWebContents(web_contents);
   TabUIHelper::CreateForWebContents(web_contents);
+  tasks::TaskTabHelper::CreateForWebContents(web_contents);
   ukm::InitializeSourceUrlRecorderForWebContents(web_contents);
   vr::VrTabHelper::CreateForWebContents(web_contents);
 
@@ -283,7 +284,6 @@
 
   SearchGeolocationDisclosureTabHelper::CreateForWebContents(web_contents);
   SingleTabModeTabHelper::CreateForWebContents(web_contents);
-  tasks::TaskTabHelper::CreateForWebContents(web_contents);
   ViewAndroidHelper::CreateForWebContents(web_contents);
 #else
   BookmarkTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
new file mode 100644
index 0000000..7cec602
--- /dev/null
+++ b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
@@ -0,0 +1,77 @@
+// Copyright 2018 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/tabs/existing_tab_group_sub_menu_model.h"
+
+#include "chrome/browser/ui/tabs/tab_group_data.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+
+constexpr int kFirstCommandIndex =
+    TabStripModel::ContextMenuCommand::CommandLast + 1;
+
+ExistingTabGroupSubMenuModel::ExistingTabGroupSubMenuModel(TabStripModel* model,
+                                                           int context_index)
+    : SimpleMenuModel(this) {
+  model_ = model;
+  context_index_ = context_index;
+  Build();
+}
+
+void ExistingTabGroupSubMenuModel::Build() {
+  // Start command ids after the parent menu's ids to avoid collisions.
+  int group_index = kFirstCommandIndex;
+  for (TabGroupData* group : model_->ListTabGroups()) {
+    if (ShouldShowGroup(model_, context_index_, group)) {
+      AddItem(group_index, group->title());
+    }
+    group_index++;
+  }
+}
+
+bool ExistingTabGroupSubMenuModel::IsCommandIdChecked(int command_id) const {
+  return false;
+}
+
+bool ExistingTabGroupSubMenuModel::IsCommandIdEnabled(int command_id) const {
+  return true;
+}
+
+void ExistingTabGroupSubMenuModel::ExecuteCommand(int command_id,
+                                                  int event_flags) {
+  const int groupId = command_id - kFirstCommandIndex;
+  // TODO(https://crbug.com/922736): If a group has been deleted, groupId may
+  // refer to a different group than it did when the menu was created.
+  DCHECK((size_t)groupId < model_->ListTabGroups().size());
+  model_->ExecuteAddToExistingGroupCommand(context_index_,
+                                           model_->ListTabGroups()[groupId]);
+}
+
+// static
+bool ExistingTabGroupSubMenuModel::ShouldShowSubmenu(TabStripModel* model,
+                                                     int context_index) {
+  for (TabGroupData* group : model->ListTabGroups()) {
+    if (ShouldShowGroup(model, context_index, group)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// static
+bool ExistingTabGroupSubMenuModel::ShouldShowGroup(TabStripModel* model,
+                                                   int context_index,
+                                                   TabGroupData* group) {
+  if (!model->IsTabSelected(context_index)) {
+    if (group != nullptr && group != model->GetTabGroupForTab(context_index)) {
+      return true;
+    }
+  } else {
+    for (int index : model->selection_model().selected_indices()) {
+      if (group != nullptr && group != model->GetTabGroupForTab(index)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
diff --git a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h
new file mode 100644
index 0000000..7901738e
--- /dev/null
+++ b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h
@@ -0,0 +1,50 @@
+// Copyright 2018 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_TABS_EXISTING_TAB_GROUP_SUB_MENU_MODEL_H_
+#define CHROME_BROWSER_UI_TABS_EXISTING_TAB_GROUP_SUB_MENU_MODEL_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/tabs/tab_group_data.h"
+#include "ui/base/models/simple_menu_model.h"
+
+class TabStripModel;
+
+class ExistingTabGroupSubMenuModel : public ui::SimpleMenuModel,
+                                     ui::SimpleMenuModel::Delegate {
+ public:
+  ExistingTabGroupSubMenuModel(TabStripModel* model, int context_index);
+  ~ExistingTabGroupSubMenuModel() override = default;
+
+  bool IsCommandIdChecked(int command_id) const override;
+
+  bool IsCommandIdEnabled(int command_id) const override;
+
+  void ExecuteCommand(int command_id, int event_flags) override;
+
+  // Whether the submenu should be shown in the provided context. True iff
+  // the submenu would show at least one group. Does not assume ownership of
+  // |model|; |model| must outlive this instance.
+  static bool ShouldShowSubmenu(TabStripModel* model, int context_index_);
+
+ private:
+  void Build();
+
+  // Unowned; |model_| must outlive this instance.
+  TabStripModel* model_;
+
+  int context_index_;
+
+  // Whether the submenu should contain the group |group|. True iff at least
+  // one tab that would be affected by the command is not in |group|.
+  static bool ShouldShowGroup(TabStripModel* model,
+                              int context_index,
+                              TabGroupData* group);
+
+  DISALLOW_COPY_AND_ASSIGN(ExistingTabGroupSubMenuModel);
+};
+
+#endif  // CHROME_BROWSER_UI_TABS_EXISTING_TAB_GROUP_SUB_MENU_MODEL_H_
diff --git a/chrome/browser/ui/tabs/tab_group_data.cc b/chrome/browser/ui/tabs/tab_group_data.cc
index 745c6d3..aced56e 100644
--- a/chrome/browser/ui/tabs/tab_group_data.cc
+++ b/chrome/browser/ui/tabs/tab_group_data.cc
@@ -7,7 +7,9 @@
 #include "third_party/skia/include/utils/SkRandom.h"
 
 TabGroupData::TabGroupData() {
-  title_ = base::ASCIIToUTF16("Group");
+  static int groupCount = 0;
+  title_ = base::ASCIIToUTF16("Group " + std::to_string(groupCount));
+  groupCount++;
   static SkRandom rand;
   stroke_color_ = rand.nextU() | 0xff000000;
 }
diff --git a/chrome/browser/ui/tabs/tab_menu_model.cc b/chrome/browser/ui/tabs/tab_menu_model.cc
index c784cf8..f6fe2d9 100644
--- a/chrome/browser/ui/tabs/tab_menu_model.cc
+++ b/chrome/browser/ui/tabs/tab_menu_model.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "chrome/browser/browser_features.h"
+#include "chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
@@ -21,6 +22,8 @@
   Build(tab_strip, index);
 }
 
+TabMenuModel::~TabMenuModel() {}
+
 void TabMenuModel::Build(TabStripModel* tab_strip, int index) {
   std::vector<int> affected_indices =
       tab_strip->IsTabSelected(index)
@@ -31,6 +34,15 @@
   if (base::FeatureList::IsEnabled(features::kTabGroups)) {
     AddItemWithStringId(TabStripModel::CommandAddToNewGroup,
                         IDS_TAB_CXMENU_ADD_TAB_TO_NEW_GROUP);
+
+    // Create submenu with existing groups
+    if (ExistingTabGroupSubMenuModel::ShouldShowSubmenu(tab_strip, index)) {
+      add_to_existing_group_submenu_ =
+          std::make_unique<ExistingTabGroupSubMenuModel>(tab_strip, index);
+      AddSubMenuWithStringId(TabStripModel::CommandAddToExistingGroup,
+                             IDS_TAB_CXMENU_ADD_TAB_TO_EXISTING_GROUP,
+                             add_to_existing_group_submenu_.get());
+    }
   }
   AddSeparator(ui::NORMAL_SEPARATOR);
   AddItemWithStringId(TabStripModel::CommandReload, IDS_TAB_CXMENU_RELOAD);
diff --git a/chrome/browser/ui/tabs/tab_menu_model.h b/chrome/browser/ui/tabs/tab_menu_model.h
index 48b9ec9..fd7c13a 100644
--- a/chrome/browser/ui/tabs/tab_menu_model.h
+++ b/chrome/browser/ui/tabs/tab_menu_model.h
@@ -18,11 +18,13 @@
   TabMenuModel(ui::SimpleMenuModel::Delegate* delegate,
                TabStripModel* tab_strip,
                int index);
-  ~TabMenuModel() override {}
+  ~TabMenuModel() override;
 
  private:
   void Build(TabStripModel* tab_strip, int index);
 
+  std::unique_ptr<ui::SimpleMenuModel> add_to_existing_group_submenu_;
+
   DISALLOW_COPY_AND_ASSIGN(TabMenuModel);
 };
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 67bc9fb..c6d6d4f 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -156,8 +156,8 @@
   void set_pinned(bool value) { pinned_ = value; }
   bool blocked() const { return blocked_; }
   void set_blocked(bool value) { blocked_ = value; }
-  TabGroupData* group() const { return group_; }
-  void set_group(TabGroupData* value) { group_ = value; }
+  const TabGroupData* group() const { return group_; }
+  void set_group(const TabGroupData* value) { group_ = value; }
 
  private:
   // Make sure that if someone deletes this WebContents out from under us, it
@@ -192,7 +192,7 @@
   //     break that guarantee, with undefined results.
   //   - The exact shape of the group-related changes to the TabStripModel API
   //     (and the relevant bits of the extension API) are TBD.
-  TabGroupData* group_ = nullptr;
+  const TabGroupData* group_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(WebContentsData);
 };
@@ -762,6 +762,15 @@
   return contents_data_[index]->group();
 }
 
+std::vector<TabGroupData*> TabStripModel::ListTabGroups() const {
+  std::vector<TabGroupData*> groups;
+  for (std::unique_ptr<TabGroupData> const& group : group_data_) {
+    groups.push_back(group.get());
+  }
+
+  return groups;
+}
+
 int TabStripModel::IndexOfFirstNonPinnedTab() const {
   for (size_t i = 0; i < contents_data_.size(); ++i) {
     if (!IsTabPinned(static_cast<int>(i)))
@@ -936,13 +945,53 @@
   if (ContainsIndex(destination_index - 1)) {
     const TabGroupData* split_group = GetTabGroupForTab(destination_index - 1);
     if (split_group != nullptr) {
-      while (ContainsIndex(destination_index + 1) &&
-             GetTabGroupForTab(destination_index + 1) == split_group) {
+      while (ContainsIndex(destination_index) &&
+             GetTabGroupForTab(destination_index) == split_group) {
         destination_index++;
       }
     }
   }
 
+  std::vector<int> new_indices =
+      IsTabPinned(indices[0]) ? SetTabsPinned(indices, true) : indices;
+
+  MoveTabsIntoGroup(new_indices, destination_index, group);
+}
+
+void TabStripModel::AddToExistingGroup(const std::vector<int>& indices,
+                                       const TabGroupData* group) {
+  // TODO(https://crbug.com/915956): Tabs should be ungrouped before they are
+  // moved (once ungrouping is a thing) so that groups never get split up.
+
+  int destination_index = -1;
+  bool pin = false;
+  for (int i = contents_data_.size() - 1; i >= 0; i--) {
+    if (contents_data_[i]->group() == group) {
+      destination_index = i + 1;
+      pin = IsTabPinned(i);
+      break;
+    }
+  }
+  // TODO(https://crbug.com/915956): No tab already exists in that group.
+  // This state will be unreachable once tab groups are deleted when their
+  // last member is ungrouped. DCHECK for now.
+  DCHECK_NE(destination_index, -1);
+
+  // Ignore indices that are already in the group.
+  std::vector<int> new_indices;
+  for (size_t i = 0; i < indices.size(); i++) {
+    if (GetTabGroupForTab(indices[i]) != group) {
+      new_indices.push_back(indices[i]);
+    }
+  }
+  new_indices = SetTabsPinned(new_indices, pin);
+
+  MoveTabsIntoGroup(new_indices, destination_index, group);
+}
+
+void TabStripModel::MoveTabsIntoGroup(const std::vector<int>& indices,
+                                      int destination_index,
+                                      const TabGroupData* group) {
   // Some tabs will need to be moved to the right, some to the left. We need to
   // handle those separately. First, move tabs to the right, starting with the
   // rightmost tab so we don't cause other tabs we are about to move to shift.
@@ -952,35 +1001,49 @@
     numTabsMovingRight++;
   }
   for (int i = numTabsMovingRight - 1; i >= 0; i--) {
-    int insertion_index = destination_index - numTabsMovingRight + i + 1;
+    int insertion_index = destination_index - numTabsMovingRight + i;
     MoveWebContentsAt(indices[i], insertion_index, false);
     contents_data_[insertion_index]->set_group(group);
   }
 
-  // Collect indices for tabs moving to the left, pinning them if any tabs in
-  // |indices| are pinned (or, equivalently, if the first tab is). Any tabs
-  // pinned here will no longer be in the position indicated in |indices|, so
-  // we need to record adjusted indices in |move_left_indices|. If we aren't
-  // pinning a tab, we can just collect its unmodified index.
+  // Collect indices for tabs moving to the left.
   std::vector<int> move_left_indices;
   for (size_t i = numTabsMovingRight; i < indices.size(); i++) {
-    if (IsTabPinned(indices[0]) && !IsTabPinned(indices[i])) {
-      SetTabPinned(indices[i], true);
-      move_left_indices.push_back(IndexOfFirstNonPinnedTab() - 1);
-    } else {
-      move_left_indices.push_back(indices[i]);
-    }
+    move_left_indices.push_back(indices[i]);
   }
   // Move tabs to the left, starting with the leftmost tab.
-  int move_left_starting_index =
-      numTabsMovingRight == 0 ? destination_index : destination_index + 1;
   for (size_t i = 0; i < move_left_indices.size(); i++) {
-    MoveWebContentsAt(move_left_indices[i], move_left_starting_index + i,
-                      false);
-    contents_data_[move_left_starting_index + i]->set_group(group);
+    MoveWebContentsAt(move_left_indices[i], destination_index + i, false);
+    contents_data_[destination_index + i]->set_group(group);
   }
 }
 
+std::vector<int> TabStripModel::SetTabsPinned(const std::vector<int>& indices,
+                                              bool pinned) {
+  std::vector<int> new_indices;
+  if (pinned) {
+    for (size_t i = 0; i < indices.size(); i++) {
+      if (IsTabPinned(indices[i])) {
+        new_indices.push_back(indices[i]);
+      } else {
+        SetTabPinned(indices[i], true);
+        new_indices.push_back(IndexOfFirstNonPinnedTab() - 1);
+      }
+    }
+  } else {
+    for (size_t i = indices.size() - 1; i < indices.size(); i--) {
+      if (!IsTabPinned(indices[i])) {
+        new_indices.push_back(indices[i]);
+      } else {
+        SetTabPinned(indices[i], false);
+        new_indices.push_back(IndexOfFirstNonPinnedTab());
+      }
+    }
+    std::reverse(new_indices.begin(), new_indices.end());
+  }
+  return new_indices;
+}
+
 // Context menu functions.
 bool TabStripModel::IsContextMenuCommandEnabled(
     int context_index,
@@ -1044,6 +1107,9 @@
     case CommandAddToNewGroup:
       return true;
 
+    case CommandAddToExistingGroup:
+      return true;
+
     default:
       NOTREACHED();
   }
@@ -1153,7 +1219,7 @@
     }
 
     case CommandToggleTabAudioMuted: {
-      const std::vector<int>& indices = GetIndicesForCommand(context_index);
+      std::vector<int> indices = GetIndicesForCommand(context_index);
       const bool mute = WillContextMenuMute(context_index);
       if (mute)
         base::RecordAction(UserMetricsAction("TabContextMenu_MuteTabs"));
@@ -1167,7 +1233,6 @@
     }
 
     case CommandToggleSiteMuted: {
-      const std::vector<int>& indices = GetIndicesForCommand(context_index);
       const bool mute = WillContextMenuMuteSites(context_index);
       if (mute) {
         base::RecordAction(
@@ -1176,7 +1241,7 @@
         base::RecordAction(
             UserMetricsAction("SoundContentSetting.UnmuteBy.TabStrip"));
       }
-      SetSitesMuted(indices, mute);
+      SetSitesMuted(GetIndicesForCommand(context_index), mute);
       break;
     }
 
@@ -1190,8 +1255,13 @@
     case CommandAddToNewGroup: {
       base::RecordAction(UserMetricsAction("TabContextMenu_AddToNewGroup"));
 
-      const std::vector<int>& indices = GetIndicesForCommand(context_index);
-      AddToNewGroup(indices);
+      AddToNewGroup(GetIndicesForCommand(context_index));
+      break;
+    }
+
+    case CommandAddToExistingGroup: {
+      // Do nothing. The submenu's delegate will invoke
+      // ExecuteAddToExistingGroupCommand with the correct group later.
       break;
     }
 
@@ -1200,25 +1270,12 @@
   }
 }
 
-std::vector<int> TabStripModel::GetIndicesClosedByCommand(
-    int index,
-    ContextMenuCommand id) const {
-  DCHECK(ContainsIndex(index));
-  DCHECK(id == CommandCloseTabsToRight || id == CommandCloseOtherTabs);
-  bool is_selected = IsTabSelected(index);
-  int last_unclosed_tab = -1;
-  if (id == CommandCloseTabsToRight) {
-    last_unclosed_tab =
-        is_selected ? selection_model_.selected_indices().back() : index;
-  }
+void TabStripModel::ExecuteAddToExistingGroupCommand(
+    int context_index,
+    const TabGroupData* group) {
+  base::RecordAction(UserMetricsAction("TabContextMenu_AddToExistingGroup"));
 
-  // NOTE: callers expect the vector to be sorted in descending order.
-  std::vector<int> indices;
-  for (int i = count() - 1; i > last_unclosed_tab; --i) {
-    if (i != index && !IsTabPinned(i) && (!is_selected || !IsTabSelected(i)))
-      indices.push_back(i);
-  }
-  return indices;
+  AddToExistingGroup(GetIndicesForCommand(context_index), group);
 }
 
 bool TabStripModel::WillContextMenuMute(int index) {
@@ -1337,6 +1394,27 @@
   return selection_model_.selected_indices();
 }
 
+std::vector<int> TabStripModel::GetIndicesClosedByCommand(
+    int index,
+    ContextMenuCommand id) const {
+  DCHECK(ContainsIndex(index));
+  DCHECK(id == CommandCloseTabsToRight || id == CommandCloseOtherTabs);
+  bool is_selected = IsTabSelected(index);
+  int last_unclosed_tab = -1;
+  if (id == CommandCloseTabsToRight) {
+    last_unclosed_tab =
+        is_selected ? selection_model_.selected_indices().back() : index;
+  }
+
+  // NOTE: callers expect the vector to be sorted in descending order.
+  std::vector<int> indices;
+  for (int i = count() - 1; i > last_unclosed_tab; --i) {
+    if (i != index && !IsTabPinned(i) && (!is_selected || !IsTabSelected(i)))
+      indices.push_back(i);
+  }
+  return indices;
+}
+
 bool TabStripModel::IsNewTabAtEndOfTabStrip(WebContents* contents) const {
   const GURL& url = contents->GetLastCommittedURL();
   return url.SchemeIs(content::kChromeUIScheme) &&
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 7dfc763..f4e8b20 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/containers/span.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -289,6 +290,9 @@
   // https://crbug.com/915956.
   const TabGroupData* GetTabGroupForTab(int index) const;
 
+  // Returns the list of tab groups that contain at least one tab in this strip.
+  std::vector<TabGroupData*> ListTabGroups() const;
+
   // Returns the index of the first tab that is not a pinned tab. This returns
   // |count()| if all of the tabs are pinned tabs, and 0 if none of the tabs are
   // pinned tabs.
@@ -346,9 +350,16 @@
   // https://crbug.com/915956.
   void AddToNewGroup(const std::vector<int>& indices);
 
+  // Add the set of tabs pointed to by |indices| to the tab group |group|. The
+  // tabs take on the pinnedness of the tabs already in the group, and are moved
+  // to immediately follow the tabs already in the group.
+  void AddToExistingGroup(const std::vector<int>& indices,
+                          const TabGroupData* group);
+
   // View API //////////////////////////////////////////////////////////////////
 
-  // Context menu functions.
+  // Context menu functions. Tab groups uses command ids following CommandLast
+  // for entries in the 'Add to existing group' submenu.
   enum ContextMenuCommand {
     CommandFirst,
     CommandNewTab,
@@ -364,6 +375,7 @@
     CommandSendToMyDevices,
     CommandBookmarkAllTabs,
     CommandAddToNewGroup,
+    CommandAddToExistingGroup,
     CommandLast
   };
 
@@ -378,11 +390,10 @@
   void ExecuteContextMenuCommand(int context_index,
                                  ContextMenuCommand command_id);
 
-  // Returns a vector of indices of the tabs that will close when executing the
-  // command |id| for the tab at |index|. The returned indices are sorted in
-  // descending order.
-  std::vector<int> GetIndicesClosedByCommand(int index,
-                                             ContextMenuCommand id) const;
+  // Adds the tab at |context_index| to the given tab group |group|. If
+  // |context_index| is selected the command applies to all selected tabs.
+  void ExecuteAddToExistingGroupCommand(int context_index,
+                                        const TabGroupData* group);
 
   // Returns true if 'CommandToggleTabAudioMuted' will mute. |index| is the
   // index supplied to |ExecuteContextMenuCommand|.
@@ -424,6 +435,8 @@
   bool ShouldResetOpenerOnActiveTabChange(content::WebContents* contents) const;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, GetIndicesClosedByCommand);
+
   class WebContentsData;
   struct DetachedWebContents;
   struct DetachNotifications;
@@ -460,6 +473,12 @@
   // increasing order.
   std::vector<int> GetIndicesForCommand(int index) const;
 
+  // Returns a vector of indices of the tabs that will close when executing the
+  // command |id| for the tab at |index|. The returned indices are sorted in
+  // descending order.
+  std::vector<int> GetIndicesClosedByCommand(int index,
+                                             ContextMenuCommand id) const;
+
   // Returns true if the specified WebContents is a New Tab at the end of
   // the tabstrip. We check for this because opener relationships are _not_
   // forgotten for the New Tab page opened as a result of a New Tab gesture
@@ -523,6 +542,17 @@
   // starting at |start| to |index|. See MoveSelectedTabsTo for more details.
   void MoveSelectedTabsToImpl(int index, size_t start, size_t length);
 
+  // Moves the set of tabs indicated by |indices| to precede the tab at index
+  // |destination_index|, maintaining their order and the order of tabs not
+  // being moved, and adds them to the tab group |group|.
+  void MoveTabsIntoGroup(const std::vector<int>& indices,
+                         int destination_index,
+                         const TabGroupData* group);
+
+  // Ensures all tabs indicated by |indices| are pinned, moving them in the
+  // process if necessary. Returns the new locations of all of those tabs.
+  std::vector<int> SetTabsPinned(const std::vector<int>& indices, bool pinned);
+
   // Sets the sound content setting for each site at the |indices|.
   void SetSitesMuted(const std::vector<int>& indices, bool mute) const;
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index fc006c5..e6f725a 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -427,20 +427,6 @@
     return actual;
   }
 
-  std::string GetIndicesClosedByCommandAsString(
-      const TabStripModel& model,
-      int index,
-      TabStripModel::ContextMenuCommand id) const {
-    std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
-    std::string result;
-    for (size_t i = 0; i < indices.size(); ++i) {
-      if (i != 0)
-        result += " ";
-      result += base::IntToString(indices[i]);
-    }
-    return result;
-  }
-
   void PrepareTabstripForSelectionTest(TabStripModel* model,
                                        int tab_count,
                                        int pinned_count,
@@ -1342,34 +1328,46 @@
   TabStripModel tabstrip(&delegate, profile());
   EXPECT_TRUE(tabstrip.empty());
 
+  const auto indicesClosedAsString =
+      [&tabstrip](int index, TabStripModel::ContextMenuCommand id) {
+        std::vector<int> indices =
+            tabstrip.GetIndicesClosedByCommand(index, id);
+        std::string result;
+        for (size_t i = 0; i < indices.size(); ++i) {
+          if (i != 0)
+            result += " ";
+          result += base::IntToString(indices[i]);
+        }
+        return result;
+      };
+
   for (int i = 0; i < 5; ++i)
     tabstrip.AppendWebContents(CreateWebContents(), true);
 
   EXPECT_EQ("4 3 2 1",
-            GetIndicesClosedByCommandAsString(
-                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
-  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
-                         tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
+            indicesClosedAsString(0, TabStripModel::CommandCloseTabsToRight));
+  EXPECT_EQ("4 3 2",
+            indicesClosedAsString(1, TabStripModel::CommandCloseTabsToRight));
 
-  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
-                           tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
-  EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
-                           tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
+  EXPECT_EQ("4 3 2 1",
+            indicesClosedAsString(0, TabStripModel::CommandCloseOtherTabs));
+  EXPECT_EQ("4 3 2 0",
+            indicesClosedAsString(1, TabStripModel::CommandCloseOtherTabs));
 
   // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
   // commands.
   tabstrip.SetTabPinned(0, true);
   tabstrip.SetTabPinned(1, true);
 
-  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
-                         tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
-  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
-                       tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
+  EXPECT_EQ("4 3 2",
+            indicesClosedAsString(0, TabStripModel::CommandCloseTabsToRight));
+  EXPECT_EQ("4 3",
+            indicesClosedAsString(2, TabStripModel::CommandCloseTabsToRight));
 
-  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
-                         tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
-  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
-                       tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
+  EXPECT_EQ("4 3 2",
+            indicesClosedAsString(0, TabStripModel::CommandCloseOtherTabs));
+  EXPECT_EQ("4 3",
+            indicesClosedAsString(2, TabStripModel::CommandCloseOtherTabs));
 
   tabstrip.CloseAllTabs();
   EXPECT_TRUE(tabstrip.empty());
@@ -2776,3 +2774,156 @@
   strip.ActivateTabAt(0, true);
   strip.CloseAllTabs();
 }
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupIdempotent) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AddToNewGroup({0});
+  const TabGroupData* group = strip.GetTabGroupForTab(0);
+
+  strip.AddToExistingGroup({0}, group);
+
+  EXPECT_EQ(strip.GetTabGroupForTab(0), group);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroup) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> in{strip.GetWebContentsAt(0),
+                               strip.GetWebContentsAt(1)};
+  strip.AddToNewGroup({0});
+  const TabGroupData* group = strip.GetTabGroupForTab(0);
+
+  strip.AddToExistingGroup({1}, group);
+
+  EXPECT_EQ(strip.GetTabGroupForTab(1), group);
+  EXPECT_EQ(strip.GetWebContentsAt(0), in[0]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), in[1]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupReordersToTheRight) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> orig{strip.GetWebContentsAt(0),
+                                 strip.GetWebContentsAt(1)};
+  strip.AddToNewGroup({1});
+  const TabGroupData* group = strip.GetTabGroupForTab(1);
+
+  strip.AddToExistingGroup({0}, group);
+
+  EXPECT_EQ(strip.GetTabGroupForTab(0), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(1), group);
+  EXPECT_EQ(strip.GetWebContentsAt(0), orig[1]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), orig[0]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupReordersToTheLeft) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> orig{strip.GetWebContentsAt(0),
+                                 strip.GetWebContentsAt(1),
+                                 strip.GetWebContentsAt(2)};
+  strip.AddToNewGroup({0});
+  const TabGroupData* group = strip.GetTabGroupForTab(0);
+
+  strip.AddToExistingGroup({2}, group);
+
+  EXPECT_EQ(strip.GetTabGroupForTab(0), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(1), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(2), nullptr);
+  EXPECT_EQ(strip.GetWebContentsAt(0), orig[0]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), orig[2]);
+  EXPECT_EQ(strip.GetWebContentsAt(2), orig[1]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupReorders) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> orig{
+      strip.GetWebContentsAt(0), strip.GetWebContentsAt(1),
+      strip.GetWebContentsAt(2), strip.GetWebContentsAt(3)};
+  strip.AddToNewGroup({1});
+  const TabGroupData* group = strip.GetTabGroupForTab(1);
+
+  strip.AddToExistingGroup({0, 3}, group);
+
+  EXPECT_EQ(strip.GetTabGroupForTab(0), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(1), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(2), group);
+  EXPECT_EQ(strip.GetTabGroupForTab(3), nullptr);
+  EXPECT_EQ(strip.GetWebContentsAt(0), orig[1]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), orig[0]);
+  EXPECT_EQ(strip.GetWebContentsAt(2), orig[3]);
+  EXPECT_EQ(strip.GetWebContentsAt(3), orig[2]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupPins) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> orig{strip.GetWebContentsAt(0),
+                                 strip.GetWebContentsAt(1)};
+  strip.SetTabPinned(0, true);
+  strip.AddToNewGroup({0});
+  const TabGroupData* group = strip.GetTabGroupForTab(0);
+
+  strip.AddToExistingGroup({1}, group);
+
+  EXPECT_TRUE(strip.IsTabPinned(1));
+  EXPECT_EQ(strip.GetTabGroupForTab(1), group);
+  EXPECT_EQ(strip.GetWebContentsAt(0), orig[0]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), orig[1]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, AddTabToExistingGroupUnpins) {
+  TabStripDummyDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  strip.AppendWebContents(CreateWebContents(), false);
+  strip.AppendWebContents(CreateWebContents(), false);
+  std::vector<WebContents*> orig{strip.GetWebContentsAt(0),
+                                 strip.GetWebContentsAt(1)};
+  strip.SetTabPinned(0, true);
+  strip.AddToNewGroup({1});
+  const TabGroupData* group = strip.GetTabGroupForTab(1);
+
+  strip.AddToExistingGroup({0}, group);
+
+  EXPECT_FALSE(strip.IsTabPinned(0));
+  EXPECT_EQ(strip.GetTabGroupForTab(0), group);
+  EXPECT_EQ(strip.GetWebContentsAt(0), orig[1]);
+  EXPECT_EQ(strip.GetWebContentsAt(1), orig[0]);
+
+  strip.ActivateTabAt(0, true);
+  strip.CloseAllTabs();
+}
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
index e9331e7..e088ab5 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 
 #include "base/macros.h"
+#include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -34,7 +35,8 @@
 class FakeUpgradeDetector : public UpgradeDetector {
  public:
   FakeUpgradeDetector()
-      : UpgradeDetector(base::DefaultTickClock::GetInstance()) {}
+      : UpgradeDetector(base::DefaultClock::GetInstance(),
+                        base::DefaultTickClock::GetInstance()) {}
 
   void BroadcastLevel(UpgradeNotificationAnnoyanceLevel level) {
     set_upgrade_notification_stage(level);
@@ -43,7 +45,7 @@
 
   // UpgradeDetector:
   base::TimeDelta GetHighAnnoyanceLevelDelta() override;
-  base::TimeTicks GetHighAnnoyanceDeadline() override;
+  base::Time GetHighAnnoyanceDeadline() override;
 
  private:
   // UpgradeDetector:
@@ -57,9 +59,9 @@
   return base::TimeDelta();
 }
 
-base::TimeTicks FakeUpgradeDetector::GetHighAnnoyanceDeadline() {
+base::Time FakeUpgradeDetector::GetHighAnnoyanceDeadline() {
   // This value is not important for this test.
-  return base::TimeTicks();
+  return base::Time();
 }
 
 void FakeUpgradeDetector::OnRelaunchNotificationPeriodPrefChanged() {}
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.cc b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
index 39be1df..66543de 100644
--- a/chrome/browser/ui/toolbar/back_forward_menu_model.cc
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
@@ -156,9 +156,6 @@
   return nullptr;
 }
 
-void BackForwardMenuModel::HighlightChangedTo(int index) {
-}
-
 void BackForwardMenuModel::ActivatedAt(int index) {
   ActivatedAt(index, 0);
 }
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.h b/chrome/browser/ui/toolbar/back_forward_menu_model.h
index 48fd16ac..5139414 100644
--- a/chrome/browser/ui/toolbar/back_forward_menu_model.h
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model.h
@@ -66,7 +66,6 @@
   ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override;
   bool IsEnabledAt(int index) const override;
   MenuModel* GetSubmenuModelAt(int index) const override;
-  void HighlightChangedTo(int index) override;
   void ActivatedAt(int index) override;
   void ActivatedAt(int index, int event_flags) override;
   void MenuWillShow() override;
diff --git a/chrome/browser/ui/user_manager.h b/chrome/browser/ui/user_manager.h
index 4e854986..7c37bfd5 100644
--- a/chrome/browser/ui/user_manager.h
+++ b/chrome/browser/ui/user_manager.h
@@ -63,33 +63,22 @@
   static constexpr int kDialogWidth = 448;
 
   // Shows a dialog where the user can re-authenticate the profile with the
-  // given |email|. This is called in the following scenarios:
-  //  -From the user manager when a profile is locked and the user's password is
-  //   detected to have been changed.
-  //  -From the user manager when a custodian account needs to be
-  //   reauthenticated.
-  // |reason| can be REASON_UNLOCK or REASON_REAUTHENTICATION to indicate
-  // whether this is a reauth or unlock scenario.
-  static void ShowReauthDialog(content::BrowserContext* browser_context,
-                               const std::string& email,
-                               signin_metrics::Reason reason);
+  // given |email|. This is called from the user manager when a profile is
+  // locked and the user's password is detected to have been changed.
+  static void ShowUnlockDialog(content::BrowserContext* browser_context,
+                               const std::string& email);
 
   // Shows a reauth dialog with profile path so that the sign in error message
   // can be displayed without browser window.
-  static void ShowReauthDialogWithProfilePath(
+  static void ShowUnlockDialogWithProfilePath(
       content::BrowserContext* browser_context,
       const std::string& email,
-      const base::FilePath& profile_path,
-      signin_metrics::Reason reason);
+      const base::FilePath& profile_path);
 
   // Shows a dialog where the user logs into their profile for the first time
-  // via the user manager.
-  // |reason| can be REASON_SIGNIN_PRIMARY_ACCOUNT or
-  // REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT to indicate whether this sign in is
-  // forced or not.
-  static void ShowSigninDialog(content::BrowserContext* browser_context,
-                               const base::FilePath& profile_path,
-                               signin_metrics::Reason reason);
+  // via the user manager, when force signin is enabled.
+  static void ShowForceSigninDialog(content::BrowserContext* browser_context,
+                                    const base::FilePath& profile_path);
 
   // Show the dialog and display local sign in error message without browser.
   static void ShowDialogAndDisplayErrorMessage(
diff --git a/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
new file mode 100644
index 0000000..695b1d9
--- /dev/null
+++ b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 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 <atk/atk.h>
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/test/base/in_process_browser_test.h"
+
+class AuraLinuxAccessibilityInProcessBrowserTest : public InProcessBrowserTest {
+ protected:
+  AuraLinuxAccessibilityInProcessBrowserTest() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AuraLinuxAccessibilityInProcessBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(AuraLinuxAccessibilityInProcessBrowserTest,
+                       IndexInParent) {
+  AtkObject* native_view_accessible =
+      static_cast<BrowserView*>(browser()->window())->GetNativeViewAccessible();
+  EXPECT_NE(nullptr, native_view_accessible);
+
+  int n_children = atk_object_get_n_accessible_children(native_view_accessible);
+  for (int i = 0; i < n_children; i++) {
+    AtkObject* child =
+        atk_object_ref_accessible_child(native_view_accessible, i);
+
+    int index_in_parent = atk_object_get_index_in_parent(child);
+    ASSERT_NE(-1, index_in_parent);
+    ASSERT_EQ(i, index_in_parent);
+
+    g_object_unref(child);
+  }
+}
diff --git a/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc b/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc
index 160ec07..e300b26 100644
--- a/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc
+++ b/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/metrics/user_metrics.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -131,8 +132,9 @@
 }
 
 bool FeaturePromoBubbleView::OnMousePressed(const ui::MouseEvent& event) {
-  CloseBubble();
-  return true;
+  base::RecordAction(
+      base::UserMetricsAction("InProductHelp.Promos.BubbleClicked"));
+  return false;
 }
 
 void FeaturePromoBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
index 3d989af6..1b944c80 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
@@ -16,6 +17,32 @@
 #include "chrome/grit/generated_resources.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 
+namespace {
+
+// Last step of the flow completed by the user before dismissal (whether by
+// successful completion of the flow, timing out, or clicking away.). This is
+// used for an UMA histogram.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ReopenTabPromoStepAtDismissal {
+  // The promo bubble was shown, but the menu was not opened; i.e. the bubble
+  // timed out.
+  kBubbleShown = 0,
+  // The menu was opened, but the user clicked away without opening the last
+  // closed tab.
+  kMenuOpened = 1,
+  // The last closed tab item was clicked. The promo was successful.
+  kTabReopened = 2,
+
+  kMaxValue = kTabReopened,
+};
+
+const char kReopenTabPromoDismissedAtHistogram[] =
+    "InProductHelp.Promos.IPH_ReopenTab.DismissedAt";
+
+}  // namespace
+
 ReopenTabPromoController::ReopenTabPromoController(BrowserView* browser_view)
     : iph_service_(ReopenTabInProductHelpFactory::GetForProfile(
           browser_view->browser()->profile())),
@@ -28,6 +55,9 @@
 }
 
 void ReopenTabPromoController::ShowPromo() {
+  // This shouldn't be called more than once. Check that state is fresh.
+  DCHECK(!tab_reopened_before_app_menu_closed_);
+
   // Here, we start the promo display. We highlight the app menu button and open
   // the promo bubble.
   BrowserAppMenuButton* app_menu_button =
@@ -64,6 +94,9 @@
   // If the menu isn't showing, that means the promo bubble timed out. We should
   // notify our IPH service that help was dismissed.
   if (!browser_view_->toolbar()->app_menu_button()->IsMenuShowing()) {
+    UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
+                              ReopenTabPromoStepAtDismissal::kBubbleShown);
+
     BrowserAppMenuButton* app_menu_button =
         browser_view_->toolbar()->app_menu_button();
     app_menu_button->RemoveMenuListener(this);
@@ -76,6 +109,11 @@
   // The menu was opened then closed, whether by clicking away or by clicking a
   // menu item. We notify the service regardless of whether IPH succeeded.
   // Success is determined by whether the reopen tab event was sent.
+  if (!tab_reopened_before_app_menu_closed_) {
+    UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
+                              ReopenTabPromoStepAtDismissal::kMenuOpened);
+  }
+
   iph_service_->HelpDismissed();
 
   browser_view_->toolbar()->app_menu_button()->SetPromoIsShowing(false);
@@ -83,3 +121,12 @@
   AppMenu* app_menu = browser_view_->toolbar()->app_menu_button()->app_menu();
   app_menu->RemoveObserver(this);
 }
+
+void ReopenTabPromoController::OnExecuteCommand(int command_id) {
+  if (command_id == AppMenuModel::kMinRecentTabsCommandId) {
+    DCHECK(!tab_reopened_before_app_menu_closed_);
+    UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
+                              ReopenTabPromoStepAtDismissal::kTabReopened);
+    tab_reopened_before_app_menu_closed_ = true;
+  }
+}
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
index e13e2de0..6178f56 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -23,6 +23,7 @@
   explicit ReopenTabPromoController(BrowserView* browser_view);
   ~ReopenTabPromoController() override = default;
 
+  // Shows the IPH promo. Should only be called once.
   void ShowPromo();
 
  private:
@@ -34,10 +35,16 @@
 
   // AppMenuObserver:
   void AppMenuClosed() override;
+  void OnExecuteCommand(int command_id) override;
 
   ReopenTabInProductHelp* const iph_service_;
   BrowserView* const browser_view_;
   FeaturePromoBubbleView* promo_bubble_ = nullptr;
+
+  // Flag used to determine whether the promo completed successfully or not upon
+  // the app menu closing. This is set to true if
+  // OnExecuteCommand(AppMenuModel::kMinRecentTabsCommandId) is called.
+  bool tab_reopened_before_app_menu_closed_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 34f0e1d..77357280 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -134,7 +134,8 @@
 }
 
 bool BrowserNonClientFrameView::CanDrawStrokes() const {
-  return true;
+  // Hosted apps should not draw strokes, as they don't have a tab strip.
+  return !browser_view_->browser()->hosted_app_controller();
 }
 
 SkColor BrowserNonClientFrameView::GetFrameColor(
@@ -169,6 +170,11 @@
                                           browser_view_->IsIncognito());
 }
 
+SkColor BrowserNonClientFrameView::GetCaptionColor(
+    ActiveState active_state) const {
+  return color_utils::GetColorWithMaxContrast(GetFrameColor(active_state));
+}
+
 SkColor BrowserNonClientFrameView::GetToolbarTopSeparatorColor() const {
   const int color_id =
       ShouldPaintAsActive()
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index 3f829c6..3a880e12 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -97,10 +97,9 @@
   // tabstrip background.
   SkColor GetFrameColor(ActiveState active_state = kUseCurrent) const;
 
-  // Returns the color to use for text and other title bar elements given the
-  // frame |active_state|.
-  virtual SkColor GetFrameForegroundColor(
-      ActiveState active_state = kUseCurrent) const = 0;
+  // Returns the color to use for text, caption buttons, and other title bar
+  // elements.
+  virtual SkColor GetCaptionColor(ActiveState active_state = kUseCurrent) const;
 
   // Returns COLOR_TOOLBAR_TOP_SEPARATOR[,_INACTIVE] depending on the activation
   // state of the window.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 7ea89fd..a0dd85f 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -301,21 +301,19 @@
   return ash::IsWindowTrustedPinned(GetFrameWindow()) ? false : true;
 }
 
-SkColor BrowserNonClientFrameViewAsh::GetFrameForegroundColor(
+SkColor BrowserNonClientFrameViewAsh::GetCaptionColor(
     ActiveState active_state) const {
   bool active = ShouldPaintAsActive(active_state);
 
-  SkColor active_color = views::FrameCaptionButton::GetButtonColor(
-      views::FrameCaptionButton::ColorMode::kDefault, ash::kDefaultFrameColor);
+  SkColor active_color =
+      views::FrameCaptionButton::GetButtonColor(ash::kDefaultFrameColor);
 
   // Hosted apps apply a theme color if specified by the extension.
   Browser* browser = browser_view()->browser();
   base::Optional<SkColor> theme_color =
       browser->hosted_app_controller()->GetThemeColor();
-  if (theme_color) {
-    active_color = views::FrameCaptionButton::GetButtonColor(
-        views::FrameCaptionButton::ColorMode::kThemed, *theme_color);
-  }
+  if (theme_color)
+    active_color = views::FrameCaptionButton::GetButtonColor(*theme_color);
 
   if (active)
     return active_color;
@@ -323,10 +321,7 @@
   // Add the container for extra hosted app buttons (e.g app menu button).
   const float inactive_alpha_ratio =
       views::FrameCaptionButton::GetInactiveButtonColorAlphaRatio();
-  SkColor inactive_color =
-      SkColorSetA(active_color, 255 * inactive_alpha_ratio);
-
-  return inactive_color;
+  return SkColorSetA(active_color, inactive_alpha_ratio * SK_AlphaOPAQUE);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -821,10 +816,9 @@
     return;
 
   // Add the container for extra hosted app buttons (e.g app menu button).
-  SkColor active_color = GetFrameForegroundColor(kActive);
-  SkColor inactive_color = GetFrameForegroundColor(kInactive);
   set_hosted_app_button_container(new HostedAppButtonContainer(
-      frame(), browser_view(), active_color, inactive_color));
+      frame(), browser_view(), GetCaptionColor(kActive),
+      GetCaptionColor(kInactive)));
   AddChildView(hosted_app_button_container());
 }
 
@@ -837,9 +831,6 @@
   } else if (browser_view()->IsBrowserTypeHostedApp()) {
     active_color =
         browser_view()->browser()->hosted_app_controller()->GetThemeColor();
-    frame_header_->set_button_color_mode(
-        active_color ? views::FrameCaptionButton::ColorMode::kThemed
-                     : views::FrameCaptionButton::ColorMode::kDefault);
   } else if (!browser_view()->browser()->is_app()) {
     active_color = kMdWebUiFrameColor;
   }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index ce4ccaf4..0cf8573 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -68,7 +68,7 @@
   void UpdateMinimumSize() override;
   void OnTabsMaxXChanged() override;
   bool CanUserExitFullscreen() const override;
-  SkColor GetFrameForegroundColor(ActiveState active_state) const override;
+  SkColor GetCaptionColor(ActiveState active_state) const override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index e8091af..aa3c06e6 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -735,10 +735,6 @@
 // fullscreen control popup doesn't show up).
 IN_PROC_BROWSER_TEST_P(ImmersiveModeBrowserViewTest,
                        LockedFullscreenDisablesImmersive) {
-  // TODO(crbug.com/912191): pinning by setting the window property doesn't work
-  // in Mash.
-  if (features::IsSingleProcessMash())
-    return;
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   EXPECT_FALSE(browser_view->GetWidget()->IsFullscreen());
 
@@ -988,7 +984,7 @@
   aura::Window* window = browser_view_->GetWidget()->GetNativeWindow();
   EXPECT_EQ(GetThemeColor(), window->GetProperty(ash::kFrameActiveColorKey));
   EXPECT_EQ(GetThemeColor(), window->GetProperty(ash::kFrameInactiveColorKey));
-  EXPECT_EQ(SK_ColorWHITE, GetActiveColor());
+  EXPECT_EQ(gfx::kGoogleGrey200, GetActiveColor());
 }
 
 // Make sure that for hosted apps, the height of the frame doesn't exceed the
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
index 97471ce6..ab8970c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -33,7 +33,6 @@
   void UpdateFullscreenTopUI(bool needs_check_tab_fullscreen) override;
   bool ShouldHideTopUIForFullscreen() const override;
   void UpdateThrobber(bool running) override;
-  SkColor GetFrameForegroundColor(ActiveState active_state) const override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
index 4dafcf68..d032f7e 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -70,16 +70,13 @@
             ->hosted_app_controller()
             ->ShouldShowHostedAppButtonContainer()) {
       set_hosted_app_button_container(new HostedAppButtonContainer(
-          frame, browser_view, GetFrameForegroundColor(kActive),
-          GetFrameForegroundColor(kInactive), kHostedAppMenuMargin));
+          frame, browser_view, GetCaptionColor(kActive),
+          GetCaptionColor(kInactive), kHostedAppMenuMargin));
       AddChildView(hosted_app_button_container());
     }
 
     DCHECK(browser_view->ShouldShowWindowTitle());
     window_title_ = new views::Label(browser_view->GetWindowTitle());
-    // view::Label's readability algorithm conflicts with the one used by
-    // |GetFrameForegroundColor|.
-    window_title_->SetAutoColorReadabilityEnabled(false);
     AddChildView(window_title_);
   }
 }
@@ -211,11 +208,6 @@
 void BrowserNonClientFrameViewMac::UpdateThrobber(bool running) {
 }
 
-SkColor BrowserNonClientFrameViewMac::GetFrameForegroundColor(
-    ActiveState active_state) const {
-  return color_utils::GetThemedAssetColor(GetFrameColor(active_state));
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameViewMac, views::NonClientFrameView implementation:
 
@@ -286,7 +278,7 @@
 
   if (window_title_) {
     window_title_->SetBackgroundColor(frame_color);
-    window_title_->SetEnabledColor(GetFrameForegroundColor(kUseCurrent));
+    window_title_->SetEnabledColor(GetCaptionColor(kUseCurrent));
   }
 
   auto* theme_service =
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 5805b6ea..0b973c4 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -829,7 +829,7 @@
 }
 
 void BrowserView::UpdateLoadingAnimations(bool should_animate) {
-  if (should_animate || tabstrip_->IsAnyIconAnimating()) {
+  if (should_animate) {
     if (!loading_animation_timer_.IsRunning()) {
       // Loads are happening, and the timer isn't running, so start it.
       loading_animation_start_ = base::TimeTicks::Now();
@@ -2301,25 +2301,12 @@
   if (!browser_->ShouldCloseWindow())
     return false;
 
-  bool fast_tab_closing_enabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableFastUnload);
-
   if (!browser_->tab_strip_model()->empty()) {
     // Tab strip isn't empty.  Hide the frame (so it appears to have closed
     // immediately) and close all the tabs, allowing the renderers to shut
     // down. When the tab strip is empty we'll be called back again.
     frame_->Hide();
     browser_->OnWindowClosing();
-    if (fast_tab_closing_enabled)
-      browser_->tab_strip_model()->CloseAllTabs();
-    return false;
-  } else if (fast_tab_closing_enabled &&
-        !browser_->HasCompletedUnloadProcessing()) {
-    // The browser needs to finish running unload handlers.
-    // Hide the frame (so it appears to have closed immediately), and
-    // the browser will call us back again when it is ready to close.
-    frame_->Hide();
     return false;
   }
 
@@ -3044,6 +3031,10 @@
   return frame_->IsVisibleOnAllWorkspaces();
 }
 
+void BrowserView::ShowEmojiPanel() {
+  GetWidget()->ShowEmojiPanel();
+}
+
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 void BrowserView::ShowInProductHelpPromo(InProductHelpFeature iph_feature) {
   switch (iph_feature) {
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 4b21a6f..3036979 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -425,6 +425,8 @@
   std::string GetWorkspace() const override;
   bool IsVisibleOnAllWorkspaces() const override;
 
+  void ShowEmojiPanel() override;
+
   BookmarkBarView* GetBookmarkBarView() const;
   LocationBarView* GetLocationBarView() const;
 
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index a6e7f607..cfeee5f 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -66,7 +66,7 @@
 
 SkColor GlassBrowserFrameView::GetReadableFeatureColor(
     SkColor background_color) {
-  // color_utils::BlendTowardMaxContrast()/IsDark() aren't used here because
+  // color_utils::GetColorWithMaxContrast()/IsDark() aren't used here because
   // they switch based on the Chrome light/dark endpoints, while we want to use
   // the system native behavior below.
   return color_utils::GetLuma(background_color) < 128 ? SK_ColorWHITE
@@ -114,14 +114,12 @@
   extensions::HostedAppBrowserController* controller =
       browser_view->browser()->hosted_app_controller();
   if (controller && controller->ShouldShowHostedAppButtonContainer()) {
-    // TODO(alancutter): Avoid snapshotting GetFrameForegroundColor() values
-    // here and call it on demand in
-    // HostedAppButtonContainer::UpdateIconsColor() via a delegate interface.
-    SkColor active_color = GetFrameForegroundColor(kActive);
-    SkColor inactive_color = GetFrameForegroundColor(kInactive);
-
+    // TODO(alancutter): Avoid snapshotting GetCaptionColor() values here and
+    // call it on demand in HostedAppButtonContainer::UpdateIconsColor() via a
+    // delegate interface.
     set_hosted_app_button_container(new HostedAppButtonContainer(
-        frame, browser_view, active_color, inactive_color));
+        frame, browser_view, GetCaptionColor(kActive),
+        GetCaptionColor(kInactive)));
     AddChildView(hosted_app_button_container());
   }
 
@@ -238,8 +236,7 @@
          BrowserNonClientFrameView::IsSingleTabModeAvailable();
 }
 
-SkColor GlassBrowserFrameView::GetFrameForegroundColor(
-    ActiveState active_state) const {
+SkColor GlassBrowserFrameView::GetCaptionColor(ActiveState active_state) const {
   const SkAlpha title_alpha = ShouldPaintAsActive(active_state)
                                   ? SK_AlphaOPAQUE
                                   : kInactiveTitlebarFeatureAlpha;
@@ -650,7 +647,7 @@
   }
 
   if (ShowCustomTitle())
-    window_title_->SetEnabledColor(GetFrameForegroundColor(kUseCurrent));
+    window_title_->SetEnabledColor(GetCaptionColor(kUseCurrent));
 }
 
 void GlassBrowserFrameView::LayoutTitleBar() {
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index c7a6aa2..80157b77 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -23,7 +23,7 @@
  public:
   // Alpha to use for features in the titlebar (the window title and caption
   // buttons) when the window is inactive. They are opaque when active.
-  static constexpr SkAlpha kInactiveTitlebarFeatureAlpha = 0x65;
+  static constexpr SkAlpha kInactiveTitlebarFeatureAlpha = 0x66;
 
   static constexpr char kClassName[] = "GlassBrowserFrameView";
 
@@ -43,7 +43,7 @@
   void UpdateThrobber(bool running) override;
   gfx::Size GetMinimumSize() const override;
   bool IsSingleTabModeAvailable() const override;
-  SkColor GetFrameForegroundColor(ActiveState active_state) const override;
+  SkColor GetCaptionColor(ActiveState active_state) const override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index 3ca57a89..89b3422 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -33,7 +33,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/color_utils.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/image/canvas_image_source.h"
@@ -181,8 +180,6 @@
 
   window_title_ = new views::Label(browser_view()->GetWindowTitle());
   window_title_->SetVisible(browser_view()->ShouldShowWindowTitle());
-  // Readability is ensured by GetFrameForegroundColor().
-  window_title_->SetAutoColorReadabilityEnabled(false);
   window_title_->SetSubpixelRenderingEnabled(false);
   window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   window_title_->set_id(VIEW_ID_WINDOW_TITLE);
@@ -192,8 +189,8 @@
       browser_view()->browser()->hosted_app_controller();
   if (controller && controller->ShouldShowHostedAppButtonContainer()) {
     set_hosted_app_button_container(new HostedAppButtonContainer(
-        frame(), browser_view(), GetFrameForegroundColor(kActive),
-        GetFrameForegroundColor(kInactive)));
+        frame(), browser_view(), GetCaptionColor(kActive),
+        GetCaptionColor(kInactive)));
     AddChildView(hosted_app_button_container());
   }
 }
@@ -501,7 +498,8 @@
 
   const bool active = ShouldPaintAsActive();
   SkColor frame_color = GetFrameColor();
-  window_title_->SetEnabledColor(GetFrameForegroundColor(kUseCurrent));
+  window_title_->SetEnabledColor(GetCaptionColor(kUseCurrent));
+  window_title_->SetBackgroundColor(frame_color);
   frame_background_->set_frame_color(frame_color);
   frame_background_->set_use_custom_frame(frame()->UseCustomFrame());
   frame_background_->set_is_active(active);
@@ -516,15 +514,6 @@
   frame_background_->set_top_area_height(GetTopAreaHeight());
 
   if (GetFrameButtonStyle() == FrameButtonStyle::kMdButton) {
-    views::FrameCaptionButton::ColorMode color_mode =
-        views::FrameCaptionButton::ColorMode::kDefault;
-    extensions::HostedAppBrowserController* controller =
-        browser_view()->browser()->hosted_app_controller();
-    if (controller) {
-      color_mode = controller->GetThemeColor()
-                       ? views::FrameCaptionButton::ColorMode::kThemed
-                       : views::FrameCaptionButton::ColorMode::kDefault;
-    }
     for (auto* button :
          {minimize_button_, maximize_button_, restore_button_, close_button_}) {
       DCHECK_EQ(std::string(views::FrameCaptionButton::kViewClassName),
@@ -533,7 +522,6 @@
           static_cast<views::FrameCaptionButton*>(button);
       frame_caption_button->set_paint_as_active(active);
       frame_caption_button->SetBackgroundColor(frame_color);
-      frame_caption_button->SetColorMode(color_mode);
     }
   }
 
@@ -701,21 +689,6 @@
       IsMaximized());
 }
 
-SkColor OpaqueBrowserFrameView::GetFrameForegroundColor(
-    ActiveState active_state) const {
-  const SkColor frame_color = GetFrameColor(active_state);
-  if (browser_view()->IsBrowserTypeHostedApp()) {
-    const bool has_site_theme = browser_view()
-                                    ->browser()
-                                    ->hosted_app_controller()
-                                    ->GetThemeColor()
-                                    .has_value();
-    if (has_site_theme && !platform_observer_->IsUsingSystemTheme())
-      return color_utils::GetThemedAssetColor(frame_color);
-  }
-  return color_utils::GetColorWithMaxContrast(frame_color);
-}
-
 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(
     gfx::Canvas* canvas) const {
   const ui::ThemeProvider* tp = GetThemeProvider();
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index d3d4744..cc4a25ae 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -63,7 +63,6 @@
   int GetThemeBackgroundXInset() const override;
   void UpdateThrobber(bool running) override;
   gfx::Size GetMinimumSize() const override;
-  SkColor GetFrameForegroundColor(ActiveState active_state) const override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
index 53b75a6..e8a1364d 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -117,9 +117,8 @@
 IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, LightThemeColor) {
   if (!InstallAndLaunchHostedApp(SK_ColorYELLOW))
     return;
-  SkColor dark_yellow = SkColorSetRGB(92, 92, 0);
   EXPECT_EQ(hosted_app_button_container_->active_color_for_testing(),
-            dark_yellow);
+            gfx::kGoogleGrey900);
 }
 
 IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, DarkThemeColor) {
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
index cd513b6..ead580d 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -104,21 +104,16 @@
 // page.
 class CustomTabBarTitleOriginView : public views::View {
  public:
-  explicit CustomTabBarTitleOriginView(SkColor text_color) {
+  explicit CustomTabBarTitleOriginView(SkColor foreground_color,
+                                       SkColor background_color) {
     title_label_ = new views::Label(
         base::string16(), views::style::TextContext::CONTEXT_DIALOG_TITLE);
     location_label_ = new views::Label(base::string16());
 
-    // We need to disable auto color readability, as we want to match the active
-    // color in the title bar, which is subtly different.
-    // TODO(http://crbug.com/883177): Enable this if we use
-    // GetColorWithMinimumContrast() for the app title text instead of
-    // GetThemedAssetColor().
-    title_label_->SetAutoColorReadabilityEnabled(false);
-    location_label_->SetAutoColorReadabilityEnabled(false);
-
-    title_label_->SetEnabledColor(text_color);
-    location_label_->SetEnabledColor(text_color);
+    title_label_->SetEnabledColor(foreground_color);
+    title_label_->SetBackgroundColor(background_color);
+    location_label_->SetEnabledColor(foreground_color);
+    title_label_->SetBackgroundColor(background_color);
 
     AddChildView(title_label_);
     AddChildView(location_label_);
@@ -157,19 +152,20 @@
   theme_color_ = optional_theme_color.value_or(GetDefaultFrameColor());
   SetBackground(views::CreateSolidBackground(theme_color_));
 
-  text_color_ = browser_view->frame()->GetFrameView()->GetFrameForegroundColor(
+  caption_color_ = browser_view->frame()->GetFrameView()->GetCaptionColor(
       BrowserNonClientFrameView::kActive);
 
   const gfx::FontList& font_list = views::style::GetFont(
       CONTEXT_OMNIBOX_PRIMARY, views::style::STYLE_PRIMARY);
 
-  close_button_ = CreateCloseButton(this, text_color_);
+  close_button_ = CreateCloseButton(this, caption_color_);
   AddChildView(close_button_);
 
   location_icon_view_ = new LocationIconView(font_list, this);
   AddChildView(location_icon_view_);
 
-  title_origin_view_ = new CustomTabBarTitleOriginView(text_color_);
+  title_origin_view_ =
+      new CustomTabBarTitleOriginView(caption_color_, theme_color_);
   AddChildView(title_origin_view_);
 
   int padding = GetLayoutConstant(LayoutConstant::LOCATION_BAR_ELEMENT_PADDING);
@@ -186,7 +182,7 @@
       views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
 
   SetLayoutManager(std::move(layout));
-  SetBorder(views::CreateSolidSidedBorder(0, 0, 1, 0, text_color_));
+  SetBorder(views::CreateSolidSidedBorder(0, 0, 1, 0, caption_color_));
 
   tab_strip_model_observer_.Add(browser->tab_strip_model());
 }
@@ -236,8 +232,8 @@
 
 SkColor CustomTabBarView::GetSecurityChipColor(
     security_state::SecurityLevel security_level) const {
-  OmniboxTint tint =
-      color_utils::IsDark(text_color_) ? OmniboxTint::LIGHT : OmniboxTint::DARK;
+  OmniboxTint tint = color_utils::IsDark(caption_color_) ? OmniboxTint::LIGHT
+                                                         : OmniboxTint::DARK;
 
   return GetOmniboxSecurityChipColor(tint, security_level);
 }
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
index 10b6f7b..5f8ef85 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
@@ -59,7 +59,7 @@
 
  private:
   SkColor theme_color_;
-  SkColor text_color_;
+  SkColor caption_color_;
 
   base::string16 last_title_;
   base::string16 last_location_;
diff --git a/chrome/browser/ui/views/menu_model_adapter_test.cc b/chrome/browser/ui/views/menu_model_adapter_test.cc
index 7e52499..9021259 100644
--- a/chrome/browser/ui/views/menu_model_adapter_test.cc
+++ b/chrome/browser/ui/views/menu_model_adapter_test.cc
@@ -73,8 +73,6 @@
 
   ui::MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
 
-  void HighlightChangedTo(int index) override {}
-
   void ActivatedAt(int index) override {}
 
   void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) override {}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index baec691..0d5ffea 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -241,8 +241,7 @@
 void OmniboxMatchCellView::OnMatchUpdate(const OmniboxResultView* result_view,
                                          const AutocompleteMatch& match) {
   is_rich_suggestion_ =
-      (OmniboxFieldTrial::IsNewAnswerLayoutEnabled() &&
-       (!!match.answer || match.type == AutocompleteMatchType::CALCULATOR)) ||
+      (!!match.answer || match.type == AutocompleteMatchType::CALCULATOR) ||
       (OmniboxFieldTrial::IsRichEntitySuggestionsEnabled() &&
        !match.image_url.empty());
   is_search_type_ = AutocompleteMatch::IsSearchType(match.type);
@@ -268,8 +267,7 @@
     icon_view_->SetSize(icon_view_->CalculatePreferredSize());
   }
 
-  if (OmniboxFieldTrial::IsNewAnswerLayoutEnabled() &&
-      match.type == AutocompleteMatchType::CALCULATOR) {
+  if (match.type == AutocompleteMatchType::CALCULATOR) {
     answer_image_view_->SetImage(
         ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
             IDR_OMNIBOX_CALCULATOR_ROUND));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
index 297f67fc..093d4d9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
@@ -57,83 +57,6 @@
   gfx::BaselineStyle baseline = gfx::NORMAL_BASELINE;
 };
 
-// Returns the styles that should be applied to the specified answer text type.
-//
-// Note that the font value is only consulted for the first text type that
-// appears on an answer line, because RenderText does not yet support multiple
-// font sizes. Subsequent text types on the same line will share the text size
-// of the first type, while the color and baseline styles specified here will
-// always apply. The gfx::INFERIOR baseline style is used as a workaround to
-// produce smaller text on the same line. The way this is used in the current
-// set of answers is that the small types (TOP_ALIGNED, DESCRIPTION_NEGATIVE,
-// DESCRIPTION_POSITIVE and SUGGESTION_SECONDARY_TEXT_SMALL) only ever appear
-// following LargeFont text, so for consistency they specify LargeFont for the
-// first value even though this is not actually used (since they're not the
-// first value).
-TextStyle GetTextStyle(int text_type) {
-  // The size delta for large fonts in the legacy spec (per comment above, the
-  // result is usually smaller due to the baseline style).
-  constexpr int kLarge = ui::ResourceBundle::kLargeFontDelta;
-
-  // The size delta for the smaller of font size in the touchable style. This
-  // will always use the same baseline style.
-  constexpr int kTouchableSmall = -3;
-
-  switch (text_type) {
-    case SuggestionAnswer::TOP_ALIGNED:
-      return {OmniboxPart::RESULTS_TEXT_DIMMED, kLarge, kTouchableSmall,
-              gfx::SUPERIOR};
-
-    case SuggestionAnswer::DESCRIPTION_NEGATIVE:
-      return {OmniboxPart::RESULTS_TEXT_NEGATIVE, kLarge, kTouchableSmall,
-              gfx::INFERIOR};
-
-    case SuggestionAnswer::DESCRIPTION_POSITIVE:
-      return {OmniboxPart::RESULTS_TEXT_POSITIVE, kLarge, kTouchableSmall,
-              gfx::INFERIOR};
-
-    case SuggestionAnswer::ANSWER_TEXT_MEDIUM:
-      return {OmniboxPart::RESULTS_TEXT_DIMMED};
-
-    case SuggestionAnswer::ANSWER_TEXT_LARGE:
-      // Note: There is no large font in the touchable spec.
-      return {OmniboxPart::RESULTS_TEXT_DIMMED, kLarge};
-
-    case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_SMALL:
-      return {OmniboxPart::RESULTS_TEXT_DIMMED, kLarge, kTouchableSmall,
-              gfx::INFERIOR};
-
-    case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_MEDIUM:
-      return {OmniboxPart::RESULTS_TEXT_DIMMED};
-
-    case SuggestionAnswer::PERSONALIZED_SUGGESTION:
-    case SuggestionAnswer::SUGGESTION:  // Fall through.
-    default:
-      return {OmniboxPart::RESULTS_TEXT_DEFAULT};
-  }
-}
-
-const gfx::FontList& GetFontForType(int text_type) {
-  const gfx::FontList& omnibox_font =
-      views::style::GetFont(CONTEXT_OMNIBOX_PRIMARY, kTextStyle);
-  if (ui::MaterialDesignController::touch_ui()) {
-    int delta = GetTextStyle(text_type).touchable_size_delta;
-    if (delta == 0)
-      return omnibox_font;
-
-    // Use the cache in ResourceBundle (gfx::FontList::Derive() is slow and
-    // doesn't return a reference).
-    return ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
-        omnibox_font.GetFontSize() - gfx::FontList().GetFontSize() + delta);
-  }
-
-  int delta = GetTextStyle(text_type).legacy_size_delta;
-  if (delta == kInherit)
-    return omnibox_font;
-
-  return ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(delta);
-}
-
 // The new answer layout has separate and different treatment of text styles,
 // and as of writing both styling approaches need to be supported.  When old
 // answer styles are deprecated, the above TextStyle structure and related
@@ -285,14 +208,6 @@
   render_text_.reset();
   render_text_ = CreateRenderText(base::string16());
 
-  if (!OmniboxFieldTrial::IsNewAnswerLayoutEnabled()) {
-    // This assumes that the first text type in the line can be used to specify
-    // the font for all the text fields in the line.  For now this works but
-    // eventually it may be necessary to get RenderText to support multiple font
-    // sizes or use multiple RenderTexts.
-    render_text_->SetFontList(GetFontForType(line.text_fields()[0].type()));
-  }
-
   for (const SuggestionAnswer::TextField& text_field : line.text_fields())
     AppendText(text_field, base::string16());
   if (!line.text_fields().empty()) {
@@ -386,26 +301,7 @@
   int offset = render_text_->text().length();
   gfx::Range range(offset, offset + text.length());
   render_text_->AppendText(text);
-  if (OmniboxFieldTrial::IsNewAnswerLayoutEnabled()) {
-    ApplyTextStyleForType(field.style(), result_view_, render_text_.get(),
-                          range);
-  } else {
-    const int text_type = field.type();
-    const TextStyle& text_style = GetTextStyle(text_type);
-    // TODO(dschuyler): follow up on the problem of different font sizes within
-    // one RenderText.  Maybe with render_text_->SetFontList(...).
-    render_text_->ApplyWeight(gfx::Font::Weight::NORMAL, range);
-    render_text_->ApplyColor(result_view_->GetColor(text_style.part), range);
-
-    // Baselines are always aligned under the touch UI. Font sizes change
-    // instead.
-    if (!ui::MaterialDesignController::touch_ui()) {
-      render_text_->ApplyBaselineStyle(text_style.baseline, range);
-    } else if (text_style.touchable_size_delta != 0) {
-      render_text_->ApplyFontSizeOverride(
-          GetFontForType(text_type).GetFontSize(), range);
-    }
-  }
+  ApplyTextStyleForType(field.style(), result_view_, render_text_.get(), range);
 }
 
 void OmniboxTextView::UpdateLineHeight() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index dbae5ab..8ab5193 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -724,37 +724,29 @@
 }
 
 bool OmniboxViewViews::UnapplySteadyStateElisions(UnelisionGesture gesture) {
-  // No need to update the text if the user is already inputting text.
-  if (model()->user_input_in_progress())
-    return false;
-
-  // No need to unelide if we are already displaying the full URL.
-  LocationBarModel* location_bar_model = controller()->GetLocationBarModel();
-  base::string16 full_url = location_bar_model->GetFormattedFullURL();
-  if (text() == full_url)
-    return false;
-
-  // Don't unelide if we are currently displaying Query in Omnibox search terms,
-  // as otherwise, it would be impossible to refine query terms.
-  if (location_bar_model->GetDisplaySearchTerms(nullptr /* search_terms */))
-    return false;
-
   // If everything is selected, the user likely does not intend to edit the URL.
   // But if the Home key is pressed, the user probably does want to interact
   // with the beginning of the URL - in which case we unelide.
   if (IsSelectAll() && gesture != UnelisionGesture::HOME_KEY_PRESSED)
     return false;
 
+  // Get the original selection bounds so we can adjust it later.
   size_t start, end;
   GetSelectionBounds(&start, &end);
 
+  // Try to unelide. Early exit if there's no unelisions to perform.
+  base::string16 original_text = GetText();
+  base::string16 original_selected_text = GetSelectedText();
+  if (!model()->Unelide(false /* exit_query_in_omnibox */))
+    return false;
+
   // Find the length of the prefix that was chopped off to form the elided URL.
   // This simple logic only works because we elide only prefixes from the full
   // URL. Otherwise, we would have to use the FormatURL offset adjustments.
-  size_t offset = full_url.find(GetText());
+  size_t offset = GetText().find(original_text);
   if (offset != base::string16::npos) {
     if (start != end && gesture == UnelisionGesture::MOUSE_RELEASE &&
-        !model()->ClassifiesAsSearch(GetSelectedText())) {
+        !model()->ClassifiesAsSearch(original_selected_text)) {
       // For user selections that look like a URL instead of a Search:
       // If we are uneliding at the end of a drag-select (on mouse release),
       // and the selection spans to the beginning of the elided URL, ensure that
@@ -775,8 +767,6 @@
     OffsetDoubleClickWord(offset);
   }
 
-  // We have already early-exited if Query in Omnibox is active.
-  model()->Unelide(false /* exit_query_in_omnibox */);
   SelectRange(gfx::Range(start, end));
   return true;
 }
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 98d241b..1ba07cd 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -506,6 +506,11 @@
     layout->AddView(CreateSiteSettingsLink(side_margin, this).release());
 
   views::BubbleDialogDelegateView::CreateBubble(this);
+
+  // CreateBubble() may not set our size synchronously so explicitly set it here
+  // before PageInfo updates trigger child layouts.
+  SetSize(GetPreferredSize());
+
   presenter_.reset(new PageInfo(
       this, profile, TabSpecificContentSettings::FromWebContents(web_contents),
       web_contents, url, security_info));
@@ -526,7 +531,8 @@
 
 void PageInfoBubbleView::OnChosenObjectDeleted(
     const PageInfoUI::ChosenObjectInfo& info) {
-  presenter_->OnSiteChosenObjectDeleted(info.ui_info, *info.object);
+  presenter_->OnSiteChosenObjectDeleted(info.ui_info,
+                                        info.chooser_object->value);
 }
 
 void PageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index fed18ec..9cb9dcc9 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -20,7 +20,6 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
@@ -175,10 +174,11 @@
   AddChildView(password_view_);
   PasswordSelectionUpdated();
 
-  views::StyledLabel* help_label =
-      new views::StyledLabel(controller_->HelpText(), this);
-  help_label->SetTextContext(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE);
-  help_label->SetDefaultTextStyle(STYLE_SECONDARY);
+  views::Label* help_label = new views::Label(
+      controller_->HelpText(), ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+      STYLE_SECONDARY);
+  help_label->SetMultiLine(true);
+  help_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   help_label->SetBackground(
       views::CreateSolidBackground(GetFooterBackgroundColor()));
   help_label->SetBorder(
@@ -214,19 +214,12 @@
 
 gfx::Size PasswordGenerationPopupViewViews::CalculatePreferredSize() const {
   int width =
-      std::max(GetLayoutManager()->GetPreferredSize(this).width(),
+      std::max(password_view_->GetPreferredSize().width(),
                gfx::ToEnclosingRect(controller_->element_bounds()).width());
   width = std::min(width, kPasswordGenerationMaxWidth);
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-void PasswordGenerationPopupViewViews::StyledLabelLinkClicked(
-    views::StyledLabel* label,
-    const gfx::Range& range,
-    int event_flags) {
-  controller_->OnSavedPasswordsLinkClicked();
-}
-
 PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
     PasswordGenerationPopupController* controller) {
   if (!controller->container_view())
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.h b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.h
index 4eed877..2ef0f02 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.h
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.h
@@ -8,13 +8,11 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/passwords/password_generation_popup_view.h"
 #include "chrome/browser/ui/views/autofill/autofill_popup_base_view.h"
-#include "ui/views/controls/styled_label_listener.h"
 
 class PasswordGenerationPopupController;
 
 class PasswordGenerationPopupViewViews : public autofill::AutofillPopupBaseView,
-                                         public PasswordGenerationPopupView,
-                                         public views::StyledLabelListener {
+                                         public PasswordGenerationPopupView {
  public:
   PasswordGenerationPopupViewViews(
       PasswordGenerationPopupController* controller,
@@ -41,11 +39,6 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   gfx::Size CalculatePreferredSize() const override;
 
-  // views::StyledLabelListener implementation
-  void StyledLabelLinkClicked(views::StyledLabel* label,
-                              const gfx::Range& range,
-                              int event_flags) override;
-
   // Sub view that displays the actual generated password.
   GeneratedPasswordBox* password_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index 2368399..d7149d05 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -15,9 +15,7 @@
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/sync/sync_ui_util.h"
 #include "chrome/browser/themes/theme_properties.h"
@@ -61,7 +59,7 @@
 #endif  // !defined(OS_CHROMEOS)
       browser_list_observer_(this),
       profile_observer_(this),
-      cookie_manager_service_observer_(this),
+      identity_manager_observer_(this),
       account_tracker_service_observer_(this) {
 
   if (IsIncognitoCounterActive())
@@ -71,8 +69,8 @@
       &g_browser_process->profile_manager()->GetProfileAttributesStorage());
 
   if (!IsIncognito() && !profile_->IsGuestSession()) {
-    cookie_manager_service_observer_.Add(
-        GaiaCookieManagerServiceFactory::GetForProfile(profile_));
+    identity_manager_observer_.Add(
+        IdentityManagerFactory::GetForProfile(profile_));
     account_tracker_service_observer_.Add(
         AccountTrackerServiceFactory::GetForProfile(profile_));
   }
@@ -218,9 +216,8 @@
   UpdateText();
 }
 
-void AvatarToolbarButton::OnGaiaAccountsInCookieUpdated(
-    const std::vector<gaia::ListedAccount>& accounts,
-    const std::vector<gaia::ListedAccount>& signed_out_accounts,
+void AvatarToolbarButton::OnAccountsInCookieUpdated(
+    const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
     const GoogleServiceAuthError& error) {
   UpdateIcon();
 }
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
index aff14df..b3e3ada 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.h
@@ -15,7 +15,7 @@
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/material_design/material_design_controller_observer.h"
 #include "ui/events/event.h"
 
@@ -25,7 +25,7 @@
                             public AvatarButtonErrorControllerDelegate,
                             public BrowserListObserver,
                             public ProfileAttributesStorage::Observer,
-                            public GaiaCookieManagerService::Observer,
+                            public identity::IdentityManager::Observer,
                             public AccountTrackerService::Observer,
                             public ui::MaterialDesignControllerObserver {
  public:
@@ -59,11 +59,10 @@
   void OnProfileNameChanged(const base::FilePath& profile_path,
                             const base::string16& old_profile_name) override;
 
-  // GaiaCookieManagerService::Observer:
+  // IdentityManager::Observer:
   // Needed if the first sync promo account should be displayed.
-  void OnGaiaAccountsInCookieUpdated(
-      const std::vector<gaia::ListedAccount>& accounts,
-      const std::vector<gaia::ListedAccount>& signed_out_accounts,
+  void OnAccountsInCookieUpdated(
+      const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
       const GoogleServiceAuthError& error) override;
 
   // AccountTrackerService::Observer:
@@ -93,8 +92,8 @@
   ScopedObserver<BrowserList, BrowserListObserver> browser_list_observer_;
   ScopedObserver<ProfileAttributesStorage, AvatarToolbarButton>
       profile_observer_;
-  ScopedObserver<GaiaCookieManagerService, AvatarToolbarButton>
-      cookie_manager_service_observer_;
+  ScopedObserver<identity::IdentityManager, AvatarToolbarButton>
+      identity_manager_observer_;
   ScopedObserver<AccountTrackerService, AvatarToolbarButton>
       account_tracker_service_observer_;
   ScopedObserver<ui::MaterialDesignController, AvatarToolbarButton>
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index d506263..a05c4d04 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -25,7 +25,6 @@
 
 namespace {
 
-const int kFixedGaiaViewHeight = 612;
 const int kModalDialogWidth = 448;
 const int kModalDialogWidthForUnifiedConsent = 512;
 const int kSyncConfirmationDialogHeight = 487;
@@ -162,6 +161,7 @@
   content_view_->RequestFocus();
 }
 
+#if defined(OS_CHROMEOS)
 // static
 std::unique_ptr<views::WebView>
 SigninViewControllerDelegateViews::CreateGaiaWebView(
@@ -169,10 +169,10 @@
     profiles::BubbleViewMode mode,
     Browser* browser,
     signin_metrics::AccessPoint access_point) {
-  GURL url =
-      signin::GetSigninURLFromBubbleViewMode(
-          browser->profile(), mode, access_point);
+  GURL url = signin::GetEmbeddedSigninURLFromBubbleViewMode(browser->profile(),
+                                                            mode, access_point);
 
+  constexpr int kFixedGaiaViewHeight = 612;
   int max_height = browser
       ->window()
       ->GetWebContentsModalDialogHost()
@@ -194,6 +194,7 @@
 
   return std::unique_ptr<views::WebView>(web_view);
 }
+#endif
 
 std::unique_ptr<views::WebView>
 SigninViewControllerDelegateViews::CreateSyncConfirmationWebView(
@@ -237,6 +238,7 @@
   return std::unique_ptr<views::WebView>(web_view);
 }
 
+#if defined(OS_CHROMEOS)
 SigninViewControllerDelegate*
 SigninViewControllerDelegate::CreateModalSigninDelegate(
     SigninViewController* signin_view_controller,
@@ -249,6 +251,7 @@
           nullptr, mode, browser, access_point),
       browser, ui::MODAL_TYPE_CHILD, false);
 }
+#endif
 
 SigninViewControllerDelegate*
 SigninViewControllerDelegate::CreateSyncConfirmationDelegate(
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
index eebf181..ffa6ab6 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
@@ -32,14 +32,6 @@
 class SigninViewControllerDelegateViews : public views::DialogDelegateView,
                                           public SigninViewControllerDelegate {
  public:
-  // Creates the web view that contains the signin flow in |mode| using
-  // |profile| as the web content's profile, then sets |delegate| as the created
-  // web content's delegate.
-  static std::unique_ptr<views::WebView> CreateGaiaWebView(
-      content::WebContentsDelegate* delegate,
-      profiles::BubbleViewMode mode,
-      Browser* browser,
-      signin_metrics::AccessPoint access_point);
 
   static std::unique_ptr<views::WebView> CreateSyncConfirmationWebView(
       Browser* browser);
@@ -59,6 +51,17 @@
  private:
   friend SigninViewControllerDelegate;
 
+#if defined(OS_CHROMEOS)
+  // Creates the web view that contains the signin flow in |mode| using
+  // |profile| as the web content's profile, then sets |delegate| as the created
+  // web content's delegate.
+  static std::unique_ptr<views::WebView> CreateGaiaWebView(
+      content::WebContentsDelegate* delegate,
+      profiles::BubbleViewMode mode,
+      Browser* browser,
+      signin_metrics::AccessPoint access_point);
+#endif
+
   // Creates and displays a constrained window containing |web_contents|. If
   // |wait_for_size| is true, the delegate will wait for ResizeNativeView() to
   // be called by the base class before displaying the constrained window.
diff --git a/chrome/browser/ui/views/profiles/user_manager_view.cc b/chrome/browser/ui/views/profiles/user_manager_view.cc
index 5f4acf47..8ab0611 100644
--- a/chrome/browser/ui/views/profiles/user_manager_view.cc
+++ b/chrome/browser/ui/views/profiles/user_manager_view.cc
@@ -231,48 +231,40 @@
 // -------------------------------------------------------------
 
 // static
-void UserManagerProfileDialog::ShowReauthDialog(
+void UserManagerProfileDialog::ShowUnlockDialog(
     content::BrowserContext* browser_context,
-    const std::string& email,
-    signin_metrics::Reason reason) {
-  ShowReauthDialogWithProfilePath(browser_context, email, base::FilePath(),
-                                  reason);
+    const std::string& email) {
+  ShowUnlockDialogWithProfilePath(browser_context, email, base::FilePath());
 }
 
 // static
-void UserManagerProfileDialog::ShowReauthDialogWithProfilePath(
+void UserManagerProfileDialog::ShowUnlockDialogWithProfilePath(
     content::BrowserContext* browser_context,
     const std::string& email,
-    const base::FilePath& profile_path,
-    signin_metrics::Reason reason) {
-  CHECK(signin_util::IsForceSigninEnabled() ||
-        reason != signin_metrics::Reason::REASON_UNLOCK)
-      << "Legacy supervised users are no longer supported.";
+    const base::FilePath& profile_path) {
   // This method should only be called if the user manager is already showing.
   if (!UserManager::IsShowing())
     return;
   // Load the re-auth URL, prepopulated with the user's email address.
   // Add the index of the profile to the URL so that the inline login page
   // knows which profile to load and update the credentials.
-  GURL url = signin::GetReauthURLWithEmailForDialog(
-      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER, reason, email);
+  GURL url = signin::GetEmbeddedReauthURLWithEmail(
+      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
+      signin_metrics::Reason::REASON_UNLOCK, email);
   g_user_manager_view->SetSigninProfilePath(profile_path);
   g_user_manager_view->ShowDialog(browser_context, email, url);
 }
 
 // static
-void UserManagerProfileDialog::ShowSigninDialog(
+void UserManagerProfileDialog::ShowForceSigninDialog(
     content::BrowserContext* browser_context,
-    const base::FilePath& profile_path,
-    signin_metrics::Reason reason) {
+    const base::FilePath& profile_path) {
   if (!UserManager::IsShowing())
     return;
-  DCHECK(reason ==
-             signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT ||
-         reason == signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT);
   g_user_manager_view->SetSigninProfilePath(profile_path);
-  GURL url = signin::GetPromoURLForDialog(
-      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER, reason, true);
+  GURL url = signin::GetEmbeddedPromoURL(
+      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER,
+      signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT, true);
   g_user_manager_view->ShowDialog(browser_context, std::string(), url);
 }
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
index c5cc52f..26ac390 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -53,6 +54,7 @@
 RelaunchNotificationController::RelaunchNotificationController(
     UpgradeDetector* upgrade_detector)
     : RelaunchNotificationController(upgrade_detector,
+                                     base::DefaultClock::GetInstance(),
                                      base::DefaultTickClock::GetInstance()) {}
 
 RelaunchNotificationController::~RelaunchNotificationController() {
@@ -65,12 +67,13 @@
 
 RelaunchNotificationController::RelaunchNotificationController(
     UpgradeDetector* upgrade_detector,
+    const base::Clock* clock,
     const base::TickClock* tick_clock)
     : upgrade_detector_(upgrade_detector),
-      tick_clock_(tick_clock),
+      clock_(clock),
       last_notification_style_(NotificationStyle::kNone),
       last_level_(UpgradeDetector::UPGRADE_ANNOYANCE_NONE),
-      timer_(tick_clock_) {
+      timer_(clock_, tick_clock) {
   PrefService* local_state = g_browser_process->local_state();
   if (local_state) {
     pref_change_registrar_.Init(local_state);
@@ -88,7 +91,7 @@
   DCHECK_NE(last_notification_style_, NotificationStyle::kNone);
   UpgradeDetector::UpgradeNotificationAnnoyanceLevel current_level =
       upgrade_detector_->upgrade_notification_stage();
-  const base::TimeTicks current_high_deadline =
+  const base::Time current_high_deadline =
       upgrade_detector_->GetHighAnnoyanceDeadline();
 
   // Nothing to do if there has been no change in the level and deadline. If
@@ -180,10 +183,14 @@
 
 void RelaunchNotificationController::ShowRelaunchNotification(
     UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
-    base::TimeTicks high_deadline) {
+    base::Time high_deadline) {
   DCHECK_NE(last_notification_style_, NotificationStyle::kNone);
 
   if (last_notification_style_ == NotificationStyle::kRecommended) {
+    // Show the dialog if there has been a level change.
+    if (level != last_level_)
+      NotifyRelaunchRecommended();
+
     // If this is the final showing (the one at the "high" level), start the
     // timer to reshow the bubble at each "elevated to high" interval.
     if (level == UpgradeDetector::UPGRADE_ANNOYANCE_HIGH) {
@@ -193,10 +200,6 @@
       // lower level.
       timer_.Stop();
     }
-
-    // Show the dialog if there has been a level change.
-    if (level != last_level_)
-      NotifyRelaunchRecommended();
   } else {
     HandleRelaunchRequiredState(level, high_deadline);
   }
@@ -222,26 +225,26 @@
 
 void RelaunchNotificationController::HandleRelaunchRequiredState(
     UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
-    base::TimeTicks high_deadline) {
+    base::Time high_deadline) {
   DCHECK_EQ(last_notification_style_, NotificationStyle::kRequired);
 
   // Make no changes if the new deadline is not in the future and the browser is
   // within the grace period of the previous deadline. The user has already been
   // given the three-minute countdown so just let it go.
-  const base::TimeTicks now = tick_clock_->NowTicks();
+  const base::Time now = clock_->Now();
   if (timer_.IsRunning()) {
-    const base::TimeTicks& desired_run_time = timer_.desired_run_time();
+    const base::Time& desired_run_time = timer_.desired_run_time();
     DCHECK(!desired_run_time.is_null());
     if (high_deadline <= now && desired_run_time - now <= kRelaunchGracePeriod)
       return;
   }
 
   // Compute the new deadline (minimally three minutes into the future).
-  const base::TimeTicks deadline =
+  const base::Time deadline =
       std::max(high_deadline, now) + kRelaunchGracePeriod;
 
   // (re)Start the timer to perform the relaunch when the deadline is reached.
-  timer_.Start(FROM_HERE, deadline - now, this,
+  timer_.Start(FROM_HERE, deadline, this,
                &RelaunchNotificationController::OnRelaunchDeadlineExpired);
 
   if (platform_impl_.IsRequiredNotificationShown()) {
@@ -254,34 +257,15 @@
       NotifyRelaunchRequired();
   }
 }
-
 void RelaunchNotificationController::StartReshowTimer() {
   DCHECK_EQ(last_notification_style_, NotificationStyle::kRecommended);
-  base::TimeDelta delay = upgrade_detector_->GetHighAnnoyanceLevelDelta();
-  if (timer_.IsRunning()) {
-    // Leave well enough alone if the timer is already running with the proper
-    // frequency. This should not happen given the early-exit in
-    // OnUpgradeRecommended when there is no change in the annoyance level or
-    // high annoyance deadline.
-    if (timer_.GetCurrentDelay() == delay)
-      return;
-
-    // Compute the new delay to have the previously-scheduled reshow appear at
-    // the right time.
-    DCHECK(!timer_.desired_run_time().is_null());
-    const base::TimeTicks start_time =
-        timer_.desired_run_time() - timer_.GetCurrentDelay();
-    const base::TimeTicks new_run_time = start_time + delay;
-    const base::TimeTicks now = tick_clock_->NowTicks();
-    if (new_run_time <= now) {
-      // The new delay puts the next reshow in the past. Handle it now.
-      timer_.Stop();
-      OnReshowRelaunchRecommended();
-      return;
-    }
-    delay = new_run_time - now;
-  }
-  timer_.Start(FROM_HERE, delay, this,
+  DCHECK(!last_relaunch_notification_time_.is_null());
+  const auto high_annoyance_delta =
+      upgrade_detector_->GetHighAnnoyanceLevelDelta();
+  // Compute the next time to show the notification.
+  const auto desired_run_time =
+      last_relaunch_notification_time_ + high_annoyance_delta;
+  timer_.Start(FROM_HERE, desired_run_time, this,
                &RelaunchNotificationController::OnReshowRelaunchRecommended);
 }
 
@@ -292,6 +276,11 @@
 }
 
 void RelaunchNotificationController::NotifyRelaunchRecommended() {
+  last_relaunch_notification_time_ = clock_->Now();
+  DoNotifyRelaunchRecommended();
+}
+
+void RelaunchNotificationController::DoNotifyRelaunchRecommended() {
   platform_impl_.NotifyRelaunchRecommended(
       upgrade_detector_->upgrade_detected_time());
 }
@@ -299,8 +288,12 @@
 void RelaunchNotificationController::NotifyRelaunchRequired() {
   DCHECK(timer_.IsRunning());
   DCHECK(!timer_.desired_run_time().is_null());
+  DoNotifyRelaunchRequired(timer_.desired_run_time());
+}
 
-  platform_impl_.NotifyRelaunchRequired(timer_.desired_run_time());
+void RelaunchNotificationController::DoNotifyRelaunchRequired(
+    base::Time deadline) {
+  platform_impl_.NotifyRelaunchRequired(deadline);
 }
 
 void RelaunchNotificationController::Close() {
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
index 9db9812..52dc836 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
+#include "chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h"
 #include "chrome/browser/upgrade_detector/upgrade_detector.h"
 #include "chrome/browser/upgrade_detector/upgrade_observer.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -19,6 +19,7 @@
 #endif  // defined(OS_CHROMEOS)
 
 namespace base {
+class Clock;
 class TickClock;
 }
 
@@ -59,6 +60,7 @@
       base::TimeDelta::FromMinutes(3);
 
   RelaunchNotificationController(UpgradeDetector* upgrade_detector,
+                                 const base::Clock* clock,
                                  const base::TickClock* tick_clock);
 
   // UpgradeObserver:
@@ -90,7 +92,7 @@
   // for further details.
   void ShowRelaunchNotification(
       UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
-      base::TimeTicks high_deadline);
+      base::Time high_deadline);
 
   // Closes any previously-shown notifications. This is safe to call if no
   // notifications have been shown. Notifications may be closed by other means
@@ -113,16 +115,23 @@
   // relaunch required notification (if shown), and showing it if needed.
   void HandleRelaunchRequiredState(
       UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
-      base::TimeTicks high_deadline);
+      base::Time high_deadline);
+
+  // Update |last_relaunch_notification_time_| before calling
+  // DoNotifyRelaunchRecommended.
+  void NotifyRelaunchRecommended();
+
+  // Provide deadline to DoNotifyRelaunchRequired.
+  virtual void NotifyRelaunchRequired();
 
   // The following methods, which are invoked by the controller to show or close
   // notifications, are virtual for the sake of testing.
 
   // Shows the relaunch recommended notification if it is not already open.
-  virtual void NotifyRelaunchRecommended();
+  virtual void DoNotifyRelaunchRecommended();
 
   // Shows the relaunch required notification if it is not already open.
-  virtual void NotifyRelaunchRequired();
+  virtual void DoNotifyRelaunchRequired(base::Time deadline);
 
   // Closes bubble or dialog if either is still open on desktop, or sets the
   // default notification on Chrome OS.
@@ -135,9 +144,9 @@
   // The process-wide upgrade detector.
   UpgradeDetector* const upgrade_detector_;
 
-  // A provider of TimeTicks to the controller and its timer for the sake of
+  // A provider of Time to the controller and its timer for the sake of
   // testability.
-  const base::TickClock* const tick_clock_;
+  const base::Clock* const clock_;
 
   // Observes changes to the browser.relaunch_notification Local State pref.
   PrefChangeRegistrar pref_change_registrar_;
@@ -155,12 +164,15 @@
   UpgradeDetector::UpgradeNotificationAnnoyanceLevel last_level_;
 
   // The last observed high annoyance deadline.
-  base::TimeTicks last_high_deadline_;
+  base::Time last_high_deadline_;
+
+  // The last time recommended relaunch notification triggered
+  base::Time last_relaunch_notification_time_;
 
   // A timer used either to repeatedly reshow the relaunch recommended bubble
   // once the high annoyance level has been reached, or to trigger browser
   // relaunch once the relaunch required dialog's deadline is reached.
-  base::OneShotTimer timer_;
+  WallClockTimer timer_;
 
   DISALLOW_COPY_AND_ASSIGN(RelaunchNotificationController);
 };
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
index ba52c72..d469554 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.cc
@@ -19,7 +19,7 @@
     ~RelaunchNotificationControllerPlatformImpl() = default;
 
 void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRecommended(
-    base::TimeTicks detection_time) {
+    base::Time detection_time) {
   if (!relaunch_recommended_timer_) {
     relaunch_recommended_timer_ = std::make_unique<RelaunchRecommendedTimer>(
         detection_time,
@@ -35,7 +35,7 @@
 }
 
 void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRequired(
-    base::TimeTicks deadline) {
+    base::Time deadline) {
   if (!relaunch_required_timer_) {
     relaunch_required_timer_ = std::make_unique<RelaunchRequiredTimer>(
         deadline,
@@ -60,7 +60,7 @@
 }
 
 void RelaunchNotificationControllerPlatformImpl::SetDeadline(
-    base::TimeTicks deadline) {
+    base::Time deadline) {
   relaunch_required_timer_->SetDeadline(deadline);
 }
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h
index 9bdf82d4..25cf9e2 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_chromeos.h
@@ -19,17 +19,17 @@
   ~RelaunchNotificationControllerPlatformImpl();
 
   // Shows the relaunch recommended notification if it is not already open.
-  void NotifyRelaunchRecommended(base::TimeTicks detection_time);
+  void NotifyRelaunchRecommended(base::Time detection_time);
 
   // Shows the relaunch required notification if it is not already open.
-  void NotifyRelaunchRequired(base::TimeTicks deadline);
+  void NotifyRelaunchRequired(base::Time deadline);
 
   // Sets the notification title to the default one on Chrome OS.
   void CloseRelaunchNotification();
 
   // Sets the relaunch deadline to |deadline| and refreshes the notification's
   // title accordingly.
-  void SetDeadline(base::TimeTicks deadline);
+  void SetDeadline(base::Time deadline);
 
   // Returns true if relaunch required notification is shown.
   bool IsRequiredNotificationShown() const;
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.cc
index 1daced2..ed33f73 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.cc
@@ -51,7 +51,7 @@
     RelaunchNotificationControllerPlatformImpl() = default;
 
 void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRecommended(
-    base::TimeTicks detection_time) {
+    base::Time detection_time) {
   // Nothing to do if the bubble is visible.
   if (widget_)
     return;
@@ -72,7 +72,7 @@
 }
 
 void RelaunchNotificationControllerPlatformImpl::NotifyRelaunchRequired(
-    base::TimeTicks deadline) {
+    base::Time deadline) {
   // Nothing to do if the dialog is visible.
   if (widget_)
     return;
@@ -98,7 +98,7 @@
 }
 
 void RelaunchNotificationControllerPlatformImpl::SetDeadline(
-    base::TimeTicks deadline) {
+    base::Time deadline) {
   DCHECK(widget_);
   RelaunchRequiredDialogView::FromWidget(widget_)->SetDeadline(deadline);
 }
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.h b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.h
index fe8d4ff..85d2567f 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_platform_impl_desktop.h
@@ -19,17 +19,17 @@
   RelaunchNotificationControllerPlatformImpl();
 
   // Shows the relaunch recommended notification if it is not already open.
-  void NotifyRelaunchRecommended(base::TimeTicks detection_time);
+  void NotifyRelaunchRecommended(base::Time detection_time);
 
   // Shows the relaunch required notification if it is not already open.
-  void NotifyRelaunchRequired(base::TimeTicks deadline);
+  void NotifyRelaunchRequired(base::Time deadline);
 
   // Closes the bubble or dialog if either is still open.
   void CloseRelaunchNotification();
 
   // Sets the relaunch deadline to |deadline| and refreshes the notification's
   // title accordingly.
-  void SetDeadline(base::TimeTicks deadline);
+  void SetDeadline(base::Time deadline);
 
   // Checks whether the required dialog is shown or not.
   bool IsRequiredNotificationShown() const;
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
index ece4f49..dc767cf 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
@@ -5,10 +5,14 @@
 #include "chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/time/clock.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -39,19 +43,20 @@
     : public RelaunchNotificationController {
  public:
   FakeRelaunchNotificationController(UpgradeDetector* upgrade_detector,
+                                     const base::Clock* clock,
                                      const base::TickClock* tick_clock,
                                      ControllerDelegate* delegate)
-      : RelaunchNotificationController(upgrade_detector, tick_clock),
+      : RelaunchNotificationController(upgrade_detector, clock, tick_clock),
         delegate_(delegate) {}
 
   using RelaunchNotificationController::kRelaunchGracePeriod;
 
  private:
-  void NotifyRelaunchRecommended() override {
+  void DoNotifyRelaunchRecommended() override {
     delegate_->NotifyRelaunchRecommended();
   }
 
-  void NotifyRelaunchRequired() override {
+  void DoNotifyRelaunchRequired(base::Time deadline) override {
     delegate_->NotifyRelaunchRequired();
   }
 
@@ -78,9 +83,10 @@
 // A fake UpgradeDetector.
 class FakeUpgradeDetector : public UpgradeDetector {
  public:
-  explicit FakeUpgradeDetector(const base::TickClock* tick_clock)
-      : UpgradeDetector(tick_clock) {
-    set_upgrade_detected_time(this->tick_clock()->NowTicks());
+  explicit FakeUpgradeDetector(const base::Clock* clock,
+                               const base::TickClock* tick_clock)
+      : UpgradeDetector(clock, tick_clock) {
+    set_upgrade_detected_time(this->clock()->Now());
   }
 
   // UpgradeDetector:
@@ -88,7 +94,7 @@
     return high_threshold_ / 3;
   }
 
-  base::TimeTicks GetHighAnnoyanceDeadline() override {
+  base::Time GetHighAnnoyanceDeadline() override {
     return upgrade_detected_time() + high_threshold_;
   }
 
@@ -117,6 +123,13 @@
   DISALLOW_COPY_AND_ASSIGN(FakeUpgradeDetector);
 };
 
+class StubPowerMonitorSource : public base::PowerMonitorSource {
+ public:
+  // base::PowerMonitorSource:
+  void Shutdown() override {}
+  bool IsOnBatteryPowerImpl() override { return false; }
+};
+
 }  // namespace
 
 // A test harness that provides facilities for manipulating the relaunch
@@ -128,7 +141,13 @@
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
             base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
         scoped_local_state_(TestingBrowserProcess::GetGlobal()),
-        upgrade_detector_(scoped_task_environment_.GetMockTickClock()) {}
+        upgrade_detector_(scoped_task_environment_.GetMockClock(),
+                          scoped_task_environment_.GetMockTickClock()) {
+    auto mock_power_monitor_source = std::make_unique<StubPowerMonitorSource>();
+    mock_power_monitor_source_ = mock_power_monitor_source.get();
+    power_monitor_ = std::make_unique<base::PowerMonitor>(
+        std::move(mock_power_monitor_source));
+  }
   UpgradeDetector* upgrade_detector() { return &upgrade_detector_; }
   FakeUpgradeDetector& fake_upgrade_detector() { return upgrade_detector_; }
 
@@ -139,6 +158,11 @@
         prefs::kRelaunchNotification, std::make_unique<base::Value>(value));
   }
 
+  // Returns the ScopedTaskEnvironment's MockClock.
+  const base::Clock* GetMockClock() {
+    return scoped_task_environment_.GetMockClock();
+  }
+
   // Returns the ScopedTaskEnvironment's MockTickClock.
   const base::TickClock* GetMockTickClock() {
     return scoped_task_environment_.GetMockTickClock();
@@ -149,7 +173,12 @@
     scoped_task_environment_.FastForwardBy(delta);
   }
 
+  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+
  private:
+  std::unique_ptr<base::PowerMonitor> power_monitor_;
+  // Owned by power_monitor_. Use this to simulate a power suspend and resume.
+  StubPowerMonitorSource* mock_power_monitor_source_ = nullptr;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   ScopedTestingLocalState scoped_local_state_;
   FakeUpgradeDetector upgrade_detector_;
@@ -161,7 +190,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 }
 
 // Without the browser.relaunch_notification preference set, the controller
@@ -171,7 +201,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_VERY_LOW);
@@ -193,7 +224,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // Nothing shown if the level is broadcast at NONE or VERY_LOW.
   fake_upgrade_detector().BroadcastLevelChange(
@@ -220,6 +252,11 @@
       UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
+  // First move time to the high annoyance deadline.
+  base::Time high_annoyance_deadline =
+      upgrade_detector()->GetHighAnnoyanceDeadline();
+  FastForwardBy(high_annoyance_deadline - GetMockClock()->Now());
+
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRecommended());
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
@@ -274,7 +311,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // Nothing shown if the level is broadcast at NONE.
   fake_upgrade_detector().BroadcastLevelChange(
@@ -329,7 +367,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   SetNotificationPref(1);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
@@ -365,7 +404,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
@@ -391,7 +431,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // As in the RequiredByPolicy test, the dialog should be shown.
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRequired());
@@ -412,7 +453,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // As in the RequiredByPolicy test, the dialog should be shown.
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRequired());
@@ -437,7 +479,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // Reduce the period.
   fake_upgrade_detector().BroadcastHighThresholdChange(
@@ -464,7 +507,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_VERY_LOW);
@@ -495,7 +539,13 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
+
+  // First move time to the high annoyance deadline.
+  base::Time high_annoyance_deadline =
+      upgrade_detector()->GetHighAnnoyanceDeadline();
+  FastForwardBy(high_annoyance_deadline - GetMockClock()->Now());
 
   // Get up to high annoyance so that the reshow timer is running.
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRecommended());
@@ -511,6 +561,7 @@
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRecommended());
   fake_upgrade_detector().BroadcastHighThresholdChange(
       fake_upgrade_detector().high_threshold() / 10);
+  RunUntilIdle();
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // And expect another reshow at the new delta.
@@ -559,7 +610,8 @@
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
   FakeRelaunchNotificationController controller(
-      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+      upgrade_detector(), GetMockClock(), GetMockTickClock(),
+      &mock_controller_delegate);
 
   // Get up to low annoyance so that the relaunch timer is running.
   EXPECT_CALL(mock_controller_delegate, NotifyRelaunchRequired());
@@ -568,9 +620,9 @@
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // Move forward partway to the current deadline. Nothing should happen.
-  base::TimeTicks high_annoyance_deadline =
+  base::Time high_annoyance_deadline =
       upgrade_detector()->GetHighAnnoyanceDeadline();
-  FastForwardBy((high_annoyance_deadline - GetMockTickClock()->NowTicks()) / 2);
+  FastForwardBy((high_annoyance_deadline - GetMockClock()->Now()) / 2);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // Lengthen the period, thereby pushing out the deadline.
@@ -581,7 +633,7 @@
   // Ensure that nothing happens when the old deadline passes.
   FastForwardBy(high_annoyance_deadline +
                 FakeRelaunchNotificationController::kRelaunchGracePeriod -
-                GetMockTickClock()->NowTicks());
+                GetMockClock()->Now());
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // But now we enter elevated annoyance level and show the dialog.
@@ -594,7 +646,7 @@
   EXPECT_CALL(mock_controller_delegate, OnRelaunchDeadlineExpired());
   FastForwardBy(upgrade_detector()->GetHighAnnoyanceDeadline() +
                 FakeRelaunchNotificationController::kRelaunchGracePeriod -
-                GetMockTickClock()->NowTicks());
+                GetMockClock()->Now());
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // Shorten the period, bringing in the deadline. Expect the dialog to show and
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
index 6a009ddd..4faf327 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
@@ -41,7 +41,7 @@
 // static
 views::Widget* RelaunchRecommendedBubbleView::ShowBubble(
     Browser* browser,
-    base::TimeTicks detection_time,
+    base::Time detection_time,
     base::RepeatingClosure on_accept) {
   DCHECK(browser);
 
@@ -169,7 +169,7 @@
 RelaunchRecommendedBubbleView::RelaunchRecommendedBubbleView(
     views::Button* anchor_button,
     const gfx::Point& anchor_point,
-    base::TimeTicks detection_time,
+    base::Time detection_time,
     base::RepeatingClosure on_accept)
     : LocationBarBubbleDelegateView(anchor_button, anchor_point, nullptr),
       on_accept_(std::move(on_accept)),
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.h b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.h
index c24eb23..2eebc31 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.h
@@ -28,7 +28,7 @@
   // |detection_time|. |on_accept| is run if the user accepts the prompt to
   // restart.
   static views::Widget* ShowBubble(Browser* browser,
-                                   base::TimeTicks detection_time,
+                                   base::Time detection_time,
                                    base::RepeatingClosure on_accept);
   ~RelaunchRecommendedBubbleView() override;
 
@@ -53,7 +53,7 @@
  private:
   RelaunchRecommendedBubbleView(views::Button* anchor_button,
                                 const gfx::Point& anchor_point,
-                                base::TimeTicks detection_time,
+                                base::Time detection_time,
                                 base::RepeatingClosure on_accept);
 
   // Invoked when the timer fires to refresh the title text.
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
index ef36e98..8e6160ae 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc
@@ -15,8 +15,8 @@
 
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    base::TimeTicks detection_time =
-        base::TimeTicks::Now() - base::TimeDelta::FromDays(3);
+    base::Time detection_time =
+        base::Time::Now() - base::TimeDelta::FromDays(3);
     RelaunchRecommendedBubbleView::ShowBubble(browser(), detection_time,
                                               base::DoNothing());
   }
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.cc
index 90c0541..d00c3ba 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.cc
@@ -11,7 +11,7 @@
 #include "ui/base/l10n/l10n_util.h"
 
 RelaunchRecommendedTimer::RelaunchRecommendedTimer(
-    base::TimeTicks upgrade_detected_time,
+    base::Time upgrade_detected_time,
     base::RepeatingClosure callback)
     : upgrade_detected_time_(upgrade_detected_time),
       callback_(std::move(callback)) {
@@ -21,20 +21,19 @@
 RelaunchRecommendedTimer::~RelaunchRecommendedTimer() {}
 
 base::string16 RelaunchRecommendedTimer::GetWindowTitle() const {
-  const base::TimeDelta elapsed =
-      base::TimeTicks::Now() - upgrade_detected_time_;
+  const base::TimeDelta elapsed = base::Time::Now() - upgrade_detected_time_;
   return l10n_util::GetPluralStringFUTF16(IDS_RELAUNCH_RECOMMENDED_TITLE,
                                           elapsed.InDays());
 }
 
 void RelaunchRecommendedTimer::ScheduleNextTitleRefresh() {
   // Refresh at the next day boundary.
-  const base::TimeDelta elapsed =
-      base::TimeTicks::Now() - upgrade_detected_time_;
+  const base::Time now = base::Time::Now();
+  const base::TimeDelta elapsed = now - upgrade_detected_time_;
   const base::TimeDelta delta =
       base::TimeDelta::FromDays(elapsed.InDays() + 1) - elapsed;
 
-  refresh_timer_.Start(FROM_HERE, delta, this,
+  refresh_timer_.Start(FROM_HERE, now + delta, this,
                        &RelaunchRecommendedTimer::OnTitleRefresh);
 }
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.h b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.h
index 3424468c..376e9de2 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_timer.h
@@ -19,7 +19,7 @@
   // |upgrade_detected_time| is used to compose current notification title
   // (see below comment).
   // |callback| is called every time the notification title has to be updated.
-  RelaunchRecommendedTimer(base::TimeTicks upgrade_detected_time,
+  RelaunchRecommendedTimer(base::Time upgrade_detected_time,
                            base::RepeatingClosure callback);
 
   ~RelaunchRecommendedTimer();
@@ -36,10 +36,10 @@
   // Invoked when the timer fires to refresh the title text.
   void OnTitleRefresh();
 
-  // The tick count at which Chrome noticed that an update was available. This
+  // The time at which Chrome noticed that an update was available. This
   // is used to write the proper string into the dialog's title and to schedule
   // title refreshes to update said string.
-  const base::TimeTicks upgrade_detected_time_;
+  const base::Time upgrade_detected_time_;
 
   // A timer with which title refreshes are scheduled.
   WallClockTimer refresh_timer_;
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
index 2dd7ddc..b33cd31 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
@@ -33,7 +33,7 @@
 // static
 views::Widget* RelaunchRequiredDialogView::Show(
     Browser* browser,
-    base::TimeTicks deadline,
+    base::Time deadline,
     base::RepeatingClosure on_accept) {
   views::Widget* widget = constrained_window::CreateBrowserModalDialogViews(
       new RelaunchRequiredDialogView(deadline, std::move(on_accept)),
@@ -51,7 +51,7 @@
       widget->widget_delegate()->AsDialogDelegate());
 }
 
-void RelaunchRequiredDialogView::SetDeadline(base::TimeTicks deadline) {
+void RelaunchRequiredDialogView::SetDeadline(base::Time deadline) {
   relaunch_required_timer_.SetDeadline(deadline);
 }
 
@@ -126,7 +126,7 @@
 // |relaunch_required_timer_| automatically starts for the next time the title
 // needs to be updated (e.g., from "2 days" to "3 days").
 RelaunchRequiredDialogView::RelaunchRequiredDialogView(
-    base::TimeTicks deadline,
+    base::Time deadline,
     base::RepeatingClosure on_accept)
     : on_accept_(on_accept),
       body_label_(nullptr),
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
index c8da32a..d9f0c05 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
@@ -25,7 +25,7 @@
   // Shows the dialog in |browser| for a relaunch that will be forced at
   // |deadline|. |on_accept| is run if the user accepts the prompt to restart.
   static views::Widget* Show(Browser* browser,
-                             base::TimeTicks deadline,
+                             base::Time deadline,
                              base::RepeatingClosure on_accept);
 
   ~RelaunchRequiredDialogView() override;
@@ -36,7 +36,7 @@
 
   // Sets the relaunch deadline to |deadline| and refreshes the view's title
   // accordingly.
-  void SetDeadline(base::TimeTicks deadline);
+  void SetDeadline(base::Time deadline);
 
   // views::DialogDelegateView:
   bool Cancel() override;
@@ -56,7 +56,7 @@
   gfx::Size CalculatePreferredSize() const override;
 
  private:
-  RelaunchRequiredDialogView(base::TimeTicks deadline,
+  RelaunchRequiredDialogView(base::Time deadline,
                              base::RepeatingClosure on_accept);
 
   // Invoked when the timer fires to refresh the title text.
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
index 0e6c37a..bedbb8e3 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc
@@ -15,8 +15,7 @@
 
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    base::TimeTicks deadline =
-        base::TimeTicks::Now() + base::TimeDelta::FromDays(3);
+    base::Time deadline = base::Time::Now() + base::TimeDelta::FromDays(3);
     RelaunchRequiredDialogView::Show(browser(), deadline, base::DoNothing());
   }
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.cc
index ce7d13b9..82d3fed 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.cc
@@ -11,7 +11,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
-RelaunchRequiredTimer::RelaunchRequiredTimer(base::TimeTicks deadline,
+RelaunchRequiredTimer::RelaunchRequiredTimer(base::Time deadline,
                                              base::RepeatingClosure callback)
     : deadline_(deadline), callback_(std::move(callback)) {
   ScheduleNextTitleRefresh();
@@ -22,7 +22,8 @@
 void RelaunchRequiredTimer::ScheduleNextTitleRefresh() {
   // Refresh at the next second, minute, hour, or day boundary; depending on the
   // relaunch deadline.
-  const base::TimeDelta deadline_offset = deadline_ - base::TimeTicks::Now();
+  const base::Time now = base::Time::Now();
+  const base::TimeDelta deadline_offset = deadline_ - now;
 
   // Don't start the timer if the deadline is in the past or right now.
   if (deadline_offset <= base::TimeDelta())
@@ -31,11 +32,11 @@
   const base::TimeDelta refresh_delta =
       relaunch_notification::ComputeNextRefreshDelta(deadline_offset);
 
-  refresh_timer_.Start(FROM_HERE, refresh_delta, this,
+  refresh_timer_.Start(FROM_HERE, now + refresh_delta, this,
                        &RelaunchRequiredTimer::OnTitleRefresh);
 }
 
-void RelaunchRequiredTimer::SetDeadline(base::TimeTicks deadline) {
+void RelaunchRequiredTimer::SetDeadline(base::Time deadline) {
   if (deadline != deadline_) {
     deadline_ = deadline;
     // Refresh the title immediately.
@@ -55,7 +56,7 @@
   // "3..2..1.." countdown to change precisely on the per-second boundaries.
   const base::TimeDelta rounded_offset =
       relaunch_notification::ComputeDeadlineDelta(deadline_ -
-                                                  base::TimeTicks::Now());
+                                                  base::Time::Now());
 
   int amount = rounded_offset.InSeconds();
   int message_id = IDS_RELAUNCH_REQUIRED_TITLE_SECONDS;
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.h b/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.h
index ce71f8c..fe6f372 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_timer.h
@@ -20,14 +20,13 @@
   // the controller, here it is used to compose the notification's title
   // accordingly (e.g. "Chrome will restart in 3 minutes").
   // |callback| is called every time the notification title has to be updated.
-  RelaunchRequiredTimer(base::TimeTicks deadline,
-                        base::RepeatingClosure callback);
+  RelaunchRequiredTimer(base::Time deadline, base::RepeatingClosure callback);
 
   ~RelaunchRequiredTimer();
 
   // Sets the relaunch deadline to |deadline| and refreshes the notifications's
   // title accordingly.
-  void SetDeadline(base::TimeTicks deadline);
+  void SetDeadline(base::Time deadline);
 
   // Returns current notification's title, composed depending on how much time
   // is left until the deadline.
@@ -41,7 +40,7 @@
   void OnTitleRefresh();
 
   // The time at which Chrome will be forcefully relaunched.
-  base::TimeTicks deadline_;
+  base::Time deadline_;
 
   // A timer with which title refreshes are scheduled.
   WallClockTimer refresh_timer_;
diff --git a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.cc b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.cc
index a7cbd230..cfdf877 100644
--- a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.cc
+++ b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.cc
@@ -24,14 +24,14 @@
 }
 
 void WallClockTimer::Start(const base::Location& posted_from,
-                           base::TimeDelta delay,
+                           base::Time desired_run_time,
                            base::OnceClosure user_task) {
   user_task_ = std::move(user_task);
   posted_from_ = posted_from;
-  delay_ = delay;
-  desired_run_time_ = Now() + delay_;
+  desired_run_time_ = desired_run_time;
   AddObserver();
-  timer_.Start(posted_from_, delay, this, &WallClockTimer::RunUserTask);
+  timer_.Start(posted_from_, desired_run_time_ - Now(), this,
+               &WallClockTimer::RunUserTask);
 }
 
 base::Time WallClockTimer::Now() const {
diff --git a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h
index 8bd4cfc..ed5faa0d 100644
--- a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h
+++ b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer.h
@@ -43,7 +43,7 @@
   // Start the timer to run at the given |delay| from now. If the timer is
   // already running, it will be replaced to call the given |user_task|.
   virtual void Start(const base::Location& posted_from,
-                     base::TimeDelta delay,
+                     base::Time desired_run_time,
                      base::OnceClosure user_task);
 
   // Start the timer to run at the given |delay| from now. If the timer is
@@ -51,10 +51,10 @@
   // |reviewer->*method|.
   template <class Receiver>
   void Start(const base::Location& posted_from,
-             base::TimeDelta delay,
+             base::Time desired_run_time,
              Receiver* receiver,
              void (Receiver::*method)()) {
-    Start(posted_from, delay,
+    Start(posted_from, desired_run_time,
           base::BindOnce(method, base::Unretained(receiver)));
   }
 
@@ -83,9 +83,6 @@
   // Location in user code.
   base::Location posted_from_;
 
-  // Delay requested by user.
-  base::TimeDelta delay_;
-
   // The desired run time of |user_task_|.
   base::Time desired_run_time_;
 
diff --git a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer_unittest.cc b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer_unittest.cc
index 7f9f0df..65af020 100644
--- a/chrome/browser/ui/views/relaunch_notification/wall_clock_timer_unittest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/wall_clock_timer_unittest.cc
@@ -58,10 +58,11 @@
   base::SimpleTestClock clock;
   // Set up a WallClockTimer that will fire in one minute.
   WallClockTimer wall_clock_timer(&clock, task_environment_.GetMockTickClock());
-  auto delay = base::TimeDelta::FromMinutes(1);
-  auto start_time = base::Time::Now();
+  const auto delay = base::TimeDelta::FromMinutes(1);
+  const auto start_time = base::Time::Now();
+  const auto run_time = start_time + delay;
   clock.SetNow(start_time);
-  wall_clock_timer.Start(FROM_HERE, delay, callback.Get());
+  wall_clock_timer.Start(FROM_HERE, run_time, callback.Get());
   EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
 
   mock_power_monitor_source_->Suspend();
diff --git a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
index 0c33a75..46eac0b 100644
--- a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
+++ b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
@@ -114,7 +114,7 @@
 }
 
 GURL GetSigninPromoURL() {
-  return signin::GetPromoURLForTab(
+  return signin::GetEmbeddedPromoURL(
       signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
       signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false);
 }
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index e268ea4..29f9919 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -92,11 +92,8 @@
 class BrowserTabStripController::TabContextMenuContents
     : public ui::SimpleMenuModel::Delegate {
  public:
-  TabContextMenuContents(Tab* tab,
-                         BrowserTabStripController* controller)
-      : tab_(tab),
-        controller_(controller),
-        last_command_(TabStripModel::CommandFirst) {
+  TabContextMenuContents(Tab* tab, BrowserTabStripController* controller)
+      : tab_(tab), controller_(controller) {
     model_.reset(new TabMenuModel(
         this, controller->model_,
         controller->tabstrip_->GetModelIndexOfTab(tab)));
@@ -105,11 +102,6 @@
         views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
   }
 
-  ~TabContextMenuContents() override {
-    if (controller_)
-      controller_->tabstrip_->StopAllHighlighting();
-  }
-
   void Cancel() {
     controller_ = NULL;
   }
@@ -136,25 +128,14 @@
                                                             accelerator) :
         false;
   }
-  void CommandIdHighlighted(int command_id) override {
-    controller_->StopHighlightTabsForCommand(last_command_, tab_);
-    last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id);
-    controller_->StartHighlightTabsForCommand(last_command_, tab_);
-  }
   void ExecuteCommand(int command_id, int event_flags) override {
     // Executing the command destroys |this|, and can also end up destroying
     // |controller_|. So stop the highlights before executing the command.
-    controller_->tabstrip_->StopAllHighlighting();
     controller_->ExecuteCommandForTab(
         static_cast<TabStripModel::ContextMenuCommand>(command_id),
         tab_);
   }
 
-  void MenuClosed(ui::SimpleMenuModel* /*source*/) override {
-    if (controller_)
-      controller_->tabstrip_->StopAllHighlighting();
-  }
-
  private:
   std::unique_ptr<TabMenuModel> model_;
   std::unique_ptr<views::MenuRunner> menu_runner_;
@@ -165,10 +146,6 @@
   // A pointer back to our hosting controller, for command state information.
   BrowserTabStripController* controller_;
 
-  // The last command that was selected, so that we can start/stop highlighting
-  // appropriately as the user moves through the menu.
-  TabStripModel::ContextMenuCommand last_command_;
-
   DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents);
 };
 
@@ -607,33 +584,6 @@
       TabRendererDataFromModel(web_contents, model_index, EXISTING_TAB));
 }
 
-void BrowserTabStripController::StartHighlightTabsForCommand(
-    TabStripModel::ContextMenuCommand command_id,
-    Tab* tab) {
-  if (command_id == TabStripModel::CommandCloseOtherTabs ||
-      command_id == TabStripModel::CommandCloseTabsToRight) {
-    int model_index = tabstrip_->GetModelIndexOfTab(tab);
-    if (IsValidIndex(model_index)) {
-      std::vector<int> indices =
-          model_->GetIndicesClosedByCommand(model_index, command_id);
-      for (std::vector<int>::const_iterator i(indices.begin());
-           i != indices.end(); ++i) {
-        tabstrip_->StartHighlight(*i);
-      }
-    }
-  }
-}
-
-void BrowserTabStripController::StopHighlightTabsForCommand(
-    TabStripModel::ContextMenuCommand command_id,
-    Tab* tab) {
-  if (command_id == TabStripModel::CommandCloseTabsToRight ||
-      command_id == TabStripModel::CommandCloseOtherTabs) {
-    // Just tell all Tabs to stop pulsing - it's safe.
-    tabstrip_->StopAllHighlighting();
-  }
-}
-
 void BrowserTabStripController::AddTab(WebContents* contents,
                                        int index,
                                        bool is_active) {
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
index d72fb97..9dfcf326 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -126,13 +126,6 @@
   // Invokes tabstrip_->SetTabData.
   void SetTabDataAt(content::WebContents* web_contents, int model_index);
 
-  void StartHighlightTabsForCommand(
-      TabStripModel::ContextMenuCommand command_id,
-      Tab* tab);
-  void StopHighlightTabsForCommand(
-      TabStripModel::ContextMenuCommand command_id,
-      Tab* tab);
-
   // Adds a tab.
   void AddTab(content::WebContents* contents, int index, bool is_active);
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 46554c2..6296d1a7 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -55,7 +55,6 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
 #include "ui/compositor/clip_recorder.h"
-#include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_analysis.h"
@@ -119,10 +118,8 @@
 // static
 const char Tab::kViewClassName[] = "Tab";
 
-Tab::Tab(TabController* controller, gfx::AnimationContainer* container)
+Tab::Tab(TabController* controller)
     : controller_(controller),
-      pulse_animation_(this),
-      animation_container_(container),
       title_(new views::Label()),
       title_animation_(this),
       hover_controller_(this) {
@@ -164,14 +161,7 @@
 
   set_context_menu_controller(this);
 
-  constexpr int kPulseDurationMs = 200;
-  pulse_animation_.SetSlideDuration(kPulseDurationMs);
-  pulse_animation_.SetContainer(animation_container_.get());
-
   title_animation_.SetDuration(base::TimeDelta::FromMilliseconds(100));
-  title_animation_.SetContainer(animation_container_.get());
-
-  hover_controller_.SetAnimationContainer(animation_container_.get());
 
   // Enable keyboard focus.
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
@@ -196,11 +186,6 @@
     return;
   }
 
-  // Ignore if the pulse animation is being performed on active tab because
-  // it repaints the same image. See PaintTab().
-  if (animation == &pulse_animation_ && IsActive())
-    return;
-
   SchedulePaint();
 }
 
@@ -665,6 +650,7 @@
     case TabAlertState::BLUETOOTH_CONNECTED:
     case TabAlertState::USB_CONNECTED:
     case TabAlertState::NONE:
+    case TabAlertState::VR_PRESENTING_IN_HEADSET:
       return button_color_;
     default:
       NOTREACHED();
@@ -749,18 +735,6 @@
   icon_->SetCanPaintToLayer(controller_->CanPaintThrobberToLayer());
 }
 
-bool Tab::ShowingLoadingAnimation() const {
-  return icon_->ShowingLoadingAnimation();
-}
-
-void Tab::StartPulse() {
-  pulse_animation_.StartThrobbing(std::numeric_limits<int>::max());
-}
-
-void Tab::StopPulse() {
-  pulse_animation_.Stop();
-}
-
 void Tab::SetTabNeedsAttention(bool attention) {
   icon_->SetAttention(TabIcon::AttentionType::kTabWantsAttentionStatus,
                       attention);
@@ -848,9 +822,7 @@
     return is_selected ? (kSelectedTabThrobScale * opacity) : opacity;
   };
 
-  if (pulse_animation_.is_animating())
-    val += pulse_animation_.GetCurrentValue() * offset();
-  else if (hover_controller_.ShouldDraw())
+  if (hover_controller_.ShouldDraw())
     val += hover_controller_.GetAnimationValue() * offset();
 
   return val;
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index a9e3d7c..1ceaec8 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -33,9 +33,7 @@
 
 namespace gfx {
 class Animation;
-class AnimationContainer;
 class LinearAnimation;
-class ThrobAnimation;
 }
 namespace views {
 class Label;
@@ -61,7 +59,7 @@
   static constexpr int kMinimumContentsWidthForCloseButtons = 68;
   static constexpr int kTouchMinimumContentsWidthForCloseButtons = 100;
 
-  Tab(TabController* controller, gfx::AnimationContainer* container);
+  explicit Tab(TabController* controller);
   ~Tab() override;
 
   // gfx::AnimationDelegate:
@@ -153,10 +151,6 @@
 
   bool ShowingLoadingAnimation() const;
 
-  // Starts/Stops a pulse animation.
-  void StartPulse();
-  void StopPulse();
-
   // Sets the visibility of the indicator shown when the tab needs to indicate
   // to the user that it needs their attention.
   void SetTabNeedsAttention(bool attention);
@@ -245,11 +239,6 @@
   // True if the tab has been detached.
   bool detached_ = false;
 
-  // Whole-tab throbbing "pulse" animation.
-  gfx::ThrobAnimation pulse_animation_;
-
-  scoped_refptr<gfx::AnimationContainer> animation_container_;
-
   TabIcon* icon_ = nullptr;
   AlertIndicator* alert_indicator_ = nullptr;
   TabCloseButton* close_button_ = nullptr;
diff --git a/chrome/browser/ui/views/tabs/tab_icon.cc b/chrome/browser/ui/views/tabs/tab_icon.cc
index aa28656..cff3858 100644
--- a/chrome/browser/ui/views/tabs/tab_icon.cc
+++ b/chrome/browser/ui/views/tabs/tab_icon.cc
@@ -28,12 +28,6 @@
 
 namespace {
 
-constexpr int kFinishedLoadingAnimationTimeMs = 375;
-constexpr int kFinishedLoadingFadeOutTimeMs = 150;
-constexpr int kFaviconFadeInMs = 350;
-constexpr int kFaviconPlaceholderFadeInMs = 400;
-constexpr int kFaviconPlaceholderFadeOutMs = 150;
-
 bool UseNewLoadingAnimation() {
   return base::FeatureList::IsEnabled(features::kNewTabLoadingAnimation);
 }
@@ -54,16 +48,6 @@
          network_state != TabNetworkState::kError;
 }
 
-// Returns a rect in which the throbber should be painted.
-gfx::RectF GetThrobberBounds(const gfx::Rect& bounds) {
-  gfx::RectF throbber_bounds(bounds);
-  constexpr float kThrobberHeightDp = 3;
-  // The throbber starts 1dp below the tab icon.
-  throbber_bounds.set_y(bounds.bottom() + 1);
-  throbber_bounds.set_height(kThrobberHeightDp);
-  return throbber_bounds;
-}
-
 }  // namespace
 
 // Helper class that manages the favicon crash animation.
@@ -94,8 +78,6 @@
   DISALLOW_COPY_AND_ASSIGN(CrashAnimation);
 };
 
-TabIcon::LoadingAnimationState::LoadingAnimationState() = default;
-
 TabIcon::TabIcon() : clock_(base::DefaultTickClock::GetInstance()) {
   set_can_process_events_within_subtree(false);
 
@@ -123,7 +105,7 @@
 
   if (was_showing_load && !showing_load) {
     // Loading animation transitioning from on to off.
-    old_animation_loading_start_time_ = base::TimeTicks();
+    loading_animation_start_time_ = base::TimeTicks();
     waiting_state_ = gfx::ThrobberWaitingState();
     SchedulePaint();
   } else if (!was_showing_load && showing_load) {
@@ -148,26 +130,7 @@
   if (inhibit_loading_animation_)
     return false;
 
-  if (NetworkStateIsAnimated(network_state_))
-    return true;
-
-  if (!UseNewLoadingAnimation())
-    return false;
-
-  // If any animations were active in the last painted state we need to keep
-  // animations going.
-  // Note that the fade-in check is different as the fade-in progress doesn't
-  // reset as it ends but stays at 1.0. Unset means we're waiting for the
-  // animation to start.
-  if (keep_loading_animation_running_ ||
-      animation_state_.finished_loading_animation ||
-      animation_state_.finished_loading_animation_fade_out ||
-      animation_state_.favicon_placeholder_alpha ||
-      animation_state_.favicon_fade_in_progress.value_or(0.0) < 1.0) {
-    return true;
-  }
-
-  return false;
+  return NetworkStateIsAnimated(network_state_);
 }
 
 bool TabIcon::ShowingAttentionIndicator() const {
@@ -182,20 +145,12 @@
 }
 
 void TabIcon::StepLoadingAnimation(const base::TimeDelta& elapsed_time) {
-  // The old loading animation only updates elapsed time while it's loading.
-  // This is used as a starting point for PaintThrobberSpinningAfterWaiting().
-  if (UseNewLoadingAnimation() || network_state_ == TabNetworkState::kWaiting)
+  // Only update elapsed time in the kWaiting state. This is later used as a
+  // starting point for PaintThrobberSpinningAfterWaiting().
+  if (network_state_ == TabNetworkState::kWaiting)
     waiting_state_.elapsed_time = elapsed_time;
-
-  UpdatePendingAnimationState();
-
   if (ShowingLoadingAnimation())
     SchedulePaint();
-
-  // TODO(pbos): Revisit this, ideally we should always be able to paint on a
-  // layer.
-  if (UseNewLoadingAnimation())
-    RefreshLayer();
 }
 
 void TabIcon::SetBackgroundColor(SkColor bg_color) {
@@ -206,9 +161,6 @@
 void TabIcon::OnPaint(gfx::Canvas* canvas) {
   // Compute the bounds adjusted for the hiding fraction.
   gfx::Rect contents_bounds = GetContentsBounds();
-  // Update animation state regardless of empty bounds or not, so we don't think
-  // we're perpetually animating.
-  animation_state_ = pending_animation_state_;
 
   if (contents_bounds.IsEmpty())
     return;
@@ -228,7 +180,6 @@
   if (ShowingAttentionIndicator() && !should_display_crashed_favicon_) {
     PaintAttentionIndicatorAndIcon(canvas, GetIconToPaint(), icon_bounds);
   } else {
-    MaybePaintFaviconPlaceholder(canvas, icon_bounds);
     MaybePaintFavicon(canvas, GetIconToPaint(), icon_bounds);
   }
 
@@ -236,72 +187,6 @@
     PaintLoadingAnimation(canvas, icon_bounds);
 }
 
-void TabIcon::UpdatePendingAnimationState() {
-  if (last_animation_update_time_.is_null())
-    return;
-  const base::TimeTicks now = clock_->NowTicks();
-
-  double animation_delta_ms =
-      (now - last_animation_update_time_).InMilliseconds();
-  last_animation_update_time_ = now;
-
-  pending_animation_state_.elapsed_time = waiting_state_.elapsed_time;
-
-  if (keep_loading_animation_running_) {
-    const int previously_painted_cycles =
-        animation_state_.elapsed_time.InMilliseconds() /
-        gfx::kNewThrobberWaitingAnimationCycleMs;
-    const int next_painted_cycles =
-        pending_animation_state_.elapsed_time.InMilliseconds() /
-        gfx::kNewThrobberWaitingAnimationCycleMs;
-    // Throbber's gone a full circle since last paint, transition to
-    // finished-loading animation.
-    if (previously_painted_cycles != next_painted_cycles) {
-      pending_animation_state_.finished_loading_animation = 0.0;
-      keep_loading_animation_running_ = false;
-      MaybeStartFaviconFadeIn();
-    }
-  }
-
-  if (pending_animation_state_.favicon_placeholder_alpha) {
-    // Fade in until the favicon fade-in starts. Then fade out.
-    if (!pending_animation_state_.favicon_fade_in_progress) {
-      pending_animation_state_.favicon_placeholder_alpha =
-          std::min(*pending_animation_state_.favicon_placeholder_alpha +
-                       animation_delta_ms / kFaviconPlaceholderFadeInMs,
-                   1.0);
-    } else {
-      *pending_animation_state_.favicon_placeholder_alpha -=
-          animation_delta_ms / kFaviconPlaceholderFadeOutMs;
-      if (pending_animation_state_.favicon_placeholder_alpha <= 0.0)
-        pending_animation_state_.favicon_placeholder_alpha.reset();
-    }
-  }
-
-  if (pending_animation_state_.finished_loading_animation) {
-    *pending_animation_state_.finished_loading_animation +=
-        animation_delta_ms / kFinishedLoadingAnimationTimeMs;
-    if (*pending_animation_state_.finished_loading_animation >= 1.0) {
-      pending_animation_state_.finished_loading_animation.reset();
-      pending_animation_state_.finished_loading_animation_fade_out = 0.0;
-    }
-  }
-
-  if (pending_animation_state_.finished_loading_animation_fade_out) {
-    *pending_animation_state_.finished_loading_animation_fade_out +=
-        animation_delta_ms / kFinishedLoadingFadeOutTimeMs;
-    if (*pending_animation_state_.finished_loading_animation_fade_out >= 1.0)
-      pending_animation_state_.finished_loading_animation_fade_out.reset();
-  }
-
-  if (pending_animation_state_.favicon_fade_in_progress) {
-    *pending_animation_state_.favicon_fade_in_progress =
-        std::min(*pending_animation_state_.favicon_fade_in_progress +
-                     animation_delta_ms / kFaviconFadeInMs,
-                 1.0);
-  }
-}
-
 void TabIcon::OnThemeChanged() {
   crashed_icon_ = gfx::ImageSkia();  // Force recomputation if crashed.
   if (!themed_favicon_.isNull())
@@ -341,67 +226,25 @@
   canvas->DrawCircle(circle_center, kAttentionIndicatorRadius, indicator_flags);
 }
 
-void TabIcon::PaintLoadingProgressIndicator(gfx::Canvas* canvas,
-                                            gfx::RectF bounds,
-                                            SkColor color) {
-  // Don't paint if both the loading-progress and fade-out animations both have
-  // finished.
-  if (!animation_state_.finished_loading_animation &&
-      !animation_state_.finished_loading_animation_fade_out) {
-    return;
-  }
-  const double progress = gfx::Tween::CalculateValue(
-      gfx::Tween::EASE_IN_OUT,
-      animation_state_.finished_loading_animation.value_or(1.0));
-  bounds.set_width(bounds.height() +
-                   progress * (bounds.width() - bounds.height()));
-
-  cc::PaintFlags flags;
-  flags.setColor(color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  if (animation_state_.finished_loading_animation_fade_out) {
-    flags.setAlpha(
-        (1.0 - *animation_state_.finished_loading_animation_fade_out) *
-        SK_AlphaOPAQUE);
-  }
-
-  canvas->DrawRoundRect(bounds, bounds.height() / 2, flags);
-}
-
 void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas,
                                     const gfx::Rect& bounds) {
   const ui::ThemeProvider* tp = GetThemeProvider();
-  if (UseNewLoadingAnimation()) {
-    const gfx::RectF throbber_bounds = GetThrobberBounds(bounds);
-    const SkColor loading_color =
-        tp->GetColor(network_state_ == TabNetworkState::kWaiting
-                         ? ThemeProperties::COLOR_TAB_THROBBER_WAITING
-                         : ThemeProperties::COLOR_TAB_THROBBER_SPINNING);
-    if (NetworkStateIsAnimated(network_state_) ||
-        keep_loading_animation_running_) {
-      gfx::PaintNewThrobberWaiting(canvas, throbber_bounds, loading_color,
-                                   animation_state_.elapsed_time);
-    } else {
-      PaintLoadingProgressIndicator(canvas, throbber_bounds, loading_color);
-    }
+  if (network_state_ == TabNetworkState::kWaiting) {
+    gfx::PaintThrobberWaiting(
+        canvas, bounds,
+        tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING),
+        waiting_state_.elapsed_time);
   } else {
-    if (network_state_ == TabNetworkState::kWaiting) {
-      gfx::PaintThrobberWaiting(
-          canvas, bounds,
-          tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING),
-          waiting_state_.elapsed_time);
-    } else {
-      const base::TimeTicks current_time = clock_->NowTicks();
-      if (old_animation_loading_start_time_.is_null())
-        old_animation_loading_start_time_ = current_time;
+    const base::TimeTicks current_time = clock_->NowTicks();
+    if (loading_animation_start_time_.is_null())
+      loading_animation_start_time_ = current_time;
 
-      waiting_state_.color =
-          tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING);
-      gfx::PaintThrobberSpinningAfterWaiting(
-          canvas, bounds,
-          tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING),
-          current_time - old_animation_loading_start_time_, &waiting_state_);
-    }
+    waiting_state_.color =
+        tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING);
+    gfx::PaintThrobberSpinningAfterWaiting(
+        canvas, bounds,
+        tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING),
+        current_time - loading_animation_start_time_, &waiting_state_);
   }
 }
 
@@ -417,54 +260,14 @@
   return themed_favicon_.isNull() ? favicon_ : themed_favicon_;
 }
 
-void TabIcon::MaybePaintFaviconPlaceholder(gfx::Canvas* canvas,
-                                           const gfx::Rect& bounds) {
-  if (!UseNewLoadingAnimation())
-    return;
-  if (!animation_state_.favicon_placeholder_alpha)
-    return;
-  cc::PaintFlags flags;
-  double placeholder_alpha = gfx::Tween::CalculateValue(
-      !pending_animation_state_.favicon_fade_in_progress
-          ? gfx::Tween::LINEAR_OUT_SLOW_IN
-          : gfx::Tween::SLOW_OUT_LINEAR_IN,
-      *animation_state_.favicon_placeholder_alpha);
-  const SkColor placeholder_color =
-      color_utils::IsDark(bg_color_)
-          ? SkColorSetA(SK_ColorWHITE, 32 * placeholder_alpha)
-          : SkColorSetA(SK_ColorBLACK, 16 * placeholder_alpha);
-  flags.setColor(placeholder_color);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  flags.setAntiAlias(true);
-
-  constexpr float kFaviconPlaceholderRadiusDp = 4;
-  canvas->DrawRoundRect(bounds, kFaviconPlaceholderRadiusDp, flags);
-}
-
 void TabIcon::MaybePaintFavicon(gfx::Canvas* canvas,
                                 const gfx::ImageSkia& icon,
                                 const gfx::Rect& bounds) {
-  // While loading, the favicon (or placeholder) isn't drawn until it has
-  // started fading in.
-  if (UseNewLoadingAnimation() && !animation_state_.favicon_fade_in_progress)
-    return;
-
   if (icon.isNull())
     return;
 
-  cc::PaintFlags flags;
-  double fade_in_progress =
-      UseNewLoadingAnimation() ? gfx::Tween::CalculateValue(
-                                     gfx::Tween::FAST_OUT_SLOW_IN,
-                                     *animation_state_.favicon_fade_in_progress)
-                               : 1.0;
-  flags.setAlpha(fade_in_progress * SK_AlphaOPAQUE);
-  // Drop in the new favicon from the top while it's fading in.
-  const int offset = round((fade_in_progress - 1.0) * 4.0);
-
   canvas->DrawImageInt(icon, 0, 0, bounds.width(), bounds.height(), bounds.x(),
-                       bounds.y() + offset, bounds.width(), bounds.height(),
-                       false, flags);
+                       bounds.y(), bounds.width(), bounds.height(), false);
 }
 
 bool TabIcon::HasNonDefaultFavicon() const {
@@ -473,25 +276,6 @@
                                    *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON));
 }
 
-void TabIcon::MaybeStartFaviconFadeIn() {
-  if (!UseNewLoadingAnimation())
-    return;
-
-  if (pending_animation_state_.favicon_fade_in_progress)
-    return;
-
-  // Start fading in the favicon if we're no longer animating.
-  if (!NetworkStateIsAnimated(network_state_)) {
-    pending_animation_state_.favicon_fade_in_progress = 0.0;
-    return;
-  }
-
-  // If we have a non-default favicon and are already loading the throbber state
-  // start fading in the favicon.
-  if (HasNonDefaultFavicon() && network_state_ == TabNetworkState::kLoading)
-    pending_animation_state_.favicon_fade_in_progress = 0.0;
-}
-
 void TabIcon::SetIcon(const GURL& url, const gfx::ImageSkia& icon) {
   // Detect when updating to the same icon. This avoids re-theming and
   // re-painting.
@@ -506,39 +290,11 @@
     themed_favicon_ = gfx::ImageSkia();
   }
 
-  MaybeStartFaviconFadeIn();
   SchedulePaint();
 }
 
 void TabIcon::SetNetworkState(TabNetworkState network_state) {
-  if (network_state_ == network_state)
-    return;
-
-  if (!UseNewLoadingAnimation()) {
-    network_state_ = network_state;
-    return;
-  }
-
-  const bool was_animated = NetworkStateIsAnimated(network_state_);
   network_state_ = network_state;
-  const bool is_animated = NetworkStateIsAnimated(network_state_);
-
-  // If we either start animating (or go from loading to waiting), reset all
-  // animations.
-  if ((!was_animated && is_animated) ||
-      network_state_ == TabNetworkState::kWaiting) {
-    last_animation_update_time_ = clock_->NowTicks();
-    keep_loading_animation_running_ = false;
-    pending_animation_state_ = LoadingAnimationState();
-    pending_animation_state_.favicon_placeholder_alpha = 0.0;
-    pending_animation_state_.favicon_fade_in_progress.reset();
-  }
-
-  if (was_animated && !is_animated)
-    keep_loading_animation_running_ = true;
-
-  MaybeStartFaviconFadeIn();
-  SchedulePaint();
 }
 
 void TabIcon::SetIsCrashed(bool is_crashed) {
diff --git a/chrome/browser/ui/views/tabs/tab_icon.h b/chrome/browser/ui/views/tabs/tab_icon.h
index 42c7504..0b143eb2 100644
--- a/chrome/browser/ui/views/tabs/tab_icon.h
+++ b/chrome/browser/ui/views/tabs/tab_icon.h
@@ -65,23 +65,6 @@
   friend CrashAnimation;
   friend class TabTest;
 
-  // State used to draw the tab-loading animation. Also used to store the
-  // last-painted state to know to redraw the final frame as the animation
-  // finishes.
-  struct LoadingAnimationState {
-    LoadingAnimationState();
-
-    base::TimeDelta elapsed_time;
-    base::Optional<double> finished_loading_animation;
-    base::Optional<double> finished_loading_animation_fade_out;
-
-    base::Optional<double> favicon_placeholder_alpha;
-    // TODO(pbos): Make this a type that can represent "not started" and "ended"
-    // separately. Right now the value 1.0 is used to indicate that the
-    // animation has ended (and we're not waiting for it to start).
-    base::Optional<double> favicon_fade_in_progress = 1.0;
-  };
-
   // views::View:
   void OnPaint(gfx::Canvas* canvas) override;
   void OnThemeChanged() override;
@@ -91,36 +74,19 @@
                                       const gfx::ImageSkia& icon,
                                       const gfx::Rect& bounds);
 
-  // Draws a progress bar at the bottom of the tab icon to indicate loading
-  // progress.
-  void PaintLoadingProgressIndicator(gfx::Canvas* canvas,
-                                     gfx::RectF bounds,
-                                     SkColor color);
-
   // Paint either the indeterimate throbber or progress indicator according to
   // current tab state.
   void PaintLoadingAnimation(gfx::Canvas* canvas, const gfx::Rect& bounds);
 
-  void UpdatePendingAnimationState();
-
-  // Returns false if painting the loading animation would paint the same thing
-  // that's already painted.
-  bool LoadingAnimationNeedsRepaint() const;
-
   // Gets either the crashed icon or favicon to be rendered for the tab.
   const gfx::ImageSkia& GetIconToPaint();
 
-  // Paints a placeholder image for the favicon if one should be painted.
-  void MaybePaintFaviconPlaceholder(gfx::Canvas* canvas,
-                                    const gfx::Rect& bounds);
   // Paint the favicon if it's available.
   void MaybePaintFavicon(gfx::Canvas* canvas,
                          const gfx::ImageSkia& icon,
                          const gfx::Rect& bounds);
   bool HasNonDefaultFavicon() const;
 
-  void MaybeStartFaviconFadeIn();
-
   // Sets the icon. Depending on the URL the icon may be automatically themed.
   void SetIcon(const GURL& url, const gfx::ImageSkia& favicon);
 
@@ -152,12 +118,9 @@
   bool inhibit_loading_animation_ = false;
 
   // The point in time when the tab icon was first painted in the loading state.
-  // TODO(pbos): Remove after |kNewTabLoadingAnimation| launches.
-  base::TimeTicks old_animation_loading_start_time_;
+  base::TimeTicks loading_animation_start_time_;
 
   // Paint state for the loading animation after the most recent waiting paint.
-  // TODO(pbos): After |kNewTabLoadingAnimation| launches, remove the need for
-  // |waiting_state_|, the new animation only uses the |elapsed_time| member.
   gfx::ThrobberWaitingState waiting_state_;
 
   // When the favicon_ has theming applied to it, the themed version will be
@@ -176,16 +139,6 @@
   // it will be drawn off the bottom.
   double hiding_fraction_ = 0.0;
 
-  base::TimeTicks last_animation_update_time_;
-  // True if the loading animation should keep looping until it hits the start.
-  // We don't want to start the finished-loading animation until it's there.
-  bool keep_loading_animation_running_ = false;
-  // Animation state to be used for the next painted frame. This state updates
-  // on state transitions and timer updates.
-  LoadingAnimationState pending_animation_state_;
-  // Last painted animation state.
-  LoadingAnimationState animation_state_;
-
   // Crash animation (in place of favicon). Lazily created since most of the
   // time it will be unneeded.
   std::unique_ptr<CrashAnimation> crash_animation_;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 07b33cc..30adfbb 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -56,7 +56,6 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
@@ -392,14 +391,6 @@
     tab_at(i)->StepLoadingAnimation(elapsed_time);
 }
 
-bool TabStrip::IsAnyIconAnimating() const {
-  for (int i = 0; i < tab_count(); i++) {
-    if (tab_at(i)->ShowingLoadingAnimation())
-      return true;
-  }
-  return false;
-}
-
 void TabStrip::SetStackedLayout(bool stacked_layout) {
   if (stacked_layout == stacked_layout_)
     return;
@@ -419,15 +410,6 @@
     tab_at(i)->Layout();
 }
 
-void TabStrip::StartHighlight(int model_index) {
-  tab_at(model_index)->StartPulse();
-}
-
-void TabStrip::StopAllHighlighting() {
-  for (int i = 0; i < tab_count(); ++i)
-    tab_at(i)->StopPulse();
-}
-
 void TabStrip::AddTabAt(int model_index, TabRendererData data, bool is_active) {
   const bool was_single_tab_mode = SingleTabMode();
 
@@ -437,7 +419,7 @@
     view_index = GetIndexOf(tab_at(model_index - 1)) + 1;
   }
 
-  Tab* tab = new Tab(this, animation_container_.get());
+  Tab* tab = new Tab(this);
   AddChildViewAt(tab, view_index);
   const bool pinned = data.pinned;
   tab->SetData(std::move(data));
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index d7ec646c..402e409b 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/views/tabs/tab_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "ui/base/material_design/material_design_controller_observer.h"
-#include "ui/gfx/animation/animation_container.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
@@ -117,8 +116,6 @@
   // keep the throbbers in sync.
   void UpdateLoadingAnimations(const base::TimeDelta& elapsed_time);
 
-  bool IsAnyIconAnimating() const;
-
   // If |adjust_layout| is true the stacked layout changes based on whether the
   // user uses a mouse or a touch device with the tabstrip.
   void set_adjust_layout(bool adjust_layout) { adjust_layout_ = adjust_layout; }
@@ -140,12 +137,6 @@
   // Returns the bounds of the new tab button.
   gfx::Rect new_tab_button_bounds() const { return new_tab_button_bounds_; }
 
-  // Starts highlighting the tab at the specified index.
-  void StartHighlight(int model_index);
-
-  // Stops all tab higlighting.
-  void StopAllHighlighting();
-
   // Adds a tab at the specified index.
   void AddTabAt(int model_index, TabRendererData data, bool is_active);
 
@@ -683,11 +674,6 @@
   // Valid for the lifetime of a drag over us.
   std::unique_ptr<DropArrow> drop_arrow_;
 
-  // To ensure all tabs pulse at the same time they share the same animation
-  // container. This is that animation container.
-  scoped_refptr<gfx::AnimationContainer> animation_container_{
-      new gfx::AnimationContainer()};
-
   // MouseWatcher is used for two things:
   // . When a tab is closed to reset the layout.
   // . When a mouse is used and the layout dynamically adjusts and is currently
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index 211f507d..ab930b60 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -34,12 +34,6 @@
 
 using views::Widget;
 
-namespace {
-bool UsingNewLoadingAnimation() {
-  return base::FeatureList::IsEnabled(features::kNewTabLoadingAnimation);
-}
-}  // namespace
-
 class FakeTabController : public TabController {
  public:
   FakeTabController() {}
@@ -319,15 +313,6 @@
 
   void SetupFakeClock(TabIcon* icon) { icon->clock_ = &fake_clock_; }
 
-  void FinishRunningLoadingAnimations(TabIcon* icon) {
-    // Forward the clock enough for any running animations to finish.
-    DCHECK(icon->clock_ == &fake_clock_);
-    constexpr base::TimeDelta delta = base::TimeDelta::FromMilliseconds(2000);
-    fake_clock_.Advance(delta);
-    icon->StepLoadingAnimation(icon->waiting_state_.elapsed_time + delta);
-    icon->animation_state_ = icon->pending_animation_state_;
-  }
-
  protected:
   void InitWidget(Widget* widget) {
     Widget::InitParams params(CreateParams(Widget::InitParams::TYPE_WINDOW));
@@ -412,7 +397,7 @@
   InitWidget(&widget);
 
   FakeTabController tab_controller;
-  Tab tab(&tab_controller, nullptr);
+  Tab tab(&tab_controller);
   widget.GetContentsView()->AddChildView(&tab);
   tab.SizeToPreferredSize();
 
@@ -446,7 +431,7 @@
   InitWidget(&widget);
 
   FakeTabController controller;
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
 
   SkBitmap bitmap;
@@ -498,7 +483,7 @@
   InitWidget(&widget);
 
   FakeTabController controller;
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
   tab.SizeToPreferredSize();
 
@@ -548,7 +533,7 @@
 // shouldn't change the insets of the close button.
 TEST_F(TabTest, CloseButtonLayout) {
   FakeTabController tab_controller;
-  Tab tab(&tab_controller, nullptr);
+  Tab tab(&tab_controller);
   tab.SetBounds(0, 0, 100, 50);
   LayoutTab(&tab);
   gfx::Insets close_button_insets = GetCloseButton(tab)->GetInsets();
@@ -569,7 +554,7 @@
   Widget widget;
   InitWidget(&widget);
   FakeTabController tab_controller;
-  Tab tab(&tab_controller, nullptr);
+  Tab tab(&tab_controller);
   widget.GetContentsView()->AddChildView(&tab);
 
   views::ImageButton* tab_close_button = GetCloseButton(tab);
@@ -590,7 +575,7 @@
   InitWidget(&widget);
 
   FakeTabController tab_controller;
-  Tab tab(&tab_controller, nullptr);
+  Tab tab(&tab_controller);
   widget.GetContentsView()->AddChildView(&tab);
   tab.SizeToPreferredSize();
 
@@ -613,12 +598,6 @@
   EXPECT_TRUE(icon->layer());
   data.network_state = TabNetworkState::kNone;
   tab.SetData(data);
-  if (UsingNewLoadingAnimation()) {
-    // The post-loading animation should still be playing (loading bar fades
-    // out).
-    EXPECT_TRUE(icon->ShowingLoadingAnimation());
-    FinishRunningLoadingAnimations(icon);
-  }
   EXPECT_FALSE(icon->ShowingLoadingAnimation());
 
   // Simulate a tab that should hide throbber.
@@ -648,12 +627,6 @@
   EXPECT_TRUE(icon->layer());
   data.network_state = TabNetworkState::kNone;
   tab.SetData(data);
-  if (UsingNewLoadingAnimation()) {
-    // The post-loading animation should still be playing (loading bar fades
-    // out).
-    EXPECT_TRUE(icon->ShowingLoadingAnimation());
-    FinishRunningLoadingAnimations(icon);
-  }
   EXPECT_FALSE(icon->ShowingLoadingAnimation());
 
   // After loading is done, simulate another resource starting to load.
@@ -664,7 +637,6 @@
   // Reset.
   data.network_state = TabNetworkState::kNone;
   tab.SetData(data);
-  FinishRunningLoadingAnimations(icon);
   EXPECT_FALSE(icon->ShowingLoadingAnimation());
 
   // Simulate a drag started and stopped during a load: layer painting stops
@@ -683,7 +655,6 @@
   EXPECT_TRUE(icon->layer());
   data.network_state = TabNetworkState::kNone;
   tab.SetData(data);
-  FinishRunningLoadingAnimations(icon);
   EXPECT_FALSE(icon->ShowingLoadingAnimation());
 
   // Simulate a tab load starting and stopping during tab dragging (or with
@@ -695,13 +666,12 @@
   EXPECT_FALSE(icon->layer());
   data.network_state = TabNetworkState::kNone;
   tab.SetData(data);
-  FinishRunningLoadingAnimations(icon);
   EXPECT_FALSE(icon->ShowingLoadingAnimation());
 }
 
 TEST_F(TabTest, TitleHiddenWhenSmall) {
   FakeTabController tab_controller;
-  Tab tab(&tab_controller, nullptr);
+  Tab tab(&tab_controller);
   tab.SetBounds(0, 0, 100, 50);
   EXPECT_GT(GetTitleWidth(tab), 0);
   tab.SetBounds(0, 0, 0, 50);
@@ -715,7 +685,7 @@
   for (bool is_active_tab : {false, true}) {
     FakeTabController controller;
     controller.set_active_tab(is_active_tab);
-    Tab tab(&controller, nullptr);
+    Tab tab(&controller);
     widget.GetContentsView()->AddChildView(&tab);
     tab.SizeToPreferredSize();
 
@@ -734,7 +704,7 @@
 
   FakeTabController controller;
   controller.set_active_tab(false);
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
   const int width = tab.tab_style()->GetContentsInsets().width() +
                     Tab::kMinimumContentsWidthForCloseButtons;
@@ -757,7 +727,7 @@
 
   FakeTabController controller;
   controller.set_active_tab(true);
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
   tab.SetBounds(0, 0, 200, 50);
   const views::View* close = GetCloseButton(tab);
@@ -777,7 +747,7 @@
   InitWidget(&widget);
 
   FakeTabController controller;
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
 
   tab.SizeToPreferredSize();
@@ -800,7 +770,7 @@
 
   FakeTabController controller;
   controller.set_active_tab(true);
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
   TabRendererData data;
   data.alert_state = TabAlertState::AUDIO_PLAYING;
@@ -845,7 +815,7 @@
   Widget widget;
   InitWidget(&widget);
   FakeTabController controller;
-  Tab tab(&controller, nullptr);
+  Tab tab(&controller);
   widget.GetContentsView()->AddChildView(&tab);
 
   for (const auto& colors : color_schemes) {
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index 42eb390..d09ca46 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -990,6 +990,10 @@
 }
 
 void AppMenu::ExecuteCommand(int command_id, int mouse_event_flags) {
+  for (AppMenuObserver& observer : observer_list_) {
+    observer.OnExecuteCommand(command_id);
+  }
+
   if (IsBookmarkCommand(command_id)) {
     UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenBookmark",
                         menu_opened_timer_.Elapsed());
diff --git a/chrome/browser/ui/views/toolbar/app_menu_observer.h b/chrome/browser/ui/views/toolbar/app_menu_observer.h
index e1b9404..bd01c37 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_observer.h
+++ b/chrome/browser/ui/views/toolbar/app_menu_observer.h
@@ -15,6 +15,10 @@
   // Called after AppMenu::RunMenu().
   virtual void AppMenuShown() {}
 
+  // Called when a menu item is activated, just before the associated command is
+  // executed.
+  virtual void OnExecuteCommand(int command_id) {}
+
  protected:
   virtual ~AppMenuObserver() {}
 };
diff --git a/chrome/browser/ui/views/toolbar/outdated_upgrade_bubble_view_browsertest.cc b/chrome/browser/ui/views/toolbar/outdated_upgrade_bubble_view_browsertest.cc
index 8e25e28..58fb244 100644
--- a/chrome/browser/ui/views/toolbar/outdated_upgrade_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/outdated_upgrade_bubble_view_browsertest.cc
@@ -31,23 +31,11 @@
   DISALLOW_COPY_AND_ASSIGN(OutdatedUpgradeBubbleTest);
 };
 
-#if defined(OS_MACOSX)
-// This bubble doesn't show on Mac right now: https://crbug.com/764111
-#define MAYBE_InvokeUi_Outdated DISABLED_InvokeUi_Outdated
-#else
-#define MAYBE_InvokeUi_Outdated InvokeUi_Outdated
-#endif
-IN_PROC_BROWSER_TEST_F(OutdatedUpgradeBubbleTest, MAYBE_InvokeUi_Outdated) {
+IN_PROC_BROWSER_TEST_F(OutdatedUpgradeBubbleTest, InvokeUi_Outdated) {
   ShowAndVerifyUi();
 }
 
-#if defined(OS_MACOSX)
-// This bubble doesn't show on Mac right now: https://crbug.com/764111
-#define MAYBE_InvokeUi_NoAutoUpdate DISABLED_InvokeUi_NoAutoUpdate
-#else
-#define MAYBE_InvokeUi_NoAutoUpdate InvokeUi_NoAutoUpdate
-#endif
-IN_PROC_BROWSER_TEST_F(OutdatedUpgradeBubbleTest, MAYBE_InvokeUi_NoAutoUpdate) {
+IN_PROC_BROWSER_TEST_F(OutdatedUpgradeBubbleTest, InvokeUi_NoAutoUpdate) {
   ShowAndVerifyUi();
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
index 4434490c..5c11e0ec 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
@@ -29,6 +29,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/menu_listener.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/test/menu_test_utils.h"
 
@@ -40,6 +41,45 @@
       ->app_menu_button();
 }
 
+class AppMenuShowingWaiter : public views::MenuListener {
+ public:
+  explicit AppMenuShowingWaiter(AppMenuButton* button);
+  ~AppMenuShowingWaiter() override;
+
+  // views::MenuListener:
+  void OnMenuOpened() override;
+
+  void Wait();
+
+ private:
+  bool observed_ = false;
+  AppMenuButton* button_;
+  base::RunLoop run_loop_;
+};
+
+AppMenuShowingWaiter::AppMenuShowingWaiter(AppMenuButton* button)
+    : button_(button) {
+  DCHECK(button_);
+  if (button_->IsMenuShowing())
+    observed_ = true;
+  button_->AddMenuListener(this);
+}
+
+AppMenuShowingWaiter::~AppMenuShowingWaiter() {
+  button_->RemoveMenuListener(this);
+}
+
+void AppMenuShowingWaiter::OnMenuOpened() {
+  observed_ = true;
+  if (run_loop_.running())
+    run_loop_.Quit();
+}
+
+void AppMenuShowingWaiter::Wait() {
+  if (!observed_)
+    run_loop_.Run();
+}
+
 // Tests clicking on an overflowed toolbar action. This is called when the app
 // menu is open, and handles actually clicking on the action.
 // |button| specifies the mouse button to click with. Optionally
@@ -66,9 +106,10 @@
   // Click on the toolbar action to activate it.
   gfx::Point action_view_loc =
       ui_test_utils::GetCenterInScreenCoordinates(action_view);
-  ui_controls::SendMouseMove(action_view_loc.x(), action_view_loc.y());
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      button, ui_controls::DOWN | ui_controls::UP));
+  EXPECT_TRUE(
+      ui_controls::SendMouseMove(action_view_loc.x(), action_view_loc.y()));
+  EXPECT_TRUE(ui_controls::SendMouseClick(button));
+  base::RunLoop().RunUntilIdle();
 
   if (toolbar_action_view)
     *toolbar_action_view = action_view;
@@ -124,10 +165,10 @@
 
   // Click on the first menu item (which shares bounds, but overlaps, the second
   // row action).
-  ui_controls::SendMouseMove(action_view_loc.x(), action_view_loc.y());
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP));
-  views::test::WaitForMenuClosureAnimation();
+  EXPECT_TRUE(
+      ui_controls::SendMouseMove(action_view_loc.x(), action_view_loc.y()));
+  EXPECT_TRUE(ui_controls::SendMouseClick(ui_controls::LEFT));
+  base::RunLoop().RunUntilIdle();
   // Test resumes in the main test body.
 }
 
@@ -160,15 +201,9 @@
   ToolbarActionsBar::disable_animations_for_testing_ = false;
 }
 
-#if defined(USE_OZONE)
-// ozone bringup - http://crbug.com/401304
-#define MAYBE_TestClickingOnOverflowedAction DISABLED_TestClickingOnOverflowedAction
-#else
-#define MAYBE_TestClickingOnOverflowedAction TestClickingOnOverflowedAction
-#endif
 // Tests clicking on an overflowed extension action.
 IN_PROC_BROWSER_TEST_F(ToolbarActionViewInteractiveUITest,
-                       MAYBE_TestClickingOnOverflowedAction) {
+                       TestClickingOnOverflowedAction) {
   // Load an extension.
   ASSERT_TRUE(LoadExtension(
       test_data_dir_.AppendASCII("ui").AppendASCII("browser_action_popup")));
@@ -186,14 +221,14 @@
   // Click on the app button.
   gfx::Point app_button_loc =
       ui_test_utils::GetCenterInScreenCoordinates(app_menu_button);
-  ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y());
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP));
-  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(
+      ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y()));
+  EXPECT_TRUE(ui_controls::SendMouseClick(ui_controls::LEFT));
+  AppMenuShowingWaiter waiter(app_menu_button);
+  waiter.Wait();
 
   TestOverflowedToolbarAction(browser(), ui_controls::LEFT, nullptr);
 
-  base::RunLoop().RunUntilIdle();
   // The app menu should no longer be showing.
   EXPECT_FALSE(app_menu_button->IsMenuShowing());
 
@@ -201,18 +236,16 @@
   EXPECT_TRUE(listener.WaitUntilSatisfied());
 }
 
-// TODO(jonross): determine cause of new flake, and restore previous MAYBE
-// conditions. Temporarily disabling due to number of flakes (crbug.com/639010)
-// Tests the context menus of overflowed extension actions.
-
-#if defined(USE_OZONE)
-// ozone bringup - http://crbug.com/401304
+#if defined(OS_CHROMEOS)
+// TODO(pkasting): https://crbug.com/911374 Menu controller thinks the mouse is
+// already down when handling the left click.
 #define MAYBE_TestContextMenuOnOverflowedAction \
   DISABLED_TestContextMenuOnOverflowedAction
 #else
 #define MAYBE_TestContextMenuOnOverflowedAction \
   TestContextMenuOnOverflowedAction
 #endif
+// Tests the context menus of overflowed extension actions.
 IN_PROC_BROWSER_TEST_F(ToolbarActionViewInteractiveUITest,
                        MAYBE_TestContextMenuOnOverflowedAction) {
   views::MenuController::TurnOffMenuSelectionHoldForTest();
@@ -235,36 +268,32 @@
     extension_service()->AddExtension(extension.get());
   }
 
-  ASSERT_EQ(16u, browser()
-                     ->window()
-                     ->GetToolbarActionsBar()
-                     ->toolbar_actions_unordered()
-                     .size());
+  const auto* const actions_bar = browser()->window()->GetToolbarActionsBar();
+  ASSERT_EQ(16u, actions_bar->toolbar_actions_unordered().size());
 
   // Reduce visible count to 0 so that all actions are overflowed.
   ToolbarActionsModel::Get(profile())->SetVisibleIconCount(0);
 
   BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser());
-  // Click on the app button, and then right-click on the first toolbar action.
+  // Click on the app button.
   gfx::Point app_button_loc =
       ui_test_utils::GetCenterInScreenCoordinates(app_menu_button);
-  ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y());
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP));
-  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(
+      ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y()));
+  EXPECT_TRUE(ui_controls::SendMouseClick(ui_controls::LEFT));
+  AppMenuShowingWaiter waiter(app_menu_button);
+  waiter.Wait();
 
-  // Right clicks on the action view, this should trigger the context menu.
+  // Right click on the action view. This should trigger the context menu.
   ToolbarActionView* action_view = nullptr;
   TestOverflowedToolbarAction(browser(), ui_controls::RIGHT, &action_view);
-  base::RunLoop().RunUntilIdle();
 
   // Ensure that the menu actually opened.
   EXPECT_TRUE(action_view->IsMenuRunningForTesting());
 
-  // Triggers the action within the context menu. This should load the extension
+  // Trigger the action within the context menu. This should load the extension
   // webpage, and close the menu.
   TestWhileContextMenuOpen(browser(), action_view);
-  base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(action_view->IsMenuRunningForTesting());
   // We should have navigated to the extension's home page, which is google.com.
@@ -277,8 +306,7 @@
 // already open results in closing the popup, and doesn't re-open it.
 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || \
     (defined(OS_WIN) && !defined(NDEBUG))
-// Flaky on Linux and ChromeOS; see https://crbug.com/617056.
-// Fails on Win debug; see https;//crbug.com/788112.
+// Flaky; see https://crbug.com/617056.
 #define MAYBE_DoubleClickToolbarActionToClose \
     DISABLED_DoubleClickToolbarActionToClose
 #elif defined(OS_MACOSX)
@@ -311,8 +339,7 @@
   EXPECT_TRUE(ui_test_utils::SendMouseMoveSync(
      ui_test_utils::GetCenterInScreenCoordinates(toolbar_action_view)));
 
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP));
+  EXPECT_TRUE(ui_controls::SendMouseClick(ui_controls::LEFT));
   EXPECT_TRUE(listener.WaitUntilSatisfied());
 
   ExtensionActionViewController* view_controller =
@@ -321,21 +348,14 @@
   EXPECT_EQ(view_controller, toolbar_actions_bar->popup_owner());
   EXPECT_TRUE(view_controller->IsShowingPopup());
 
-  {
-    // Click down on the action button; this should close the popup.
-    content::WindowedNotificationObserver observer(
-        extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
-        content::NotificationService::AllSources());
-    // For reasons unbeknownst to me, SendMouseEventsSync() with only a down
-    // event will cause Windows to hang. Using SendMouseEvents() and running all
-    // pending UI tasks seems to do the trick.
-    base::RunLoop loop;
-    EXPECT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
-        ui_controls::LEFT, ui_controls::DOWN, loop.QuitClosure()));
-    loop.Run();
-    observer.Wait();  // Wait for the popup to fully close.
-  }
-
+  // Click down on the action button; this should close the popup, though the
+  // closure may be asynchronous.
+  content::WindowedNotificationObserver observer(
+      extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+      content::NotificationService::AllSources());
+  EXPECT_TRUE(
+      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
+  observer.Wait();
   EXPECT_FALSE(view_controller->IsShowingPopup());
   EXPECT_EQ(nullptr, toolbar_actions_bar->popup_owner());
 
@@ -346,16 +366,8 @@
   EXPECT_EQ(nullptr, toolbar_actions_bar->popup_owner());
 }
 
-#if defined(USE_OZONE)
-// ozone bringup - http://crbug.com/401304
-#define MAYBE_ActivateOverflowedToolbarActionWithKeyboard \
-  DISABLED_ActivateOverflowedToolbarActionWithKeyboard
-#else
-#define MAYBE_ActivateOverflowedToolbarActionWithKeyboard \
-  ActivateOverflowedToolbarActionWithKeyboard
-#endif
 IN_PROC_BROWSER_TEST_F(ToolbarActionViewInteractiveUITest,
-                       MAYBE_ActivateOverflowedToolbarActionWithKeyboard) {
+                       ActivateOverflowedToolbarActionWithKeyboard) {
   views::MenuController::TurnOffMenuSelectionHoldForTest();
   // Load an extension with an action.
   ASSERT_TRUE(LoadExtension(
@@ -373,24 +385,23 @@
   BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser());
   gfx::Point app_button_loc =
       ui_test_utils::GetCenterInScreenCoordinates(app_menu_button);
-  ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y());
-  EXPECT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP));
-
-  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(
+      ui_controls::SendMouseMove(app_button_loc.x(), app_button_loc.y()));
+  EXPECT_TRUE(ui_controls::SendMouseClick(ui_controls::LEFT));
+  AppMenuShowingWaiter waiter(app_menu_button);
+  waiter.Wait();
 
   EXPECT_TRUE(app_menu_button->IsMenuShowing());
   gfx::NativeWindow native_window =
       views::MenuController::GetActiveInstance()->owner()->GetNativeWindow();
   // Send a key down event followed by the return key.
   // The key down event targets the toolbar action in the app menu.
-  ui_controls::SendKeyPress(native_window, ui::VKEY_DOWN, false, false, false,
-                            false);
+  EXPECT_TRUE(ui_controls::SendKeyPress(native_window, ui::VKEY_DOWN, false,
+                                        false, false, false));
   // The triggering of the action and subsequent widget destruction occurs on
   // the message loop. Wait for this all to complete.
   EXPECT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
       native_window, ui::VKEY_RETURN, false, false, false, false));
-  base::RunLoop().RunUntilIdle();
 
   // The menu should be closed.
   EXPECT_FALSE(app_menu_button->IsMenuShowing());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc
index 2b5f47e..11607fb 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button_views_unittest.cc
@@ -51,7 +51,6 @@
   }
   bool IsEnabledAt(int index) const override { return false; }
   ui::MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
-  void HighlightChangedTo(int index) override {}
   void ActivatedAt(int index) override {}
   void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) override {}
   ui::MenuModelDelegate* GetMenuModelDelegate() const override {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index fde06d60c..443b3e2 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -89,7 +89,7 @@
 #include "chrome/browser/signin/signin_global_error_factory.h"
 #endif
 
-#if !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
+#if !defined(OS_CHROMEOS)
 #include "chrome/browser/ui/views/outdated_upgrade_bubble_view.h"
 #endif
 
@@ -789,8 +789,7 @@
 }
 
 void ToolbarView::ShowOutdatedInstallNotification(bool auto_update_enabled) {
-#if !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
-  // TODO(tapted): Show this on Mac. See http://crbug.com/764111.
+#if !defined(OS_CHROMEOS)
   OutdatedUpgradeBubbleView::ShowBubble(app_menu_button_, browser_,
                                         auto_update_enabled);
 #endif
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
index e34f231..e17550c 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -31,6 +31,7 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "ui/views/controls/menu/menu_listener.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view.h"
@@ -38,10 +39,14 @@
 
 using bookmarks::BookmarkModel;
 
-class ToolbarViewInteractiveUITest : public extensions::ExtensionBrowserTest {
+class ToolbarViewInteractiveUITest : public extensions::ExtensionBrowserTest,
+                                     public views::MenuListener {
  public:
-  ToolbarViewInteractiveUITest();
-  ~ToolbarViewInteractiveUITest() override;
+  ToolbarViewInteractiveUITest() = default;
+  ~ToolbarViewInteractiveUITest() override = default;
+
+  // views::MenuListener:
+  void OnMenuOpened() override;
 
  protected:
   ToolbarView* toolbar_view() { return toolbar_view_; }
@@ -49,87 +54,68 @@
 
   // Performs a drag-and-drop operation by moving the mouse to |start|, clicking
   // the left button, moving the mouse to |end|, and releasing the left button.
-  // TestWhileInDragOperation() is called after the mouse has moved to |end|,
-  // but before the click is released.
   void DoDragAndDrop(const gfx::Point& start, const gfx::Point& end);
 
-  // A function to perform testing actions while in the drag operation from
-  // DoDragAndDrop.
-  void TestWhileInDragOperation();
-
  private:
-  // Finishes the drag-and-drop operation started in DoDragAndDrop().
-  void FinishDragAndDrop(base::Closure quit_closure);
-
   // InProcessBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
-  ToolbarView* toolbar_view_;
-
-  BrowserActionsContainer* browser_actions_;
-
-  // The drag-and-drop background thread.
-  std::unique_ptr<base::Thread> dnd_thread_;
+  ToolbarView* toolbar_view_ = nullptr;
+  BrowserActionsContainer* browser_actions_ = nullptr;
+  bool menu_opened_ = false;
+  base::OnceClosure quit_closure_;
 };
 
-ToolbarViewInteractiveUITest::ToolbarViewInteractiveUITest()
-    : toolbar_view_(NULL),
-      browser_actions_(NULL) {
-}
-
-ToolbarViewInteractiveUITest::~ToolbarViewInteractiveUITest() {
+void ToolbarViewInteractiveUITest::OnMenuOpened() {
+  menu_opened_ = true;
+  ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, ui_controls::UP,
+                                             std::move(quit_closure_));
 }
 
 void ToolbarViewInteractiveUITest::DoDragAndDrop(const gfx::Point& start,
                                                  const gfx::Point& end) {
   // Much of this function is modeled after methods in ViewEventTestBase (in
-  // particular, the |dnd_thread_|, but it's easier to move that here than try
+  // particular, the |dnd_thread|, but it's easier to move that here than try
   // to make ViewEventTestBase play nice with a BrowserView (for the toolbar).
   // TODO(devlin): In a perfect world, this would be factored better.
 
-  // Send the mouse to |start|, and click.
-  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(start));
-  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
-                  ui_controls::LEFT, ui_controls::DOWN));
+  // Begin listening for the app menu to open.
+  toolbar_view()->app_menu_button()->AddMenuListener(this);
 
-  scoped_refptr<content::MessageLoopRunner> runner =
-      new content::MessageLoopRunner();
+  // Send the mouse to |start|, and click.  The event queue must be flushed
+  // after processing the click, or the next mouse move sent may get processed
+  // before the click is fully handled, causing the test to fail.
+  ASSERT_TRUE(ui_controls::SendMouseMove(start.x(), start.y()));
+  ASSERT_TRUE(
+      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
 
-  ui_controls::SendMouseMoveNotifyWhenDone(
-      end.x() + 10, end.y(),
-      base::BindOnce(&ToolbarViewInteractiveUITest::FinishDragAndDrop,
-                     base::Unretained(this), runner->QuitClosure()));
+  // Enqueue an event to move the mouse, which will start a drag.
+  ASSERT_TRUE(ui_controls::SendMouseMove(end.x() + 10, end.y()));
 
-  // Also post a move task to the drag and drop thread.
-  if (!dnd_thread_.get()) {
-    dnd_thread_.reset(new base::Thread("mouse_move_thread"));
-    dnd_thread_->Start();
-  }
+  // Enqueue an event to move the mouse to |end|.  This must be done on a
+  // background thread, since starting a drag triggers a nested message loop
+  // that filters messages other than mouse events, so further tasks on the main
+  // message loop will be blocked.  Because the mouse move above is already
+  // queued, this is guaranteed to queue after that, and end the drag operation
+  // in the right place.
+  base::ScopedAllowBaseSyncPrimitivesForTesting allow_thread_join;
+  base::Thread dnd_thread("mouse_move_thread");
+  dnd_thread.Start();
+  dnd_thread.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove),
+                                end.x(), end.y()));
 
-  dnd_thread_->task_runner()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove), end.x(),
-                     end.y()),
-      base::TimeDelta::FromMilliseconds(200));
-  runner->Run();
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitWhenIdleClosure();
+  run_loop.Run();
+  EXPECT_TRUE(menu_opened_);
 
   // The app menu should have closed once the drag-and-drop completed.
   EXPECT_FALSE(toolbar_view()->app_menu_button()->IsMenuShowing());
-}
 
-void ToolbarViewInteractiveUITest::TestWhileInDragOperation() {
-  EXPECT_TRUE(toolbar_view()->app_menu_button()->IsMenuShowing());
-}
-
-void ToolbarViewInteractiveUITest::FinishDragAndDrop(
-    base::Closure quit_closure) {
-  base::ScopedAllowBaseSyncPrimitivesForTesting allow_thread_join;
-  dnd_thread_.reset();
-  TestWhileInDragOperation();
-  ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, ui_controls::UP,
-                                             quit_closure);
+  toolbar_view()->app_menu_button()->RemoveMenuListener(this);
 }
 
 void ToolbarViewInteractiveUITest::SetUpCommandLine(
@@ -152,13 +138,13 @@
   BrowserAppMenuButton::g_open_app_immediately_for_testing = false;
 }
 
-// Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc,
-// since these are also disabled on Linux for drag and drop.
-// TODO(erg): Fix DND tests on linux_aura. crbug.com/163931
-#if defined(OS_LINUX) && defined(USE_AURA)
+#if defined(OS_MACOSX)
+// TODO(pkasting): https://crbug.com/910435 Test hangs in the run loop on Mac, I
+// don't know why.
 #define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag
-#elif defined(OS_MACOSX)
-// Illegal thread join on the UI thread, may fix above: http://crbug.com/824570
+#elif defined(USE_OZONE)
+// TODO(pkasting): https://crbug.com/910423 Can't post mouse events from
+// background threads on Ozone, which is required to avoid hanging.
 #define MAYBE_TestAppMenuOpensOnDrag DISABLED_TestAppMenuOpensOnDrag
 #else
 #define MAYBE_TestAppMenuOpensOnDrag TestAppMenuOpensOnDrag
diff --git a/chrome/browser/ui/webui/app_management/app_management.mojom b/chrome/browser/ui/webui/app_management/app_management.mojom
index 3de0881..c536372 100644
--- a/chrome/browser/ui/webui/app_management/app_management.mojom
+++ b/chrome/browser/ui/webui/app_management/app_management.mojom
@@ -39,6 +39,7 @@
   GetApps() => (array<App> apps);
   GetExtensionAppPermissionMessages(string app_id) =>
       (array<ExtensionAppPermissionMessage> messages);
+  SetPinned(string app_id, apps.mojom.OptionalBool pinned);
   SetPermission(string app_id,
                 apps.mojom.Permission permission);
   Uninstall(string app_id);
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index d9bf68c..f2ab1c1 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -19,22 +19,10 @@
 #include "extensions/common/permissions/permission_message.h"
 #include "extensions/common/permissions/permissions_data.h"
 
+using apps::mojom::OptionalBool;
+
 namespace {
 
-app_management::mojom::AppPtr CreateUIAppPtr(const apps::AppUpdate& update) {
-  base::flat_map<uint32_t, apps::mojom::PermissionPtr> permissions;
-  for (const auto& permission : update.Permissions()) {
-    permissions[permission->permission_id] = permission->Clone();
-  }
-
-  return app_management::mojom::App::New(
-      update.AppId(), update.AppType(), update.Name(),
-      base::nullopt /*description*/,
-      apps::mojom::OptionalBool::kUnknown /*is_pinned*/,
-      base::nullopt /*version*/, base::nullopt /*size*/,
-      std::move(permissions));
-}
-
 app_management::mojom::ExtensionAppPermissionMessagePtr
 CreateExtensionAppPermissionMessage(
     const extensions::PermissionMessage& message) {
@@ -45,6 +33,7 @@
   return app_management::mojom::ExtensionAppPermissionMessage::New(
       base::UTF16ToUTF8(message.message()), std::move(submessages));
 }
+
 }  // namespace
 
 AppManagementPageHandler::AppManagementPageHandler(
@@ -53,10 +42,49 @@
     content::WebUI* web_ui)
     : binding_(this, std::move(request)),
       page_(std::move(page)),
-      profile_(Profile::FromWebUI(web_ui)) {}
+      profile_(Profile::FromWebUI(web_ui))
+#if defined(OS_CHROMEOS)
+      ,
+      shelf_delegate_(this)
+#endif
+{
+  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+
+  // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
+  // incognito
+  if (!proxy)
+    return;
+
+  Observe(&proxy->Cache());
+}
 
 AppManagementPageHandler::~AppManagementPageHandler() {}
 
+void AppManagementPageHandler::OnPinnedChanged(const std::string& app_id,
+                                               bool pinned) {
+  apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
+
+  // TODO(crbug.com/826982): revisit pending decision on AppServiceProxy in
+  // incognito
+  if (!proxy)
+    return;
+
+  app_management::mojom::AppPtr app;
+
+  proxy->Cache().ForOneApp(app_id, [this, &app](const apps::AppUpdate& update) {
+    if (update.Readiness() == apps::mojom::Readiness::kReady)
+      app = CreateUIAppPtr(update);
+  });
+
+  // If an app with this id is not already installed, do nothing.
+  if (!app)
+    return;
+
+  app->is_pinned = pinned ? OptionalBool::kTrue : OptionalBool::kFalse;
+
+  page_->OnAppChanged(std::move(app));
+}
+
 void AppManagementPageHandler::GetApps(GetAppsCallback callback) {
   apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile_);
 
@@ -66,15 +94,41 @@
     return;
 
   std::vector<app_management::mojom::AppPtr> apps;
-  proxy->Cache().ForEachApp([&apps](const apps::AppUpdate& update) {
+  proxy->Cache().ForEachApp([this, &apps](const apps::AppUpdate& update) {
     apps.push_back(CreateUIAppPtr(update));
   });
 
-  Observe(&proxy->Cache());
-
   std::move(callback).Run(std::move(apps));
 }
 
+void AppManagementPageHandler::GetExtensionAppPermissionMessages(
+    const std::string& app_id,
+    GetExtensionAppPermissionMessagesCallback callback) {
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(profile_);
+  const extensions::Extension* extension = registry->GetExtensionById(
+      app_id, extensions::ExtensionRegistry::ENABLED |
+                  extensions::ExtensionRegistry::DISABLED |
+                  extensions::ExtensionRegistry::BLACKLISTED);
+  std::vector<app_management::mojom::ExtensionAppPermissionMessagePtr> messages;
+  if (extension) {
+    for (const auto& message :
+         extension->permissions_data()->GetPermissionMessages()) {
+      messages.push_back(CreateExtensionAppPermissionMessage(message));
+    }
+  }
+  std::move(callback).Run(std::move(messages));
+}
+
+void AppManagementPageHandler::SetPinned(const std::string& app_id,
+                                         OptionalBool pinned) {
+#if defined(OS_CHROMEOS)
+  shelf_delegate_.SetPinned(app_id, pinned);
+#else
+  NOTREACHED();
+#endif
+}
+
 void AppManagementPageHandler::SetPermission(
     const std::string& app_id,
     apps::mojom::PermissionPtr permission) {
@@ -106,9 +160,34 @@
   // incognito
   if (!proxy)
     return;
+
   proxy->OpenNativeSettings(app_id);
 }
 
+app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
+    const apps::AppUpdate& update) {
+  base::flat_map<uint32_t, apps::mojom::PermissionPtr> permissions;
+  for (const auto& permission : update.Permissions()) {
+    permissions[permission->permission_id] = permission->Clone();
+  }
+
+  auto app = app_management::mojom::App::New();
+  app->id = update.AppId();
+  app->type = update.AppType();
+  app->title = update.Name();
+  app->permissions = std::move(permissions);
+
+  // On other OS's, is_pinned defaults to OptionalBool::kUnknown, which is
+  // used to represent the fact that there is no concept of being pinned.
+#if defined(OS_CHROMEOS)
+  app->is_pinned = shelf_delegate_.IsPinned(update.AppId())
+                       ? OptionalBool::kTrue
+                       : OptionalBool::kFalse;
+#endif
+
+  return app;
+}
+
 void AppManagementPageHandler::OnAppUpdate(const apps::AppUpdate& update) {
   if (update.ReadinessChanged() &&
       update.Readiness() == apps::mojom::Readiness::kUninstalledByUser) {
@@ -121,22 +200,3 @@
     page_->OnAppChanged(CreateUIAppPtr(update));
   }
 }
-
-void AppManagementPageHandler::GetExtensionAppPermissionMessages(
-    const std::string& app_id,
-    GetExtensionAppPermissionMessagesCallback callback) {
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(profile_);
-  const extensions::Extension* extension = registry->GetExtensionById(
-      app_id, extensions::ExtensionRegistry::ENABLED |
-                  extensions::ExtensionRegistry::DISABLED |
-                  extensions::ExtensionRegistry::BLACKLISTED);
-  std::vector<app_management::mojom::ExtensionAppPermissionMessagePtr> messages;
-  if (extension) {
-    for (const auto& message :
-         extension->permissions_data()->GetPermissionMessages()) {
-      messages.push_back(CreateExtensionAppPermissionMessage(message));
-    }
-  }
-  std::move(callback).Run(std::move(messages));
-}
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
index f7e02aa5..0d78023 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.h
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/app_management/app_management.mojom.h"
+#include "chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h"
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
@@ -24,17 +25,23 @@
                            content::WebUI* web_ui);
   ~AppManagementPageHandler() override;
 
+  void OnPinnedChanged(const std::string& app_id, bool pinned);
+
   // app_management::mojom::PageHandler:
   void GetApps(GetAppsCallback callback) override;
-  void SetPermission(const std::string& app_id,
-                     apps::mojom::PermissionPtr permission) override;
   void GetExtensionAppPermissionMessages(
       const std::string& app_id,
       GetExtensionAppPermissionMessagesCallback callback) override;
+  void SetPinned(const std::string& app_id,
+                 apps::mojom::OptionalBool pinned) override;
+  void SetPermission(const std::string& app_id,
+                     apps::mojom::PermissionPtr permission) override;
   void Uninstall(const std::string& app_id) override;
   void OpenNativeSettings(const std::string& app_id) override;
 
  private:
+  app_management::mojom::AppPtr CreateUIAppPtr(const apps::AppUpdate& update);
+
   // apps::AppRegistryCache::Observer overrides:
   void OnAppUpdate(const apps::AppUpdate& update) override;
 
@@ -44,6 +51,10 @@
 
   Profile* profile_;
 
+#if defined(OS_CHROMEOS)
+  AppManagementShelfDelegate shelf_delegate_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(AppManagementPageHandler);
 };
 
diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
new file mode 100644
index 0000000..1070ace
--- /dev/null
+++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
@@ -0,0 +1,68 @@
+// Copyright 2018 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/webui/app_management/app_management_shelf_delegate_chromeos.h"
+
+#include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
+#include "chrome/services/app_service/public/mojom/types.mojom.h"
+
+using apps::mojom::OptionalBool;
+
+AppManagementShelfDelegate::AppManagementShelfDelegate(
+    AppManagementPageHandler* page_handler)
+    : page_handler_(page_handler) {
+  ChromeLauncherController::instance()->shelf_model()->AddObserver(this);
+}
+
+AppManagementShelfDelegate::~AppManagementShelfDelegate() {
+  ChromeLauncherController::instance()->shelf_model()->RemoveObserver(this);
+}
+
+bool AppManagementShelfDelegate::IsPinned(const std::string& app_id) {
+  return ChromeLauncherController::instance()->IsAppPinned(app_id);
+}
+
+void AppManagementShelfDelegate::SetPinned(const std::string& app_id,
+                                           OptionalBool pinned) {
+  if (pinned == OptionalBool::kTrue) {
+    ChromeLauncherController::instance()->PinAppWithID(app_id);
+  } else if (pinned == OptionalBool::kFalse) {
+    ChromeLauncherController::instance()->UnpinAppWithID(app_id);
+  } else {
+    NOTREACHED();
+  }
+}
+
+void AppManagementShelfDelegate::ShelfItemAdded(int index) {
+  const std::string& app_id = ChromeLauncherController::instance()
+                                  ->shelf_model()
+                                  ->items()[index]
+                                  .id.app_id;
+  bool is_pinned = ChromeLauncherController::instance()->IsAppPinned(app_id);
+
+  page_handler_->OnPinnedChanged(app_id, is_pinned);
+}
+
+void AppManagementShelfDelegate::ShelfItemRemoved(
+    int index,
+    const ash::ShelfItem& old_item) {
+  // If the app has been removed from the shelf model, it is not longer pinned.
+  page_handler_->OnPinnedChanged(old_item.id.app_id, false);
+}
+
+void AppManagementShelfDelegate::ShelfItemChanged(
+    int index,
+    const ash::ShelfItem& old_item) {
+  const std::string& app_id = ChromeLauncherController::instance()
+                                  ->shelf_model()
+                                  ->items()[index]
+                                  .id.app_id;
+  bool is_pinned = ChromeLauncherController::instance()->IsAppPinned(app_id);
+
+  page_handler_->OnPinnedChanged(app_id, is_pinned);
+}
diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h
new file mode 100644
index 0000000..40c3ba4
--- /dev/null
+++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h
@@ -0,0 +1,37 @@
+// Copyright 2018 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_WEBUI_APP_MANAGEMENT_APP_MANAGEMENT_SHELF_DELEGATE_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_APP_MANAGEMENT_APP_MANAGEMENT_SHELF_DELEGATE_CHROMEOS_H_
+
+#include "ash/public/cpp/shelf_model_observer.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/app_management/app_management.mojom.h"
+
+class AppManagementPageHandler;
+
+// This is a helper class used by the AppManagementPageHandler to manage
+// shelf-related functionality, which is only meaningful when running Chrome OS.
+// It observes the ShelfModel, and notifies the AppManagementPageHandler when
+// apps are pinned or unpinned.
+class AppManagementShelfDelegate : public ash::ShelfModelObserver {
+ public:
+  explicit AppManagementShelfDelegate(AppManagementPageHandler* page_handler);
+  ~AppManagementShelfDelegate() override;
+
+  bool IsPinned(const std::string& app_id);
+  void SetPinned(const std::string& app_id, apps::mojom::OptionalBool pinned);
+
+ private:
+  // ash::ShelfModelObserver:
+  void ShelfItemAdded(int index) override;
+  void ShelfItemRemoved(int index, const ash::ShelfItem& old_item) override;
+  void ShelfItemChanged(int index, const ash::ShelfItem& old_item) override;
+
+  AppManagementPageHandler* page_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppManagementShelfDelegate);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_APP_MANAGEMENT_APP_MANAGEMENT_SHELF_DELEGATE_CHROMEOS_H_
diff --git a/chrome/browser/ui/webui/certificate_viewer_ui.cc b/chrome/browser/ui/webui/certificate_viewer_ui.cc
index d8ba1b0..59e252e 100644
--- a/chrome/browser/ui/webui/certificate_viewer_ui.cc
+++ b/chrome/browser/ui/webui/certificate_viewer_ui.cc
@@ -63,19 +63,6 @@
 
 }  // namespace
 
-CertificateViewerModalDialogUI::CertificateViewerModalDialogUI(
-    content::WebUI* web_ui)
-    : ui::WebDialogUI(web_ui) {
-  // Set up the chrome://view-cert-dialog source.
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(
-      profile,
-      GetWebUIDataSource(chrome::kChromeUICertificateViewerDialogHost));
-}
-
-CertificateViewerModalDialogUI::~CertificateViewerModalDialogUI() {
-}
-
 CertificateViewerUI::CertificateViewerUI(content::WebUI* web_ui)
     : ConstrainedWebDialogUI(web_ui) {
   // Set up the chrome://view-cert source.
diff --git a/chrome/browser/ui/webui/certificate_viewer_ui.h b/chrome/browser/ui/webui/certificate_viewer_ui.h
index 5355a07..b998cd4 100644
--- a/chrome/browser/ui/webui/certificate_viewer_ui.h
+++ b/chrome/browser/ui/webui/certificate_viewer_ui.h
@@ -7,17 +7,6 @@
 
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
-#include "ui/web_dialogs/web_dialog_ui.h"
-
-// The WebUI for chrome://view-cert-dialog
-class CertificateViewerModalDialogUI : public ui::WebDialogUI {
- public:
-  explicit CertificateViewerModalDialogUI(content::WebUI* web_ui);
-  ~CertificateViewerModalDialogUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CertificateViewerModalDialogUI);
-};
 
 // The WebUI for chrome://view-cert
 class CertificateViewerUI : public ConstrainedWebDialogUI {
diff --git a/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chrome/browser/ui/webui/certificate_viewer_webui.cc
index e817c04..2c47114 100644
--- a/chrome/browser/ui/webui/certificate_viewer_webui.cc
+++ b/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -18,7 +18,6 @@
 #include "base/values.h"
 #include "chrome/browser/certificate_viewer.h"
 #include "chrome/browser/platform_util.h"
-#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/certificate_dialogs.h"
 #include "chrome/browser/ui/webui/certificate_viewer_ui.h"
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
@@ -120,17 +119,44 @@
       net::x509_util::CreateCERTCertificateListFromX509Certificate(cert);
   if (nss_certs.empty())
     return;
-  CertificateViewerDialog* dialog =
-      new CertificateViewerDialog(std::move(nss_certs));
-  dialog->Show(web_contents, parent);
+
+  CertificateViewerDialog::ShowConstrained(std::move(nss_certs), web_contents,
+                                           parent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // CertificateViewerDialog
 
-CertificateViewerModalDialog::CertificateViewerModalDialog(
+// static
+CertificateViewerDialog* CertificateViewerDialog::ShowConstrained(
+    net::ScopedCERTCertificateList certs,
+    WebContents* web_contents,
+    gfx::NativeWindow parent) {
+  CertificateViewerDialog* dialog =
+      new CertificateViewerDialog(std::move(certs));
+
+  // TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
+  // on the title for Aura ConstrainedWebDialogUI.
+  dialog->delegate_ = ShowConstrainedWebDialog(
+      web_contents->GetBrowserContext(), dialog, web_contents);
+
+  // Clear the zoom level for the dialog so that it is not affected by the page
+  // zoom setting.
+  content::WebContents* dialog_web_contents =
+      dialog->delegate_->GetWebContents();
+  const GURL dialog_url = dialog->GetDialogContentURL();
+  content::HostZoomMap::Get(dialog_web_contents->GetSiteInstance())
+      ->SetZoomLevelForHostAndScheme(dialog_url.scheme(), dialog_url.host(), 0);
+  return dialog;  // For tests.
+}
+
+gfx::NativeWindow CertificateViewerDialog::GetNativeWebContentsModalDialog() {
+  return delegate_->GetNativeDialog();
+}
+
+CertificateViewerDialog::CertificateViewerDialog(
     net::ScopedCERTCertificateList certs)
-    : nss_certs_(std::move(certs)), webui_(NULL), window_(NULL) {
+    : nss_certs_(std::move(certs)) {
   // Construct the dialog title from the certificate.
   title_ = l10n_util::GetStringFUTF16(
       IDS_CERT_INFO_DIALOG_TITLE,
@@ -138,52 +164,34 @@
           x509_certificate_model::GetTitle(nss_certs_.front().get())));
 }
 
-CertificateViewerModalDialog::~CertificateViewerModalDialog() {
+CertificateViewerDialog::~CertificateViewerDialog() = default;
+
+ui::ModalType CertificateViewerDialog::GetDialogModalType() const {
+  return ui::MODAL_TYPE_NONE;
 }
 
-void CertificateViewerModalDialog::Show(content::WebContents* web_contents,
-                                        gfx::NativeWindow parent) {
-  window_ = chrome::ShowWebDialog(parent,
-                                  web_contents->GetBrowserContext(),
-                                  this);
-}
-
-gfx::NativeWindow
-CertificateViewerModalDialog::GetNativeWebContentsModalDialog() {
-#if defined(USE_AURA)
-  return window_;
-#else
-  NOTREACHED();
-  return NULL;
-#endif
-}
-
-ui::ModalType CertificateViewerModalDialog::GetDialogModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
-base::string16 CertificateViewerModalDialog::GetDialogTitle() const {
+base::string16 CertificateViewerDialog::GetDialogTitle() const {
   return title_;
 }
 
-GURL CertificateViewerModalDialog::GetDialogContentURL() const {
-  return GURL(chrome::kChromeUICertificateViewerDialogURL);
+GURL CertificateViewerDialog::GetDialogContentURL() const {
+  return GURL(chrome::kChromeUICertificateViewerURL);
 }
 
-void CertificateViewerModalDialog::GetWebUIMessageHandlers(
+void CertificateViewerDialog::GetWebUIMessageHandlers(
     std::vector<WebUIMessageHandler*>* handlers) const {
   handlers->push_back(new CertificateViewerDialogHandler(
-      const_cast<CertificateViewerModalDialog*>(this),
+      const_cast<CertificateViewerDialog*>(this),
       net::x509_util::DupCERTCertificateList(nss_certs_)));
 }
 
-void CertificateViewerModalDialog::GetDialogSize(gfx::Size* size) const {
+void CertificateViewerDialog::GetDialogSize(gfx::Size* size) const {
   const int kDefaultWidth = 544;
   const int kDefaultHeight = 628;
   size->SetSize(kDefaultWidth, kDefaultHeight);
 }
 
-std::string CertificateViewerModalDialog::GetDialogArgs() const {
+std::string CertificateViewerDialog::GetDialogArgs() const {
   std::string data;
 
   // Certificate information. The keys in this dictionary's general key
@@ -275,67 +283,30 @@
   return data;
 }
 
-void CertificateViewerModalDialog::OnDialogShown(
+void CertificateViewerDialog::OnDialogShown(
     content::WebUI* webui,
     content::RenderViewHost* render_view_host) {
   webui_ = webui;
 }
 
-void CertificateViewerModalDialog::OnDialogClosed(
-    const std::string& json_retval) {
+void CertificateViewerDialog::OnDialogClosed(const std::string& json_retval) {
+  // Don't |delete this|: owned by the constrained dialog manager.
 }
 
-void CertificateViewerModalDialog::OnCloseContents(WebContents* source,
+void CertificateViewerDialog::OnCloseContents(WebContents* source,
                                               bool* out_close_dialog) {
   *out_close_dialog = true;
 }
 
-bool CertificateViewerModalDialog::ShouldShowDialogTitle() const {
+bool CertificateViewerDialog::ShouldShowDialogTitle() const {
   return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// CertificateViewerDialog
-
-CertificateViewerDialog::CertificateViewerDialog(
-    net::ScopedCERTCertificateList certs)
-    : CertificateViewerModalDialog(std::move(certs)), dialog_(NULL) {}
-
-CertificateViewerDialog::~CertificateViewerDialog() {
-}
-
-void CertificateViewerDialog::Show(WebContents* web_contents,
-                                   gfx::NativeWindow parent) {
-  // TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
-  // on the title for Aura ConstrainedWebDialogUI.
-  dialog_ = ShowConstrainedWebDialog(web_contents->GetBrowserContext(), this,
-                                     web_contents);
-
-  // Clear the zoom level for the dialog so that it is not affected by the page
-  // zoom setting.
-  content::WebContents* dialog_web_contents = dialog_->GetWebContents();
-  const GURL dialog_url = GetDialogContentURL();
-  content::HostZoomMap::Get(dialog_web_contents->GetSiteInstance())
-      ->SetZoomLevelForHostAndScheme(dialog_url.scheme(), dialog_url.host(), 0);
-}
-
-gfx::NativeWindow CertificateViewerDialog::GetNativeWebContentsModalDialog() {
-  return dialog_->GetNativeDialog();
-}
-
-GURL CertificateViewerDialog::GetDialogContentURL() const {
-  return GURL(chrome::kChromeUICertificateViewerURL);
-}
-
-ui::ModalType CertificateViewerDialog::GetDialogModalType() const {
-  return ui::MODAL_TYPE_NONE;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // CertificateViewerDialogHandler
 
 CertificateViewerDialogHandler::CertificateViewerDialogHandler(
-    CertificateViewerModalDialog* dialog,
+    CertificateViewerDialog* dialog,
     net::ScopedCERTCertificateList cert_chain)
     : dialog_(dialog), cert_chain_(std::move(cert_chain)) {}
 
diff --git a/chrome/browser/ui/webui/certificate_viewer_webui.h b/chrome/browser/ui/webui/certificate_viewer_webui.h
index bda5b2f..c39620f 100644
--- a/chrome/browser/ui/webui/certificate_viewer_webui.h
+++ b/chrome/browser/ui/webui/certificate_viewer_webui.h
@@ -23,24 +23,29 @@
 
 class ConstrainedWebDialogDelegate;
 
-// Modal dialog for displaying detailed certificate information. This is used in
-// chromeos builds to display detailed information in a modal dialog when the
-// user clicks on "View" from the Certificate Manager dialog.
-class CertificateViewerModalDialog : public ui::WebDialogDelegate {
+// Dialog for displaying detailed certificate information. This is used in linux
+// and chromeos builds to display detailed information in a floating dialog when
+// the user clicks on "Certificate Information" from the lock icon of a web site
+// or "View" from the Certificate Manager.
+class CertificateViewerDialog : public ui::WebDialogDelegate {
  public:
+  static CertificateViewerDialog* ShowConstrained(
+      net::ScopedCERTCertificateList certs,
+      content::WebContents* web_contents,
+      gfx::NativeWindow parent);
+
+  gfx::NativeWindow GetNativeWebContentsModalDialog();
+
+ private:
+  friend class CertificateViewerUITest;
+
   // Construct a certificate viewer for the passed in certificate. A reference
   // to the certificate pointer is added for the lifetime of the certificate
   // viewer.
-  explicit CertificateViewerModalDialog(net::ScopedCERTCertificateList certs);
-  ~CertificateViewerModalDialog() override;
+  explicit CertificateViewerDialog(net::ScopedCERTCertificateList certs);
+  ~CertificateViewerDialog() override;
 
-  virtual void Show(content::WebContents* web_contents,
-                    gfx::NativeWindow parent);
-  virtual gfx::NativeWindow GetNativeWebContentsModalDialog();
-  const content::WebUI* GetWebUI() const { return webui_; }
-
- protected:
-  // Overridden from ui::WebDialogDelegate:
+  // ui::WebDialogDelegate:
   ui::ModalType GetDialogModalType() const override;
   base::string16 GetDialogTitle() const override;
   GURL GetDialogContentURL() const override;
@@ -61,36 +66,8 @@
   // The title of the certificate viewer dialog, Certificate Viewer: CN.
   base::string16 title_;
 
- private:
-  content::WebUI* webui_;
-  gfx::NativeWindow window_;
-  DISALLOW_COPY_AND_ASSIGN(CertificateViewerModalDialog);
-};
-
-// Dialog for displaying detailed certificate information. This is used in linux
-// and chromeos builds to display detailed information in a floating dialog when
-// the user clicks on "Certificate Information" from the lock icon of a web site
-// or "View" from the Certificate Manager.
-class CertificateViewerDialog : public CertificateViewerModalDialog {
- public:
-  // Construct a certificate viewer for the passed in certificate. A reference
-  // to the certificate pointer is added for the lifetime of the certificate
-  // viewer.
-  explicit CertificateViewerDialog(net::ScopedCERTCertificateList certs);
-  ~CertificateViewerDialog() override;
-
-  // CertificateViewerModalDialog overrides.
-  void Show(content::WebContents* web_contents,
-            gfx::NativeWindow parent) override;
-  gfx::NativeWindow GetNativeWebContentsModalDialog() override;
-
- protected:
-  // Overridden from ui::WebDialogDelegate:
-  GURL GetDialogContentURL() const override;
-  ui::ModalType GetDialogModalType() const override;
-
- private:
-  ConstrainedWebDialogDelegate* dialog_;
+  content::WebUI* webui_ = nullptr;
+  ConstrainedWebDialogDelegate* delegate_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(CertificateViewerDialog);
 };
@@ -99,7 +76,7 @@
 // details and export the certificate.
 class CertificateViewerDialogHandler : public content::WebUIMessageHandler {
  public:
-  CertificateViewerDialogHandler(CertificateViewerModalDialog* dialog,
+  CertificateViewerDialogHandler(CertificateViewerDialog* dialog,
                                  net::ScopedCERTCertificateList cert_chain);
   ~CertificateViewerDialogHandler() override;
 
@@ -125,7 +102,7 @@
   int GetCertificateIndex(const base::ListValue* args) const;
 
   // The dialog.
-  CertificateViewerModalDialog* dialog_;
+  CertificateViewerDialog* dialog_;
 
   // The certificate chain.
   net::ScopedCERTCertificateList cert_chain_;
diff --git a/chrome/browser/ui/webui/certificates_handler.cc b/chrome/browser/ui/webui/certificates_handler.cc
index 59325894..d3120861 100644
--- a/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chrome/browser/ui/webui/certificates_handler.cc
@@ -450,11 +450,11 @@
   CERTCertificate* cert = cert_id_map_->CallbackArgsToCert(args);
   if (!cert)
     return;
+
   net::ScopedCERTCertificateList certs;
   certs.push_back(net::x509_util::DupCERTCertificate(cert));
-  CertificateViewerDialog* dialog =
-      new CertificateViewerDialog(std::move(certs));
-  dialog->Show(web_ui()->GetWebContents(), GetParentWindow());
+  CertificateViewerDialog::ShowConstrained(
+      std::move(certs), web_ui()->GetWebContents(), GetParentWindow());
 }
 
 void CertificatesHandler::AssignWebUICallbackId(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index b1e1e853..576fbe3 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -593,10 +593,6 @@
 #if defined(USE_NSS_CERTS) && defined(USE_AURA)
   if (url.host_piece() == chrome::kChromeUICertificateViewerHost)
     return &NewWebUI<CertificateViewerUI>;
-#if defined(OS_CHROMEOS)
-  if (url.host_piece() == chrome::kChromeUICertificateViewerDialogHost)
-    return &NewWebUI<CertificateViewerModalDialogUI>;
-#endif
 #endif  // USE_NSS_CERTS && USE_AURA
 
   if (url.host_piece() == chrome::kChromeUIPolicyHost)
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
index 14482ec..6aea49cf 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -263,9 +263,9 @@
              Polymer.dom.flush();
              let item = activityLog.shadowRoot.querySelector(
                  'activity-log-item');
-             let apiCall = item.shadowRoot.getElementById('api-call');
+             let activityKey = item.shadowRoot.getElementById('activity-key');
              window.domAutomationController.send(
-                 apiCall.innerText === 'test.sendMessage');
+                 activityKey.innerText === 'test.sendMessage');
          });
       )",
       &has_api_call));
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
index 1504269..81bd450 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/bind_helpers.h"
+#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
@@ -239,9 +240,10 @@
     PrintCallback callback) {
   size_t size_in_kb = print_data->size() / 1024;
   UMA_HISTOGRAM_MEMORY_KB("Printing.CUPS.PrintDocumentSize", size_in_kb);
-
-  StartLocalPrint(ticket_json, print_data, preview_web_contents_,
-                  std::move(callback));
+  std::unique_ptr<base::Value> job_settings =
+      base::JSONReader::Read(ticket_json);
+  StartLocalPrint(base::Value::FromUniquePtrValue(std::move(job_settings)),
+                  print_data, preview_web_contents_, std::move(callback));
 }
 
 }  // namespace printing
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
index dc23201..8b767a7c 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
@@ -7,8 +7,10 @@
 #include <string>
 #include <utility>
 
+#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/task/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
@@ -124,8 +126,10 @@
     const gfx::Size& page_size,
     const scoped_refptr<base::RefCountedMemory>& print_data,
     PrintCallback callback) {
-  StartLocalPrint(ticket_json, print_data, preview_web_contents_,
-                  std::move(callback));
+  std::unique_ptr<base::Value> job_settings =
+      base::JSONReader::Read(ticket_json);
+  StartLocalPrint(base::Value::FromUniquePtrValue(std::move(job_settings)),
+                  print_data, preview_web_contents_, std::move(callback));
 }
 
 }  // namespace printing
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_utils.cc b/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
index 48eca940..8bcf51f 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/stl_util.h"
@@ -180,17 +179,10 @@
   std::move(done_callback).Run();
 }
 
-void StartLocalPrint(const std::string& ticket_json,
-                     const scoped_refptr<base::RefCountedMemory>& print_data,
+void StartLocalPrint(base::Value job_settings,
+                     scoped_refptr<base::RefCountedMemory> print_data,
                      content::WebContents* preview_web_contents,
                      PrinterHandler::PrintCallback callback) {
-  std::unique_ptr<base::DictionaryValue> job_settings =
-      base::DictionaryValue::From(base::JSONReader::Read(ticket_json));
-  if (!job_settings) {
-    std::move(callback).Run(base::Value("Invalid settings"));
-    return;
-  }
-
   // Get print view manager.
   PrintPreviewDialogController* dialog_controller =
       PrintPreviewDialogController::GetInstance();
@@ -204,11 +196,8 @@
     return;
   }
 
-  bool system_dialog = false;
-  job_settings->GetBoolean(kSettingShowSystemDialog, &system_dialog);
-  bool open_in_pdf = false;
-  job_settings->GetBoolean(kSettingOpenPDFInPreview, &open_in_pdf);
-  if (system_dialog || open_in_pdf) {
+  if (job_settings.FindBoolKey(kSettingShowSystemDialog).value_or(false) ||
+      job_settings.FindBoolKey(kSettingOpenPDFInPreview).value_or(false)) {
     // Run the callback early, or the modal dialogs will prevent the preview
     // from closing until they do.
     std::move(callback).Run(base::Value());
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_utils.h b/chrome/browser/ui/webui/print_preview/print_preview_utils.h
index 92016d22..ed8a23fa 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_utils.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_utils.h
@@ -41,9 +41,9 @@
     const base::DictionaryValue& cdd);
 
 // Starts a local print of |print_data| with print settings dictionary
-// |ticket_json|. Runs |callback| on failure or success.
-void StartLocalPrint(const std::string& ticket_json,
-                     const scoped_refptr<base::RefCountedMemory>& print_data,
+// |job_settings|. Runs |callback| on failure or success.
+void StartLocalPrint(base::Value job_settings,
+                     scoped_refptr<base::RefCountedMemory> print_data,
                      content::WebContents* preview_web_contents,
                      PrinterHandler::PrintCallback callback);
 
diff --git a/chrome/browser/ui/webui/profile_helper.cc b/chrome/browser/ui/webui/profile_helper.cc
index c980bfa..48a822c 100644
--- a/chrome/browser/ui/webui/profile_helper.cc
+++ b/chrome/browser/ui/webui/profile_helper.cc
@@ -47,11 +47,10 @@
   return base::UTF16ToUTF8(entry->GetUserName());
 }
 
-void ShowReauthDialog(const std::string& user_name,
+void ShowUnlockDialog(const std::string& user_name,
                       Profile* system_profile,
                       Profile::CreateStatus status) {
-  UserManagerProfileDialog::ShowReauthDialog(
-      system_profile, user_name, signin_metrics::Reason::REASON_UNLOCK);
+  UserManagerProfileDialog::ShowUnlockDialog(system_profile, user_name);
 }
 
 void DeleteProfileCallback(std::unique_ptr<ScopedKeepAlive> keep_alive,
@@ -69,7 +68,7 @@
       ShowUserManager(ProfileManager::CreateCallback());
     } else {
       ShowUserManager(
-          base::Bind(&ShowReauthDialog, GetProfileUserName(profile)));
+          base::Bind(&ShowUnlockDialog, GetProfileUserName(profile)));
     }
   } else {
     profiles::FindOrCreateNewWindowForProfile(
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 7dacdfff..7932d706 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -213,6 +213,43 @@
   return existing_printer.uri() == new_printer.uri();
 }
 
+// Assumes |info| is a dictionary.
+void SetPpdReference(const Printer::PpdReference& ppd_ref, base::Value* info) {
+  if (!ppd_ref.user_supplied_ppd_url.empty()) {
+    info->SetKey("ppdRefUserSuppliedPpdUrl",
+                 base::Value(ppd_ref.user_supplied_ppd_url));
+  } else if (!ppd_ref.effective_make_and_model.empty()) {
+    info->SetKey("ppdRefEffectiveMakeAndModel",
+                 base::Value(ppd_ref.effective_make_and_model));
+  } else {  // Must be autoconf, shouldn't be possible
+    NOTREACHED() << "Succeeded in PPD matching without emm";
+  }
+}
+
+Printer::PpdReference GetPpdReference(const base::Value* info) {
+  const char ppd_ref_pathname[] = "printerPpdReference";
+  auto* user_supplied_ppd_url =
+      info->FindPath({ppd_ref_pathname, "userSuppliedPPDUrl"});
+  auto* effective_make_and_model =
+      info->FindPath({ppd_ref_pathname, "effectiveMakeAndModel"});
+  auto* autoconf = info->FindPath({ppd_ref_pathname, "autoconf"});
+
+  if (user_supplied_ppd_url != nullptr) {
+    DCHECK(!effective_make_and_model && !autoconf);
+    return Printer::PpdReference{user_supplied_ppd_url->GetString(), "", false};
+  }
+
+  if (effective_make_and_model != nullptr) {
+    DCHECK(!user_supplied_ppd_url && !autoconf);
+    return Printer::PpdReference{"", effective_make_and_model->GetString(),
+                                 false};
+  }
+
+  // Otherwise it must be autoconf
+  DCHECK(autoconf && autoconf->GetBool());
+  return Printer::PpdReference{"", "", true};
+}
+
 }  // namespace
 
 CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui)
@@ -470,11 +507,42 @@
   PRINTER_LOG(DEBUG) << "Resolved printer information: make_and_model("
                      << make_and_model << ") autoconf(" << ipp_everywhere
                      << ")";
-  base::DictionaryValue info;
-  info.SetString("manufacturer", make);
-  info.SetString("model", model);
-  info.SetString("makeAndModel", make_and_model);
-  info.SetBoolean("autoconf", ipp_everywhere);
+
+  // Bundle printer metadata
+  base::Value info(base::Value::Type::DICTIONARY);
+  info.SetKey("manufacturer", base::Value(make));
+  info.SetKey("model", base::Value(model));
+  info.SetKey("makeAndModel", base::Value(make_and_model));
+  info.SetKey("autoconf", base::Value(ipp_everywhere));
+
+  if (ipp_everywhere) {
+    info.SetKey("ppdReferenceResolved", base::Value(true));
+    ResolveJavascriptCallback(base::Value(callback_id), info);
+    return;
+  }
+
+  PpdProvider::PrinterSearchData ppd_search_data;
+  ppd_search_data.make_and_model.push_back(make_and_model);
+
+  // Try to resolve the PPD matching.
+  ppd_provider_->ResolvePpdReference(
+      ppd_search_data,
+      base::BindOnce(&CupsPrintersHandler::OnPpdResolved,
+                     weak_factory_.GetWeakPtr(), callback_id, std::move(info)));
+}
+
+void CupsPrintersHandler::OnPpdResolved(const std::string& callback_id,
+                                        base::Value info,
+                                        PpdProvider::CallbackResultCode res,
+                                        const Printer::PpdReference& ppd_ref) {
+  if (res != PpdProvider::CallbackResultCode::SUCCESS) {
+    info.SetKey("ppdReferenceResolved", base::Value(false));
+    ResolveJavascriptCallback(base::Value(callback_id), info);
+    return;
+  }
+
+  SetPpdReference(ppd_ref, &info);
+  info.SetKey("ppdReferenceResolved", base::Value(true));
   ResolveJavascriptCallback(base::Value(callback_id), info);
 }
 
@@ -535,12 +603,13 @@
   std::string printer_ppd_path;
   printer_dict->GetString("printerPPDPath", &printer_ppd_path);
 
-  bool autoconf = false;
-  printer_dict->GetBoolean("printerAutoconf", &autoconf);
+  // Checks whether a resolved PPD Reference is available.
+  bool ppd_ref_resolved = false;
+  printer_dict->GetBoolean("printerPpdReferenceResolved", &ppd_ref_resolved);
 
   // Verify that the printer is autoconf or a valid ppd path is present.
-  if (autoconf) {
-    printer->mutable_ppd_reference()->autoconf = true;
+  if (ppd_ref_resolved) {
+    *printer->mutable_ppd_reference() = GetPpdReference(printer_dict);
   } else if (!printer_ppd_path.empty()) {
     RecordPpdSource(kUser);
     GURL tmp = net::FilePathToFileURL(base::FilePath(printer_ppd_path));
@@ -557,7 +626,7 @@
     bool found = false;
     for (const auto& resolved_printer : resolved_printers_[ppd_manufacturer]) {
       if (resolved_printer.name == ppd_model) {
-        *(printer->mutable_ppd_reference()) = resolved_printer.ppd_ref;
+        *printer->mutable_ppd_reference() = resolved_printer.ppd_ref;
         found = true;
         break;
       }
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index 741b87e..f6b4aa1 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -78,6 +78,12 @@
                                    const std::string& make_and_model,
                                    bool ipp_everywhere);
 
+  // Callback for PPD matching attempts;
+  void OnPpdResolved(const std::string& callback_id,
+                     base::Value info,
+                     PpdProvider::CallbackResultCode res,
+                     const Printer::PpdReference& ppd_ref);
+
   void HandleAddCupsPrinter(const base::ListValue* args);
 
   // Handles the result of adding a printer which the user specified the
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 5f022cb..8401ddc 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -2051,6 +2051,8 @@
        IDS_SETTINGS_SAFEBROWSING_ENABLEPROTECTION},
       {"safeBrowsingEnableProtectionDesc",
        IDS_SETTINGS_SAFEBROWSING_ENABLEPROTECTION_DESC},
+      {"syncAndGoogleServicesPrivacyDescription",
+       IDS_SETTINGS_SYNC_AND_GOOGLE_SERVICES_PRIVACY_DESC_UNIFIED_CONSENT},
       {"urlKeyedAnonymizedDataCollection",
        IDS_SETTINGS_ENABLE_URL_KEYED_ANONYMIZED_DATA_COLLECTION},
       {"urlKeyedAnonymizedDataCollectionDesc",
@@ -2077,8 +2079,6 @@
         {"spellingPref", IDS_SETTINGS_SPELLING_PREF_UNIFIED_CONSENT},
         {"spellingDescription",
          IDS_SETTINGS_SPELLING_DESCRIPTION_UNIFIED_CONSENT},
-        {"syncAndPersonalizationLink",
-         IDS_SETTINGS_PRIVACY_MORE_SETTINGS_UNIFIED_CONSENT},
         {"enableLogging", IDS_SETTINGS_ENABLE_LOGGING_UNIFIED_CONSENT},
         {"enableLoggingDesc", IDS_SETTINGS_ENABLE_LOGGING_DESC_UNIFIED_CONSENT},
     };
@@ -2095,7 +2095,6 @@
       {"linkDoctorPrefDesc", IDS_SETTINGS_EMPTY_STRING},
       {"spellingPref", IDS_SETTINGS_SPELLING_PREF},
       {"spellingDescription", IDS_SETTINGS_SPELLING_DESCRIPTION},
-      {"syncAndPersonalizationLink", IDS_SETTINGS_PRIVACY_MORE_SETTINGS},
 #if defined(OS_CHROMEOS)
       {"enableLogging", IDS_SETTINGS_ENABLE_LOGGING_DIAGNOSTIC_AND_USAGE_DATA},
 #else
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index c764b1e4..1c7a58f 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -18,9 +18,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -36,7 +34,6 @@
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_consistency_method.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_error_controller.h"
 #include "components/signin/core/browser/signin_header_helper.h"
 #include "components/signin/core/browser/signin_metrics.h"
@@ -52,16 +49,17 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "google_apis/gaia/gaia_auth_util.h"
+#include "services/identity/public/cpp/accounts_mutator.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_mutator.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #else
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/ui/webui/profile_helper.h"
-#include "components/signin/core/browser/signin_manager.h"
 #endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -335,29 +333,27 @@
     signin_metrics::AccessPoint access_point) {
   Browser* browser =
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
-  bool force_new_tab = false;
-  if (!browser) {
-    // Settings is not displayed in a browser window. Open a new window.
-    browser = new Browser(
-        Browser::CreateParams(Browser::TYPE_TABBED, profile_, true));
-    force_new_tab = true;
-  }
+  if (!browser)
+    return;
+
+  auto* identity_manager =
+      IdentityManagerFactory::GetForProfile(browser->profile());
 
   syncer::SyncService* service = GetSyncService();
   if (service && service->HasUnrecoverableError()) {
     // When the user has an unrecoverable error, they first have to sign out and
     // then sign in again.
-    SigninManagerFactory::GetForProfile(browser->profile())
-        ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS,
-                  signin_metrics::SignoutDelete::IGNORE_METRIC);
+
+    identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount(
+        identity::PrimaryAccountMutator::ClearAccountsAction::kDefault,
+        signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS,
+        signin_metrics::SignoutDelete::IGNORE_METRIC);
   }
 
   // If the signin manager already has an authenticated username, this is a
   // re-auth scenario, and we need to ensure that the user signs in with the
   // same email address.
-  GURL url;
-  if (SigninManagerFactory::GetForProfile(browser->profile())
-          ->IsAuthenticated()) {
+  if (identity_manager->HasPrimaryAccount()) {
     UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
                               signin_metrics::HISTOGRAM_REAUTH_SHOWN,
                               signin_metrics::HISTOGRAM_REAUTH_MAX);
@@ -365,29 +361,14 @@
     SigninErrorController* error_controller =
         SigninErrorControllerFactory::GetForProfile(browser->profile());
     DCHECK(error_controller->HasError());
-    if (!force_new_tab) {
       browser->window()->ShowAvatarBubbleFromAvatarButton(
           BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH,
           signin::ManageAccountsParams(), access_point, false);
-    } else {
-      url = signin::GetReauthURLForTab(
-          access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
-          browser->profile(), error_controller->error_account_id());
-    }
   } else {
-    if (!force_new_tab) {
-      browser->window()->ShowAvatarBubbleFromAvatarButton(
-          BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
-          signin::ManageAccountsParams(), access_point, false);
-    } else {
-      url = signin::GetPromoURLForTab(
-          access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-          true);
-    }
+    browser->window()->ShowAvatarBubbleFromAvatarButton(
+        BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
+        signin::ManageAccountsParams(), access_point, false);
   }
-
-  if (url.is_valid())
-    ShowSingletonTab(browser, url);
 }
 #endif
 
@@ -516,9 +497,6 @@
   if (!dice_enabled && !unified_consent::IsUnifiedConsentFeatureEnabled())
     return accounts;
 
-  AccountTrackerService* account_tracker =
-      AccountTrackerServiceFactory::GetForProfile(profile_);
-
   base::Value::ListStorage& accounts_list = accounts.GetList();
   if (dice_enabled) {
     // If dice is enabled, show all the accounts.
@@ -531,11 +509,10 @@
   } else {
     // If dice is disabled (and unified consent enabled), show only the primary
     // account.
-    std::string primary_account = SigninManagerFactory::GetForProfile(profile_)
-                                      ->GetAuthenticatedAccountId();
-    if (!primary_account.empty()) {
+    auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
+    if (identity_manager->HasPrimaryAccount()) {
       accounts_list.push_back(
-          GetAccountValue(account_tracker->GetAccountInfo(primary_account)));
+          GetAccountValue(identity_manager->GetPrimaryAccountInfo()));
     }
   }
 
@@ -552,12 +529,15 @@
   Browser* browser =
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
 
-  AccountTrackerService* account_tracker =
-      AccountTrackerServiceFactory::GetForProfile(profile_);
-  AccountInfo account =
-      account_tracker->FindAccountInfoByEmail(email->GetString());
+  base::Optional<AccountInfo> maybe_account =
+      IdentityManagerFactory::GetForProfile(profile_)
+          ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress(
+              email->GetString());
+
   signin_ui_util::EnableSyncFromPromo(
-      browser, account, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
+      browser,
+      maybe_account.has_value() ? maybe_account.value() : AccountInfo(),
+      signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
       is_default_promo_account->GetBool());
 }
 #endif
@@ -667,7 +647,7 @@
   // This if-statement is not using IsProfileAuthNeededOrHasErrors(), because
   // in some error cases (e.g. "confirmSyncSettings") the UI still needs to
   // show.
-  if (!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()) {
+  if (!IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount()) {
     // For web-based signin, the signin page is not displayed in an overlay
     // on the settings page. So if we get here, it must be due to the user
     // cancelling signin (by reloading the sync settings page during initial
@@ -761,24 +741,24 @@
     // If the user cannot signout, the profile must be destroyed.
     DCHECK(delete_profile);
   } else {
-    SigninManager* signin_manager =
-        SigninManagerFactory::GetForProfile(profile_);
-    if (signin_manager->IsAuthenticated()) {
+    auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
+    if (identity_manager->HasPrimaryAccount()) {
       if (GetSyncService())
         syncer::RecordSyncEvent(syncer::STOP_FROM_OPTIONS);
 
       signin_metrics::SignoutDelete delete_metric =
           delete_profile ? signin_metrics::SignoutDelete::DELETED
                          : signin_metrics::SignoutDelete::KEEPING;
-      signin_manager->SignOutAndRemoveAllAccounts(
+
+      identity_manager->GetPrimaryAccountMutator()->ClearPrimaryAccount(
+          identity::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll,
           signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, delete_metric);
     } else {
       DCHECK(!delete_profile)
           << "Deleting the profile should only be offered the user is syncing.";
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
-          ->RevokeAllCredentials(
-              signin_metrics::SourceForRefreshTokenOperation::
-                  kSettings_Signout);
+
+      identity_manager->GetAccountsMutator()->RemoveAllAccounts(
+          signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout);
     }
   }
 
@@ -790,11 +770,14 @@
 
 void PeopleHandler::HandlePauseSync(const base::ListValue* args) {
   DCHECK(AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_));
-  SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
-  DCHECK(signin_manager->IsAuthenticated());
-  ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->UpdateCredentials(
-      signin_manager->GetAuthenticatedAccountId(),
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
+  DCHECK(identity_manager->HasPrimaryAccount());
+
+  AccountInfo primary_account_info = identity_manager->GetPrimaryAccountInfo();
+  identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
+      primary_account_info.gaia, primary_account_info.email,
       OAuth2TokenServiceDelegate::kInvalidRefreshToken,
+      primary_account_info.is_under_advanced_protection,
       signin_metrics::SourceForRefreshTokenOperation::kSettings_PauseSync);
 }
 #endif
@@ -845,9 +828,13 @@
           // Sign out the user on desktop Chrome if they click cancel during
           // initial setup.
           if (sync_service->IsFirstSetupInProgress()) {
-            SigninManagerFactory::GetForProfile(profile_)
-                ->SignOut(signin_metrics::ABORT_SIGNIN,
-                          signin_metrics::SignoutDelete::IGNORE_METRIC);
+            IdentityManagerFactory::GetForProfile(profile_)
+                ->GetPrimaryAccountMutator()
+                ->ClearPrimaryAccount(
+                    identity::PrimaryAccountMutator::ClearAccountsAction::
+                        kDefault,
+                    signin_metrics::ABORT_SIGNIN,
+                    signin_metrics::SignoutDelete::IGNORE_METRIC);
           }
 #endif
         }
@@ -920,16 +907,13 @@
   sync_status->SetBoolean("supervisedUser", profile_->IsSupervised());
   sync_status->SetBoolean("childUser", profile_->IsChild());
 
-  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_);
-  DCHECK(signin);
-
   auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
   DCHECK(identity_manager);
 
 #if !defined(OS_CHROMEOS)
   // Signout is not allowed if the user has policy (crbug.com/172204).
   if (!signin_util::IsUserSignoutAllowedForProfile(profile_)) {
-    std::string username = signin->GetAuthenticatedAccountInfo().email;
+    std::string username = identity_manager->GetPrimaryAccountInfo().email;
 
     // If there is no one logged in or if the profile name is empty then the
     // domain name is empty. This happens in browser tests.
@@ -946,16 +930,18 @@
   bool disallowed_by_policy =
       service && service->HasDisableReason(
                      syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
-  sync_status->SetBoolean("signinAllowed", signin->IsSigninAllowed());
+  sync_status->SetBoolean(
+      "signinAllowed", profile_->GetPrefs()->GetBoolean(prefs::kSigninAllowed));
   sync_status->SetBoolean("syncSystemEnabled", (service != nullptr));
   sync_status->SetBoolean("setupInProgress",
                           service && !disallowed_by_policy &&
                               service->IsFirstSetupInProgress() &&
-                              signin->IsAuthenticated());
+                              identity_manager->HasPrimaryAccount());
 
   base::string16 status_label;
   base::string16 link_label;
   sync_ui_util::ActionType action_type = sync_ui_util::NO_ACTION;
+
   bool status_has_error =
       sync_ui_util::GetStatusLabels(profile_, service, identity_manager,
                                     &status_label, &link_label,
@@ -969,7 +955,7 @@
   sync_status->SetBoolean(
       "disabled", !service || disallowed_by_policy ||
                       !service->GetUserSettings()->IsSyncAllowedByPlatform());
-  sync_status->SetBoolean("signedIn", signin->IsAuthenticated());
+  sync_status->SetBoolean("signedIn", identity_manager->HasPrimaryAccount());
   sync_status->SetString(
       "signedInUsername",
       signin_ui_util::GetAuthenticatedUsername(identity_manager));
@@ -1112,7 +1098,8 @@
 }
 
 bool PeopleHandler::IsProfileAuthNeededOrHasErrors() {
-  return !SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
+  return !IdentityManagerFactory::GetForProfile(profile_)
+              ->HasPrimaryAccount() ||
          SigninErrorControllerFactory::GetForProfile(profile_)->HasError();
 }
 
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 91c56a12..7ef1443 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/scoped_account_consistency.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -33,8 +32,6 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/unified_consent/scoped_unified_consent.h"
 #include "content/public/browser/web_contents.h"
@@ -45,8 +42,9 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_web_ui.h"
 #include "content/public/test/web_contents_tester.h"
-#include "google_apis/gaia/oauth2_token_service_delegate.h"
+#include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/identity_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
@@ -874,19 +872,21 @@
       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
 
   SetupInitializedProfileSyncService();
-  DCHECK_EQ(
-      identity_test_env()->identity_manager()->GetPrimaryAccountInfo().email,
-      kTestUser);
-  const std::string& account_id = identity_test_env()
-                                      ->identity_manager()
-                                      ->GetPrimaryAccountInfo()
-                                      .account_id;
-  ProfileOAuth2TokenService* token_service =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
-  token_service->UpdateCredentials(account_id, "refresh_token");
-  // TODO(https://crbug.com/836212): Do not use the delegate directly, because
-  // it is internal API.
-  token_service->GetDelegate()->UpdateAuthError(account_id, error_);
+
+  auto* identity_manager = identity_test_env()->identity_manager();
+  AccountInfo primary_account_info = identity_manager->GetPrimaryAccountInfo();
+  DCHECK_EQ(primary_account_info.email, kTestUser);
+
+  auto* accounts_mutator = identity_manager->GetAccountsMutator();
+  DCHECK(accounts_mutator);
+
+  accounts_mutator->AddOrUpdateAccount(
+      primary_account_info.gaia, primary_account_info.email, "refresh_token",
+      primary_account_info.is_under_advanced_protection,
+      signin_metrics::SourceForRefreshTokenOperation::kUnknown);
+
+  identity::UpdatePersistentErrorOfRefreshTokenForAccount(
+      identity_manager, primary_account_info.account_id, error_);
 
   ON_CALL(*mock_pss_, GetDisableReasons())
       .WillByDefault(Return(syncer::SyncService::DISABLE_REASON_NONE));
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chrome/browser/ui/webui/signin/inline_login_handler.cc
index 7e04d93..68051bd 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -104,9 +104,9 @@
 
   const GURL& current_url = web_ui()->GetWebContents()->GetURL();
   signin_metrics::AccessPoint access_point =
-      signin::GetAccessPointForPromoURL(current_url);
+      signin::GetAccessPointForEmbeddedPromoURL(current_url);
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url);
 
   if (reason != signin_metrics::Reason::REASON_REAUTHENTICATION &&
       reason != signin_metrics::Reason::REASON_UNLOCK &&
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index dda5745..3a5c3aa4 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -277,7 +277,7 @@
     browser = handler_->GetDesktopBrowser();
 
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url_);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url_);
   if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
 // Constants are only available on Windows for the Google Credential
 // Provider for Windows. Other platforms will just close the dialog here.
@@ -306,9 +306,10 @@
   about_signin_internals->OnRefreshTokenReceived("Successful");
 
   // Prime the account tracker with this combination of gaia id/display email.
+  AccountTrackerService* account_tracker_service =
+      AccountTrackerServiceFactory::GetForProfile(profile_);
   std::string account_id =
-      AccountTrackerServiceFactory::GetForProfile(profile_)->SeedAccountInfo(
-          gaia_id_, email_);
+      account_tracker_service->SeedAccountInfo(gaia_id_, email_);
 
   identity::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_);
@@ -337,12 +338,14 @@
   if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
       reason == signin_metrics::Reason::REASON_UNLOCK ||
       reason == signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) {
+    account_tracker_service->SetIsAdvancedProtectionAccount(
+        account_id, result.is_under_advanced_protection);
     ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
         ->UpdateCredentials(account_id, result.refresh_token,
                             signin_metrics::SourceForRefreshTokenOperation::
                                 kInlineLoginHandler_Signin);
 
-    if (signin::IsAutoCloseEnabledInURL(current_url_)) {
+    if (signin::IsAutoCloseEnabledInEmbeddedURL(current_url_)) {
       // Close the gaia sign in tab via a task to make sure we aren't in the
       // middle of any webui handler code.
       bool show_account_management = ShouldShowAccountManagement(
@@ -412,8 +415,8 @@
   // OneClickSigninSyncStarter will delete itself once the job is done.
   new OneClickSigninSyncStarter(
       profile_, browser, gaia_id_, email_, password_, refresh_token,
-      signin::GetAccessPointForPromoURL(current_url),
-      signin::GetSigninReasonForPromoURL(current_url), profile_mode,
+      signin::GetAccessPointForEmbeddedPromoURL(current_url),
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url), profile_mode,
       base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
 }
 
@@ -482,7 +485,7 @@
     handler_->HandleLoginError(error.ToString(), base::string16());
 
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url_);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url_);
   if (reason != signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
     AboutSigninInternals* about_signin_internals =
         AboutSigninInternalsFactory::GetForProfile(profile_);
@@ -537,7 +540,7 @@
   content::WebContents* contents = web_ui()->GetWebContents();
   const GURL& current_url = contents->GetURL();
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url);
 
 #if defined(OS_WIN)
   if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
@@ -615,7 +618,7 @@
   // find the right profile to reauthenticate.  Otherwise the profile can be
   // taken from web_ui().
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url);
 
   Profile* profile = Profile::FromWebUI(web_ui());
   if (reason != signin_metrics::Reason::REASON_FETCH_LST_ONLY &&
@@ -704,7 +707,7 @@
     Profile* profile,
     Profile::CreateStatus status) {
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(params.url);
+      signin::GetSigninReasonForEmbeddedPromoURL(params.url);
 
   std::string default_email;
   net::GetValueForKeyInQuery(params.url, "email", &default_email);
@@ -756,7 +759,7 @@
   }
 
   signin_metrics::AccessPoint access_point =
-      signin::GetAccessPointForPromoURL(params.url);
+      signin::GetAccessPointForEmbeddedPromoURL(params.url);
   LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED);
   bool switch_to_advanced =
       params.choose_what_to_sync &&
@@ -830,7 +833,7 @@
   content::WebContents* contents = web_ui()->GetWebContents();
   const GURL& current_url = contents->GetURL();
   signin_metrics::Reason reason =
-      signin::GetSigninReasonForPromoURL(current_url);
+      signin::GetSigninReasonForEmbeddedPromoURL(current_url);
 
   if (reason == signin_metrics::Reason::REASON_FETCH_LST_ONLY) {
     base::Value error_value(base::Value::Type::DICTIONARY);
@@ -887,8 +890,8 @@
 
   const GURL& current_url = contents->GetLastCommittedURL();
   signin_metrics::AccessPoint access_point =
-      signin::GetAccessPointForPromoURL(current_url);
-  bool auto_close = signin::IsAutoCloseEnabledInURL(current_url);
+      signin::GetAccessPointForEmbeddedPromoURL(current_url);
+  bool auto_close = signin::IsAutoCloseEnabledInEmbeddedURL(current_url);
 
   if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) {
     RedirectToNtpOrAppsPage(contents, access_point);
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 499700b..eccd49b 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -12,8 +12,8 @@
 #include "build/buildflag.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/signin_promo.h"
-#include "chrome/browser/signin/signin_tracker_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/signin_view_controller_delegate.h"
@@ -28,6 +28,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 using content::MessageLoopRunner;
 
@@ -43,14 +44,12 @@
     "  return e.querySelector('input[type=password]');"
     "})()";
 
-// The SignInObserver observes the signin manager and blocks until a
-// GoogleSigninSucceeded or a GoogleSigninFailed notification is fired.
-class SignInObserver : public SigninTracker::Observer {
+// The SignInObserver observes the signin manager and blocks until a signin
+// success or failure notification is fired.
+class SignInObserver : public identity::IdentityManager::Observer {
  public:
   SignInObserver() : seen_(false), running_(false), signed_in_(false) {}
 
-  virtual ~SignInObserver() {}
-
   // Returns whether a GoogleSigninSucceeded event has happened.
   bool DidSignIn() {
     return signed_in_;
@@ -68,14 +67,13 @@
     EXPECT_TRUE(seen_);
   }
 
-  void SigninFailed(const GoogleServiceAuthError& error) override {
+  void OnPrimaryAccountSigninFailed(
+      const GoogleServiceAuthError& error) override {
     DVLOG(1) << "Google signin failed.";
     QuitLoopRunner();
   }
 
-  void AccountAddedToCookie(const GoogleServiceAuthError& error) override {}
-
-  void SigninSuccess() override {
+  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override {
     DVLOG(1) << "Google signin succeeded.";
     signed_in_ = true;
     QuitLoopRunner();
@@ -131,28 +129,10 @@
   run_loop.Run();
 }
 
-// Returns true if the Dice signin page is used, and false if the embedded
-// signin flow is used.
-// The Dice signin page is shown in a full tab, whereas the embedded signin flow
-// runs inside a WebUI.
-bool IsDiceSigninPageEnabled(Profile* profile) {
-  signin::AccountConsistencyMethod account_consistency =
-      AccountConsistencyModeManager::GetMethodForProfile(profile);
-  return (account_consistency != signin::AccountConsistencyMethod::kMirror) &&
-         signin::DiceMethodGreaterOrEqual(
-             account_consistency,
-             signin::AccountConsistencyMethod::kDiceMigration);
-}
-
 // Returns the render frame host where Gaia credentials can be filled in.
 content::RenderFrameHost* GetSigninFrame(content::WebContents* web_contents) {
-  if (IsDiceSigninPageEnabled(
-          Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
-    // Dice displays the Gaia page directly in a tab.
-    return web_contents->GetMainFrame();
-  }
-  // Embedded signin flow, uses a sub-frame in WebUI.
-  return signin::GetAuthFrame(web_contents, "signin-frame");
+  // Dice displays the Gaia page directly in a tab.
+  return web_contents->GetMainFrame();
 }
 
 // Waits until the condition is met, by polling.
@@ -273,21 +253,15 @@
   ASSERT_TRUE(content::ExecuteScript(GetSigninFrame(web_contents), js));
 
   // Fill the password input field.
-  if (IsDiceSigninPageEnabled(browser->profile())) {
-    std::string password_script = kGetPasswordFieldFromDiceSigninPage;
-    // Wait until the password field exists.
-    WaitUntilCondition(
-        base::BindLambdaForTesting([&browser, &password_script]() -> bool {
-          return EvaluateBooleanScriptInSigninFrame(
-              browser, password_script + " != null");
-        }),
-        "Could not find Dice password field");
-    js = password_script + ".value = '" + password + "';";
-  } else {
-    WaitUntilAnyElementExistsInSigninFrame(browser, {"password"});
-    js = "document.getElementById('password').value = '" + password + "';";
-  }
-
+  std::string password_script = kGetPasswordFieldFromDiceSigninPage;
+  // Wait until the password field exists.
+  WaitUntilCondition(
+      base::BindLambdaForTesting([&browser, &password_script]() -> bool {
+        return EvaluateBooleanScriptInSigninFrame(browser,
+                                                  password_script + " != null");
+      }),
+      "Could not find Dice password field");
+  js = password_script + ".value = '" + password + "';";
   js += "document.getElementById('passwordNext').click();";
   ASSERT_TRUE(content::ExecuteScript(GetSigninFrame(web_contents), js));
 }
@@ -323,42 +297,20 @@
                   const std::string& username,
                   const std::string& password) {
   SignInObserver signin_observer;
-  std::unique_ptr<SigninTracker> tracker =
-      SigninTrackerFactory::CreateForProfile(browser->profile(),
-                                             &signin_observer);
+  ScopedObserver<identity::IdentityManager, SignInObserver>
+      scoped_signin_observer(&signin_observer);
+  scoped_signin_observer.Add(
+      IdentityManagerFactory::GetForProfile(browser->profile()));
+
   signin_metrics::AccessPoint access_point =
       signin_metrics::AccessPoint::ACCESS_POINT_MENU;
-
-  if (IsDiceSigninPageEnabled(browser->profile())) {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-    chrome::ShowBrowserSignin(browser, access_point);
-    content::WebContents* active_contents =
-        browser->tab_strip_model()->GetActiveWebContents();
-    DCHECK(active_contents);
-    content::TestNavigationObserver observer(
-        active_contents, 1, content::MessageLoopRunner::QuitMode::DEFERRED);
-    observer.Wait();
-#else
-    NOTREACHED();
-#endif
-  } else {
-    GURL signin_url = signin::GetPromoURLForTab(
-        access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
-        false);
-    DVLOG(1) << "Navigating to " << signin_url;
-    // For some tests, the window is not shown yet and this might be the first
-    // tab navigation, so GetActiveWebContents() for CURRENT_TAB is NULL. That's
-    // why we use NEW_FOREGROUND_TAB rather than the CURRENT_TAB used by default
-    // in ui_test_utils::NavigateToURL().
-    ui_test_utils::NavigateToURLWithDisposition(
-        browser, signin_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-
-    // Wait for the WebUI embedding the signin flow to be ready.
-    DVLOG(1) << "Wait for login UI to be ready.";
-    WaitUntilUIReady(browser);
-  }
-
+  chrome::ShowBrowserSignin(browser, access_point);
+  content::WebContents* active_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+  DCHECK(active_contents);
+  content::TestNavigationObserver observer(
+      active_contents, 1, content::MessageLoopRunner::QuitMode::DEFERRED);
+  observer.Wait();
   DVLOG(1) << "Sign in user: " << username;
   ExecuteJsToSigninInSigninFrame(browser, username, password);
   signin_observer.Wait();
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
index 789ee86..2e1f097 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -218,7 +218,7 @@
     // have been run, to give them a chance to initialize the profile.
     OpenNewWindowForProfile(profile, Profile::CREATE_STATUS_INITIALIZED);
   } else if (is_force_signin_enabled) {
-    OpenSigninDialogForProfile(profile);
+    OpenForceSigninDialogForProfile(profile);
   }
   profile_creation_type_ = NO_CREATION_IN_PROGRESS;
 }
@@ -237,12 +237,10 @@
       profile, status);
 }
 
-void SigninCreateProfileHandler::OpenSigninDialogForProfile(Profile* profile) {
-  UserManagerProfileDialog::ShowSigninDialog(
-      web_ui()->GetWebContents()->GetBrowserContext(), profile->GetPath(),
-      signin_util::IsForceSigninEnabled()
-          ? signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT
-          : signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT);
+void SigninCreateProfileHandler::OpenForceSigninDialogForProfile(
+    Profile* profile) {
+  UserManagerProfileDialog::ShowForceSigninDialog(
+      web_ui()->GetWebContents()->GetBrowserContext(), profile->GetPath());
 }
 
 void SigninCreateProfileHandler::ShowProfileCreationError(
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.h b/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
index 44899388..12e3a060 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
@@ -101,7 +101,7 @@
                                        Profile::CreateStatus status);
 
   // Opens a new signin dialog for |profile|.
-  virtual void OpenSigninDialogForProfile(Profile* profile);
+  virtual void OpenForceSigninDialogForProfile(Profile* profile);
 
   // This callback is run after a new browser (but not the window) has been
   // created for the new profile.
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
index 44a3468e..db57f7a 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
@@ -87,7 +87,7 @@
 
   // Mock this method so that we don't actually open the signin dialog during
   // the test.
-  MOCK_METHOD1(OpenSigninDialogForProfile, void(Profile* profile));
+  MOCK_METHOD1(OpenForceSigninDialogForProfile, void(Profile* profile));
 
  private:
   TestingProfileManager* profile_manager_;
@@ -171,7 +171,7 @@
   EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _));
 
   // Expect no signin dialog opened for the new profile.
-  EXPECT_CALL(*handler(), OpenSigninDialogForProfile(_)).Times(0);
+  EXPECT_CALL(*handler(), OpenForceSigninDialogForProfile(_)).Times(0);
 
   // Create a profile.
   base::ListValue list_args;
@@ -203,7 +203,7 @@
   EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _)).Times(0);
 
   // Expect a signin dialog opened for the new profile.
-  EXPECT_CALL(*handler(), OpenSigninDialogForProfile(_)).Times(1);
+  EXPECT_CALL(*handler(), OpenForceSigninDialogForProfile(_)).Times(1);
 
   base::ListValue list_args;
   list_args.AppendString(kTestProfileName);
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index 6b61b979..91b4e01 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -370,9 +370,8 @@
     // password token, the user must perform a full online reauth.
     RecordAuthenticatedLaunchUserEvent(
         AuthenticatedLaunchUserEvent::GAIA_REAUTH_DIALOG);
-    UserManagerProfileDialog::ShowReauthDialogWithProfilePath(
-        browser_context, email_address_, profile_path,
-        signin_metrics::Reason::REASON_UNLOCK);
+    UserManagerProfileDialog::ShowUnlockDialogWithProfilePath(
+        browser_context, email_address_, profile_path);
   } else if (entry->IsSigninRequired() && entry->IsSupervised()) {
     // Supervised profile will only be locked when force-sign-in is enabled
     // and it shouldn't be unlocked. Display the error message directly via
@@ -398,13 +397,11 @@
         web_ui()->GetWebContents()->GetBrowserContext());
   } else {
     // Fresh sign in via user manager without existing email address.
+    DCHECK(signin_util::IsForceSigninEnabled());
     RecordAuthenticatedLaunchUserEvent(
         AuthenticatedLaunchUserEvent::FORCED_PRIMARY_SIGNIN_DIALOG);
-    UserManagerProfileDialog::ShowSigninDialog(
-        browser_context, profile_path,
-        signin_util::IsForceSigninEnabled()
-            ? signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT
-            : signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT);
+    UserManagerProfileDialog::ShowForceSigninDialog(browser_context,
+                                                    profile_path);
   }
 }
 
@@ -564,9 +561,8 @@
   // Password has changed.  Go through online signin flow.
   DCHECK(!email_address_.empty());
   oauth_client_.reset();
-  UserManagerProfileDialog::ShowReauthDialog(
-      web_ui()->GetWebContents()->GetBrowserContext(), email_address_,
-      signin_metrics::Reason::REASON_UNLOCK);
+  UserManagerProfileDialog::ShowUnlockDialog(
+      web_ui()->GetWebContents()->GetBrowserContext(), email_address_);
 }
 
 void UserManagerScreenHandler::OnNetworkError(int response_code) {
diff --git a/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc b/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc
index 6d43b1b..d151d56 100644
--- a/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc
@@ -197,6 +197,7 @@
 IN_PROC_BROWSER_TEST_F(UserManagerUIAuthenticatedUserBrowserTest,
                        ForcedPrimarySignin) {
   Init();
+  signin_util::SetForceSigninForTesting(true);
 
   LaunchAuthenticatedUser("");
 
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index c0035d64..61096387 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -204,6 +204,33 @@
   return SiteSettingSource::kPreference;
 }
 
+// Retrieves the source of a chooser exception as a string. This method uses the
+// CalculateSiteSettingSource method above to calculate the correct string to
+// use.
+std::string GetSourceStringForChooserException(
+    Profile* profile,
+    ContentSettingsType content_type,
+    content_settings::SettingSource source) {
+  // Prepare the parameters needed by CalculateSiteSettingSource
+  content_settings::SettingInfo info;
+  info.source = source;
+
+  // Chooser exceptions do not use a PermissionContextBase for their
+  // permissions.
+  PermissionResult permission_result(CONTENT_SETTING_DEFAULT,
+                                     PermissionStatusSource::UNSPECIFIED);
+
+  // The |origin| parameter is only used for |CONTENT_SETTINGS_TYPE_ADS| with
+  // the |kSafeBrowsingSubresourceFilter| feature flag enabled, so an empty GURL
+  // is used.
+  SiteSettingSource calculated_source = CalculateSiteSettingSource(
+      profile, content_type, /*origin=*/GURL::EmptyGURL(), info,
+      permission_result);
+  DCHECK(calculated_source == SiteSettingSource::kPolicy ||
+         calculated_source == SiteSettingSource::kPreference);
+  return SiteSettingSourceToString(calculated_source);
+}
+
 ChooserContextBase* GetUsbChooserContext(Profile* profile) {
   return UsbChooserContextFactory::GetForProfile(profile);
 }
@@ -589,6 +616,8 @@
   }
 
   ChooserContextBase* chooser_context = chooser_type.get_context(profile);
+  ContentSettingsType content_type =
+      ContentSettingsTypeFromGroupName(std::string(chooser_type.name));
   std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       chooser_context->GetAllGrantedObjects();
   AllOriginObjects all_origin_objects;
@@ -597,14 +626,20 @@
     // TODO(https://crbug.com/854329): Include policy controlled objects
     // when the UI is capable of displaying them properly as policy controlled
     // objects.
-    if (object->source == SiteSettingSourceToString(SiteSettingSource::kPolicy))
+    if (object->source ==
+        content_settings::SettingSource::SETTING_SOURCE_POLICY) {
       continue;
-    std::string name = chooser_context->GetObjectName(object->object);
+    }
+
+    std::string name = chooser_context->GetObjectName(object->value);
+    std::string source = GetSourceStringForChooserException(
+        profile, content_type, object->source);
+
     // It is safe for this structure to hold references into |objects| because
     // they are both destroyed at the end of this function.
-    all_origin_objects[make_pair(object->requesting_origin, object->source)]
+    all_origin_objects[make_pair(object->requesting_origin, source)]
                       [object->embedding_origin]
-                          .insert(make_pair(name, &object->object));
+                          .insert(make_pair(name, &object->value));
   }
 
   // Keep the exceptions sorted by provider so they will be displayed in
@@ -740,18 +775,23 @@
   }
 
   ChooserContextBase* chooser_context = chooser_type.get_context(profile);
+  ContentSettingsType content_type =
+      ContentSettingsTypeFromGroupName(std::string(chooser_type.name));
   std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       chooser_context->GetAllGrantedObjects();
   AllChooserObjects all_chooser_objects;
 
   for (const auto& object : objects) {
     if (object->incognito == incognito) {
-      std::string name = chooser_context->GetObjectName(object->object);
+      std::string name = chooser_context->GetObjectName(object->value);
       auto& chooser_exception_details =
-          all_chooser_objects[std::make_pair(name, object->object.Clone())];
+          all_chooser_objects[std::make_pair(name, object->value.Clone())];
+
+      std::string source = GetSourceStringForChooserException(
+          profile, content_type, object->source);
 
       const auto requesting_origin_source_pair =
-          std::make_pair(object->requesting_origin, object->source);
+          std::make_pair(object->requesting_origin, source);
       auto& embedding_origin_set =
           chooser_exception_details[requesting_origin_source_pair];
 
diff --git a/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chrome/browser/ui/webui/webui_webview_browsertest.cc
index 31ede0b..246bd3d 100644
--- a/chrome/browser/ui/webui/webui_webview_browsertest.cc
+++ b/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -123,7 +123,7 @@
 #if defined(OS_CHROMEOS)
     return GURL(chrome::kChromeUIOobeURL).Resolve("/login");
 #else
-    return GURL(signin::GetPromoURLForTab(
+    return GURL(signin::GetEmbeddedPromoURL(
         signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
         signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false));
 #endif
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index 941a1c4..42a9eb3a 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -130,7 +130,7 @@
   int aborts_;
 };
 
-class UnloadTestBase : public InProcessBrowserTest {
+class UnloadTest : public InProcessBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     const testing::TestInfo* const test_info =
@@ -216,24 +216,12 @@
   }
 };
 
-class UnloadTest : public UnloadTestBase,
-                   public testing::WithParamInterface<bool> {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    UnloadTestBase::SetUpCommandLine(command_line);
-    if (GetParam())
-      command_line->AppendSwitch(switches::kEnableFastUnload);
-  }
-};
-
-INSTANTIATE_TEST_CASE_P(, UnloadTest, testing::Bool());
-
 // Navigate to a page with an infinite unload handler.
 // Then two async crosssite requests to ensure
 // we don't get confused and think we're closing the tab.
 //
 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
-IN_PROC_BROWSER_TEST_P(UnloadTest, CrossSiteInfiniteUnloadAsync) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -247,7 +235,7 @@
 // Navigate to a page with an infinite unload handler.
 // Then two sync crosssite requests to ensure
 // we correctly nav to each one.
-IN_PROC_BROWSER_TEST_P(UnloadTest, CrossSiteInfiniteUnloadSync) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -263,7 +251,7 @@
 // we don't get confused and think we're closing the tab.
 // This test is flaky on the valgrind UI bots. http://crbug.com/39057 and
 // http://crbug.com/86469
-IN_PROC_BROWSER_TEST_P(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -278,7 +266,7 @@
 // Then two two sync crosssite requests to ensure
 // we correctly nav to each one.
 // Flaky on Win, Linux, and Mac; http://crbug.com/462671.
-IN_PROC_BROWSER_TEST_P(UnloadTest, DISABLED_CrossSiteInfiniteBeforeUnloadSync) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_CrossSiteInfiniteBeforeUnloadSync) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -290,19 +278,19 @@
 }
 
 // Tests closing the browser on a page with no unload listeners registered.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseNoUnloadListeners) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
   LoadUrlAndQuitBrowser(NOLISTENERS_HTML, "nolisteners");
 }
 
 // Tests closing the browser on a page with an unload listener registered.
 // Test marked as flaky in http://crbug.com/51698
-IN_PROC_BROWSER_TEST_P(UnloadTest, DISABLED_BrowserCloseUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseUnload) {
   LoadUrlAndQuitBrowser(UNLOAD_HTML, "unload");
 }
 
 // Tests closing the browser with a beforeunload handler and clicking
 // OK in the beforeunload confirm dialog.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseBeforeUnloadOK) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -317,7 +305,7 @@
 // Tests closing the browser with a beforeunload handler and clicking
 // CANCEL in the beforeunload confirm dialog.
 // If this test flakes, reopen http://crbug.com/123110
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseBeforeUnloadCancel) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
   chrome::CloseWindow(browser());
@@ -344,7 +332,7 @@
 
 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile,
 // on a page with no unload listeners registered.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListCloseNoUnloadListeners) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseNoUnloadListeners) {
   NavigateToDataURL(NOLISTENERS_HTML, "nolisteners");
 
   content::WindowedNotificationObserver window_observer(
@@ -363,7 +351,7 @@
 
 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with a
 // beforeunload handler and clicking Leave in the beforeunload confirm dialog.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListCloseBeforeUnloadOK) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseBeforeUnloadOK) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -384,7 +372,7 @@
 
 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with a
 // beforeunload handler and clicking Stay in the beforeunload confirm dialog.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListCloseBeforeUnloadCancel) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseBeforeUnloadCancel) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -420,7 +408,7 @@
 
 // Tests double calls to BrowserList::CloseAllBrowsersWithProfile, with a
 // beforeunload handler and clicking Leave in the beforeunload confirm dialog.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListDoubleCloseBeforeUnloadOK) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListDoubleCloseBeforeUnloadOK) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -446,7 +434,7 @@
 
 // Tests double calls to BrowserList::CloseAllBrowsersWithProfile, with a
 // beforeunload handler and clicking Stay in the beforeunload confirm dialog.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListDoubleCloseBeforeUnloadCancel) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListDoubleCloseBeforeUnloadCancel) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -488,7 +476,7 @@
 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with
 // a null success callback, a beforeunload handler and clicking Leave in the
 // beforeunload confirm dialog. The test succeed if no crash happens.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListCloseBeforeUnloadNullCallbackOk) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseBeforeUnloadNullCallbackOk) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
 
@@ -506,7 +494,7 @@
 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with
 // a null failure callback, a beforeunload handler and clicking Stay in the
 // beforeunload confirm dialog. The test succeed if no crash happens.
-IN_PROC_BROWSER_TEST_P(UnloadTest,
+IN_PROC_BROWSER_TEST_F(UnloadTest,
                        BrowserListCloseBeforeUnloadNullCallbackCancel) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   PrepareForDialog(browser());
@@ -539,7 +527,7 @@
 // Tests terminating the browser with a beforeunload handler.
 // Currently only ChromeOS shuts down gracefully.
 #if defined(OS_CHROMEOS)
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserTerminateBeforeUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserTerminateBeforeUnload) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
   EXPECT_EQ(kill(base::GetCurrentProcessHandle(), SIGTERM), 0);
 }
@@ -548,7 +536,7 @@
 // Tests closing the browser and clicking OK in the beforeunload confirm dialog
 // if an inner frame has the focus.
 // If this flakes, use http://crbug.com/32615 and http://crbug.com/45675
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseWithInnerFocusedFrame) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithInnerFocusedFrame) {
   NavigateToDataURL(INNER_FRAME_WITH_FOCUS_HTML, "innerframewithfocus");
   PrepareForDialog(browser());
 
@@ -562,14 +550,14 @@
 
 // Tests closing the browser with a beforeunload handler that takes forever
 // by running an infinite loop.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseInfiniteBeforeUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnload) {
   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML,
                         "infinitebeforeunload");
 }
 
 // Tests closing the browser on a page with an unload listener registered where
 // the unload handler has an infinite loop.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseInfiniteUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -580,7 +568,7 @@
 
 // Tests closing the browser with a beforeunload handler that hangs.
 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469
-IN_PROC_BROWSER_TEST_P(UnloadTest, DISABLED_BrowserCloseInfiniteBeforeUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseInfiniteBeforeUnload) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -592,7 +580,7 @@
 // Tests closing the browser on a page with an unload listener registered where
 // the unload handler has an infinite loop followed by an alert.
 // If this flakes, use http://crbug.com/86469
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSingleProcess))
@@ -604,7 +592,7 @@
 // Tests closing the browser with a beforeunload handler that hangs then
 // pops up an alert.
 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469.
-IN_PROC_BROWSER_TEST_P(UnloadTest,
+IN_PROC_BROWSER_TEST_F(UnloadTest,
                        DISABLED_BrowserCloseInfiniteBeforeUnloadAlert) {
   // Tests makes no sense in single-process mode since the renderer is hung.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -617,13 +605,13 @@
 
 // Tests closing the browser on a page with an unload listener registered where
 // the unload handler has an 2 second long loop followed by an alert.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
   LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, "twosecondunloadalert");
 }
 
 // Tests closing the browser with a beforeunload handler that takes
 // two seconds to run then pops up an alert.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
                         "twosecondbeforeunloadalert");
 }
@@ -633,7 +621,7 @@
 // handler can be closed.
 // If this flakes, see http://crbug.com/45162, http://crbug.com/45281 and
 // http://crbug.com/86769.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) {
   NavigateToDataURL(CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER, "only_one_unload");
 
   // Simulate a click to force user_gesture to true; if we don't, the resulting
@@ -658,7 +646,7 @@
   CheckTitle("only_one_unload");
 }
 
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListForceCloseNoUnloadListeners) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseNoUnloadListeners) {
   NavigateToDataURL(NOLISTENERS_HTML, "nolisteners");
 
   content::WindowedNotificationObserver window_observer(
@@ -675,7 +663,7 @@
   EXPECT_EQ(0, unload_results.get_aborts());
 }
 
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListForceCloseWithBeforeUnload) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseWithBeforeUnload) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
 
   content::WindowedNotificationObserver window_observer(
@@ -692,7 +680,7 @@
   EXPECT_EQ(0, unload_results.get_aborts());
 }
 
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserListForceCloseAfterNormalClose) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseAfterNormalClose) {
   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
 
   content::WindowedNotificationObserver window_observer(
@@ -716,7 +704,7 @@
 
 // Tests that a cross-site iframe runs its beforeunload handler when closing
 // the browser.  See https://crbug.com/853021.
-IN_PROC_BROWSER_TEST_P(UnloadTest, BrowserCloseWithCrossSiteIframe) {
+IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithCrossSiteIframe) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Navigate to a page with an iframe.
@@ -746,319 +734,5 @@
   window_observer.Wait();
 }
 
-class FastUnloadTest : public UnloadTestBase {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    UnloadTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableFastUnload);
-  }
-
-  void SetUpInProcessBrowserTestFixture() override {
-    ASSERT_TRUE(embedded_test_server()->Start());
-  }
-
-  GURL GetUrl(const std::string& name) {
-    return GURL(
-        embedded_test_server()->GetURL("/fast_tab_close/" + name + ".html"));
-  }
-
-  void NavigateToPage(const char* name) {
-    ui_test_utils::NavigateToURL(browser(), GetUrl(name));
-    CheckTitle(name);
-  }
-
-  void NavigateToPageInNewTab(const char* name) {
-    ui_test_utils::NavigateToURLWithDisposition(
-        browser(), GetUrl(name), WindowOpenDisposition::NEW_FOREGROUND_TAB,
-        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-    CheckTitle(name);
-  }
-
-  std::string GetCookies(const char* name) {
-    content::WebContents* contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    return content::GetCookies(contents->GetBrowserContext(), GetUrl(name));
-  }
-};
-
-class FastTabCloseTabStripModelObserver : public TabStripModelObserver {
- public:
-  FastTabCloseTabStripModelObserver(TabStripModel* model,
-                                    base::RunLoop* run_loop)
-      : model_(model),
-        run_loop_(run_loop) {
-    model_->AddObserver(this);
-  }
-
-  ~FastTabCloseTabStripModelObserver() override {
-    model_->RemoveObserver(this);
-  }
-
-  // TabStripModelObserver:
-  void OnTabStripModelChanged(
-      TabStripModel* tab_strip_model,
-      const TabStripModelChange& change,
-      const TabStripSelectionChange& selection) override {
-    if (change.type() != TabStripModelChange::kRemoved)
-      return;
-
-    run_loop_->Quit();
-  }
-
- private:
-  TabStripModel* const model_;
-  base::RunLoop* const run_loop_;
-};
-
-
-// Test that fast-tab-close works when closing a tab with an unload handler
-// (http://crbug.com/142458).
-// Flaky on Windows bots (http://crbug.com/267597).
-#if defined(OS_WIN)
-#define MAYBE_UnloadHidden \
-    DISABLED_UnloadHidden
-#else
-#define MAYBE_UnloadHidden \
-    UnloadHidden
-#endif
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, MAYBE_UnloadHidden) {
-  NavigateToPage("no_listeners");
-  NavigateToPageInNewTab("unload_sets_cookie");
-  EXPECT_EQ("", GetCookies("no_listeners"));
-
-  content::WebContentsDestroyedWatcher destroyed_watcher(
-      browser()->tab_strip_model()->GetActiveWebContents());
-
-  {
-    base::RunLoop run_loop;
-    FastTabCloseTabStripModelObserver observer(
-        browser()->tab_strip_model(), &run_loop);
-    chrome::CloseTab(browser());
-    run_loop.Run();
-  }
-
-  // Check that the browser only has the original tab.
-  CheckTitle("no_listeners");
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  // Wait for the actual destruction.
-  destroyed_watcher.Wait();
-
-  // Verify that the destruction had the desired effect.
-  EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
-}
-
-// Test that fast-tab-close does not break a solo tab.
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_ClosingLastTabFinishesUnload) {
-  // The unload handler sleeps before setting the cookie to catch cases when
-  // unload handlers are not allowed to run to completion. (For example,
-  // using the detached handler for the tab and then closing the browser.)
-  NavigateToPage("unload_sleep_before_cookie");
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  EXPECT_EQ("", GetCookies("unload_sleep_before_cookie"));
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  chrome::CloseTab(browser());
-  window_observer.Wait();
-}
-
-// Fails on Mac, Linux, Win7 (http://crbug.com/301173).
-// Flaky on Windows bots (http://crbug.com/267597).
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_ClosingLastTabFinishesUnload) {
-  // Check for cookie set in unload handler of PRE_ test.
-  NavigateToPage("no_listeners");
-  EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
-}
-
-// Test that fast-tab-close does not break window close.
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_WindowCloseFinishesUnload) {
-  NavigateToPage("no_listeners");
-
-  // The unload handler sleeps before setting the cookie to catch cases when
-  // unload handlers are not allowed to run to completion. Without the sleep,
-  // the cookie can get set even if the browser does not wait for
-  // the unload handler to finish.
-  NavigateToPageInNewTab("unload_sleep_before_cookie");
-  EXPECT_EQ(2, browser()->tab_strip_model()->count());
-  EXPECT_EQ("", GetCookies("no_listeners"));
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  chrome::CloseWindow(browser());
-  window_observer.Wait();
-}
-
-// Flaky on Windows bots (http://crbug.com/279267) and fails on Mac / Linux bots
-// (http://crbug.com/301173).
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseFinishesUnload) {
-  // Check for cookie set in unload during PRE_ test.
-  NavigateToPage("no_listeners");
-  EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
-}
-
-// Test that a tab crash during unload does not break window close.
-//
-// Hits assertion on Linux and Mac:
-//     [FATAL:profile_destroyer.cc(46)] Check failed:
-//         hosts.empty() ||
-//         profile->IsOffTheRecord() ||
-//         content::RenderProcessHost::run_renderer_in_process().
-//     More details: The renderer process host matches the closed, crashed tab.
-//     The |UnloadController| receives |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|
-//     and proceeds with the close.
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseAfterUnloadCrash) {
-  NavigateToPage("no_listeners");
-  NavigateToPageInNewTab("unload_sets_cookie");
-  content::WebContents* unload_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ("", GetCookies("no_listeners"));
-
-  {
-    base::RunLoop run_loop;
-    FastTabCloseTabStripModelObserver observer(
-        browser()->tab_strip_model(), &run_loop);
-    chrome::CloseTab(browser());
-    run_loop.Run();
-  }
-
-  // Check that the browser only has the original tab.
-  CheckTitle("no_listeners");
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  CrashTab(unload_contents);
-
-  // Check that the browser only has the original tab.
-  CheckTitle("no_listeners");
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  chrome::CloseWindow(browser());
-  window_observer.Wait();
-}
-
-// Times out on Windows and Linux.
-// Crashes on Mac (http://crbug/810294).
-#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
-#define MAYBE_WindowCloseAfterBeforeUnloadCrash \
-    DISABLED_WindowCloseAfterBeforeUnloadCrash
-#else
-#define MAYBE_WindowCloseAfterBeforeUnloadCrash \
-    WindowCloseAfterBeforeUnloadCrash
-#endif
-IN_PROC_BROWSER_TEST_F(FastUnloadTest,
-                       MAYBE_WindowCloseAfterBeforeUnloadCrash) {
-  // Tests makes no sense in single-process mode since the renderer is hung.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSingleProcess))
-    return;
-
-  NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
-  PrepareForDialog(browser());
-  content::WebContents* beforeunload_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  content::WindowedNotificationObserver window_observer(
-        chrome::NOTIFICATION_BROWSER_CLOSED,
-        content::NotificationService::AllSources());
-  chrome::CloseWindow(browser());
-  CrashTab(beforeunload_contents);
-  window_observer.Wait();
-}
-
-IN_PROC_BROWSER_TEST_F(FastUnloadTest,
-                       BrowserListForceCloseNoUnloadListenersWithFastUnload) {
-  NavigateToDataURL(NOLISTENERS_HTML, "nolisteners");
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  UnloadResults unload_results;
-  BrowserList::CloseAllBrowsersWithProfile(
-      browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
-      true);
-  window_observer.Wait();
-  EXPECT_EQ(1, unload_results.get_successes());
-  EXPECT_EQ(0, unload_results.get_aborts());
-}
-
-IN_PROC_BROWSER_TEST_F(FastUnloadTest,
-                       BrowserListForceCloseWithBeforeUnloadWithFastUnload) {
-  NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
-  PrepareForDialog(browser());
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  UnloadResults unload_results;
-  BrowserList::CloseAllBrowsersWithProfile(
-      browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&UnloadResults::AddAbort, base::Unretained(&unload_results)),
-      true);
-  window_observer.Wait();
-  EXPECT_EQ(1, unload_results.get_successes());
-  EXPECT_EQ(0, unload_results.get_aborts());
-}
-
-IN_PROC_BROWSER_TEST_F(FastUnloadTest,
-                       BrowserListForceCloseWithIncognitoProfileCloseProfiles) {
-  Profile* otr_profile = browser()->profile()->GetOffTheRecordProfile();
-  Browser* otr_browser1 = CreateBrowser(otr_profile);
-  Browser* otr_browser2 = CreateBrowser(otr_profile);
-  content::WindowedNotificationObserver otr_browser1_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::Source<Browser>(otr_browser1));
-  content::WindowedNotificationObserver otr_browser2_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::Source<Browser>(otr_browser2));
-  UnloadResults unload_results;
-  BrowserList::CloseAllBrowsersWithIncognitoProfile(
-      otr_profile,
-      base::BindRepeating(&UnloadResults::AddSuccess,
-                          base::Unretained(&unload_results)),
-      base::BindRepeating(&UnloadResults::AddAbort,
-                          base::Unretained(&unload_results)),
-      true);
-  // Incognito browsers should be closed.
-  otr_browser1_observer.Wait();
-  otr_browser2_observer.Wait();
-  // Browser with original profile should not close.
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  EXPECT_EQ(1, unload_results.get_successes());
-  EXPECT_EQ(0, unload_results.get_aborts());
-}
-
-IN_PROC_BROWSER_TEST_F(FastUnloadTest,
-                       BrowserListForceCloseAfterNormalCloseWithFastUnload) {
-  NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
-  PrepareForDialog(browser());
-
-  content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_CLOSED,
-      content::NotificationService::AllSources());
-  UnloadResults unload_results;
-  BrowserList::CloseAllBrowsersWithProfile(
-      browser()->profile(),
-      base::Bind(&UnloadResults::AddSuccess, base::Unretained(&unload_results)),
-      base::Bind(&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)),
-      true);
-  window_observer.Wait();
-  EXPECT_EQ(1, unload_results.get_successes());
-  EXPECT_EQ(0, unload_results.get_aborts());
-}
-
 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
 // and multiple windows.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector.cc b/chrome/browser/upgrade_detector/upgrade_detector.cc
index ec63a9e5..5bc0266 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/time/clock.h"
 #include "base/time/tick_clock.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/browser_process.h"
@@ -63,8 +64,10 @@
   return gfx::Image(gfx::CreateVectorIcon(kBrowserToolsUpdateIcon, color));
 }
 
-UpgradeDetector::UpgradeDetector(const base::TickClock* tick_clock)
-    : tick_clock_(tick_clock),
+UpgradeDetector::UpgradeDetector(const base::Clock* clock,
+                                 const base::TickClock* tick_clock)
+    : clock_(clock),
+      tick_clock_(tick_clock),
       upgrade_available_(UPGRADE_AVAILABLE_NONE),
       best_effort_experiment_updates_available_(false),
       critical_experiment_updates_available_(false),
diff --git a/chrome/browser/upgrade_detector/upgrade_detector.h b/chrome/browser/upgrade_detector/upgrade_detector.h
index e5f1ddd7..e47e5c45 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector.h
@@ -17,6 +17,7 @@
 class PrefRegistrySimple;
 class UpgradeObserver;
 namespace base {
+class Clock;
 class TickClock;
 }
 
@@ -58,9 +59,7 @@
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   // Returns the time at which an available upgrade was detected.
-  base::TimeTicks upgrade_detected_time() const {
-    return upgrade_detected_time_;
-  }
+  base::Time upgrade_detected_time() const { return upgrade_detected_time_; }
 
   // Whether the user should be notified about an upgrade.
   bool notify_upgrade() const { return notify_upgrade_; }
@@ -112,7 +111,7 @@
 
   // Returns the tick count at which "high" annoyance level will be (or was)
   // reached, or a null tick count if an upgrade has not yet been detected.
-  virtual base::TimeTicks GetHighAnnoyanceDeadline() = 0;
+  virtual base::Time GetHighAnnoyanceDeadline() = 0;
 
   void AddObserver(UpgradeObserver* observer);
 
@@ -142,13 +141,15 @@
     UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU,
   };
 
-  explicit UpgradeDetector(const base::TickClock* tick_clock);
+  UpgradeDetector(const base::Clock* clock, const base::TickClock* tick_clock);
 
   // Returns the notification period specified via the
   // RelaunchNotificationPeriod policy setting, or a zero delta if unset or out
   // of range.
   static base::TimeDelta GetRelaunchNotificationPeriod();
 
+  const base::Clock* clock() { return clock_; }
+
   const base::TickClock* tick_clock() { return tick_clock_; }
 
   // Notifies that update is recommended and triggers different actions based
@@ -180,7 +181,7 @@
     upgrade_available_ = available;
   }
 
-  void set_upgrade_detected_time(base::TimeTicks upgrade_detected_time) {
+  void set_upgrade_detected_time(base::Time upgrade_detected_time) {
     upgrade_detected_time_ = upgrade_detected_time;
   }
 
@@ -225,7 +226,10 @@
   // input events since the specified time.
   void CheckIdle();
 
-  // A provider of TimeTicks to the detector and its timers.
+  // A provider of Time to the detector.
+  const base::Clock* const clock_;
+
+  // A provider of TimeTicks to the detectors' timers.
   const base::TickClock* const tick_clock_;
 
   // Observes changes to the browser.relaunch_notification_period Local State
@@ -237,7 +241,7 @@
   UpgradeAvailable upgrade_available_;
 
   // The time at which an available upgrade was detected.
-  base::TimeTicks upgrade_detected_time_;
+  base::Time upgrade_detected_time_;
 
   // Whether "best effort" experiment updates are available.
   bool best_effort_experiment_updates_available_;
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
index 4cad501..feda627 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
@@ -12,6 +12,8 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/update_engine_client.h"
@@ -101,8 +103,9 @@
 }  // namespace
 
 UpgradeDetectorChromeos::UpgradeDetectorChromeos(
+    const base::Clock* clock,
     const base::TickClock* tick_clock)
-    : UpgradeDetector(tick_clock),
+    : UpgradeDetector(clock, tick_clock),
       high_threshold_(DetermineHighThreshold()),
       upgrade_notification_timer_(tick_clock),
       initialized_(false),
@@ -130,8 +133,8 @@
   return high_threshold_ - (high_threshold_ * kElevatedScaleFactor);
 }
 
-base::TimeTicks UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() {
-  const base::TimeTicks detected_time = upgrade_detected_time();
+base::Time UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() {
+  const base::Time detected_time = upgrade_detected_time();
   if (detected_time.is_null())
     return detected_time;
   return detected_time + high_threshold_;
@@ -152,7 +155,7 @@
 void UpgradeDetectorChromeos::UpdateStatusChanged(
     const UpdateEngineClient::Status& status) {
   if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
-    set_upgrade_detected_time(tick_clock()->NowTicks());
+    set_upgrade_detected_time(clock()->Now());
 
     if (status.is_rollback) {
       // Powerwash will be required, determine what kind of notification to show
@@ -182,7 +185,7 @@
 void UpgradeDetectorChromeos::NotifyOnUpgrade() {
   const base::TimeDelta elevated_threshold =
       high_threshold_ * kElevatedScaleFactor;
-  base::TimeDelta delta = tick_clock()->NowTicks() - upgrade_detected_time();
+  base::TimeDelta delta = clock()->Now() - upgrade_detected_time();
   // The delay from now until the next highest notification stage is reached, or
   // zero if the highest notification stage has been reached.
   base::TimeDelta next_delay;
@@ -244,7 +247,7 @@
 // static
 UpgradeDetectorChromeos* UpgradeDetectorChromeos::GetInstance() {
   static base::NoDestructor<UpgradeDetectorChromeos> instance(
-      base::DefaultTickClock::GetInstance());
+      base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance());
   return instance.get();
 }
 
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
index 47487d0..6d1931d 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
@@ -15,6 +15,7 @@
 #include "chromeos/dbus/update_engine_client.h"
 
 namespace base {
+class Clock;
 template <typename T>
 class NoDestructor;
 class TickClock;
@@ -37,12 +38,13 @@
 
   // UpgradeDetector:
   base::TimeDelta GetHighAnnoyanceLevelDelta() override;
-  base::TimeTicks GetHighAnnoyanceDeadline() override;
+  base::Time GetHighAnnoyanceDeadline() override;
 
  private:
   friend class base::NoDestructor<UpgradeDetectorChromeos>;
 
-  explicit UpgradeDetectorChromeos(const base::TickClock* tick_clock);
+  UpgradeDetectorChromeos(const base::Clock* clock,
+                          const base::TickClock* tick_clock);
 
   // Returns the threshold to reach high annoyance level.
   static base::TimeDelta DetermineHighThreshold();
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_impl.cc b/chrome/browser/upgrade_detector/upgrade_detector_impl.cc
index 67bbade..04ea6f5 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_impl.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_impl.cc
@@ -26,7 +26,10 @@
 #include "base/task_runner_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/google/google_brand.h"
@@ -139,8 +142,9 @@
 
 }  // namespace
 
-UpgradeDetectorImpl::UpgradeDetectorImpl(const base::TickClock* tick_clock)
-    : UpgradeDetector(tick_clock),
+UpgradeDetectorImpl::UpgradeDetectorImpl(const base::Clock* clock,
+                                         const base::TickClock* tick_clock)
+    : UpgradeDetector(clock, tick_clock),
       blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
@@ -302,7 +306,7 @@
     return;
 
   if (upgrade_detected_time().is_null())
-    set_upgrade_detected_time(tick_clock()->NowTicks());
+    set_upgrade_detected_time(clock()->Now());
 
   // Start the repeating timer for notifying the user after a certain period.
   upgrade_notification_timer_.Start(
@@ -561,8 +565,7 @@
 #endif  // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
 
 void UpgradeDetectorImpl::NotifyOnUpgrade() {
-  const base::TimeDelta time_passed =
-      tick_clock()->NowTicks() - upgrade_detected_time();
+  const base::TimeDelta time_passed = clock()->Now() - upgrade_detected_time();
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NotifyOnUpgradeWithTimePassed(time_passed);
 }
@@ -570,7 +573,7 @@
 // static
 UpgradeDetectorImpl* UpgradeDetectorImpl::GetInstance() {
   static base::NoDestructor<UpgradeDetectorImpl> instance(
-      base::DefaultTickClock::GetInstance());
+      base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance());
   return instance.get();
 }
 
@@ -579,9 +582,9 @@
   return stages_[kStagesIndexHigh] - stages_[kStagesIndexElevated];
 }
 
-base::TimeTicks UpgradeDetectorImpl::GetHighAnnoyanceDeadline() {
+base::Time UpgradeDetectorImpl::GetHighAnnoyanceDeadline() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const base::TimeTicks detected_time = upgrade_detected_time();
+  const base::Time detected_time = upgrade_detected_time();
   if (detected_time.is_null())
     return detected_time;
   return detected_time + stages_[kStagesIndexHigh];
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_impl.h b/chrome/browser/upgrade_detector/upgrade_detector_impl.h
index 998ec45..b39e21a 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_impl.h
+++ b/chrome/browser/upgrade_detector/upgrade_detector_impl.h
@@ -19,6 +19,7 @@
 #include "components/variations/service/variations_service.h"
 
 namespace base {
+class Clock;
 template <typename T>
 class NoDestructor;
 class SequencedTaskRunner;
@@ -42,10 +43,11 @@
 
   // UpgradeDetector:
   base::TimeDelta GetHighAnnoyanceLevelDelta() override;
-  base::TimeTicks GetHighAnnoyanceDeadline() override;
+  base::Time GetHighAnnoyanceDeadline() override;
 
  protected:
-  explicit UpgradeDetectorImpl(const base::TickClock* tick_clock);
+  UpgradeDetectorImpl(const base::Clock* clock,
+                      const base::TickClock* tick_clock);
 
   // Sends out a notification and starts a one shot timer to wait until
   // notifying the user.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_impl_unittest.cc b/chrome/browser/upgrade_detector/upgrade_detector_impl_unittest.cc
index 1834b398..5ece01c 100644
--- a/chrome/browser/upgrade_detector/upgrade_detector_impl_unittest.cc
+++ b/chrome/browser/upgrade_detector/upgrade_detector_impl_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/time/clock.h"
 #include "base/time/tick_clock.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -32,17 +33,18 @@
 
 class TestUpgradeDetectorImpl : public UpgradeDetectorImpl {
  public:
-  explicit TestUpgradeDetectorImpl(const base::TickClock* tick_clock)
-      : UpgradeDetectorImpl(tick_clock) {}
+  explicit TestUpgradeDetectorImpl(const base::Clock* clock,
+                                   const base::TickClock* tick_clock)
+      : UpgradeDetectorImpl(clock, tick_clock) {}
   ~TestUpgradeDetectorImpl() override = default;
 
   // Exposed for testing.
+  using UpgradeDetectorImpl::clock;
+  using UpgradeDetectorImpl::GetThresholdForLevel;
+  using UpgradeDetectorImpl::NotifyOnUpgradeWithTimePassed;
+  using UpgradeDetectorImpl::OnExperimentChangesDetected;
   using UpgradeDetectorImpl::UPGRADE_AVAILABLE_REGULAR;
   using UpgradeDetectorImpl::UpgradeDetected;
-  using UpgradeDetectorImpl::OnExperimentChangesDetected;
-  using UpgradeDetectorImpl::NotifyOnUpgradeWithTimePassed;
-  using UpgradeDetectorImpl::GetThresholdForLevel;
-  using UpgradeDetectorImpl::tick_clock;
 
   // UpgradeDetector:
   void TriggerCriticalUpdate() override {
@@ -120,6 +122,10 @@
                                            std::make_unique<base::Value>(true));
   }
 
+  const base::Clock* GetMockClock() {
+    return scoped_task_environment_.GetMockClock();
+  }
+
   const base::TickClock* GetMockTickClock() {
     return scoped_task_environment_.GetMockTickClock();
   }
@@ -153,7 +159,7 @@
 };
 
 TEST_F(UpgradeDetectorImplTest, VariationsChanges) {
-  TestUpgradeDetectorImpl detector(GetMockTickClock());
+  TestUpgradeDetectorImpl detector(GetMockClock(), GetMockTickClock());
   TestUpgradeNotificationListener notifications_listener(&detector);
   EXPECT_FALSE(detector.notify_upgrade());
   EXPECT_EQ(0, notifications_listener.notification_count());
@@ -174,7 +180,7 @@
 }
 
 TEST_F(UpgradeDetectorImplTest, VariationsCriticalChanges) {
-  TestUpgradeDetectorImpl detector(GetMockTickClock());
+  TestUpgradeDetectorImpl detector(GetMockClock(), GetMockTickClock());
   TestUpgradeNotificationListener notifications_listener(&detector);
   EXPECT_FALSE(detector.notify_upgrade());
   EXPECT_EQ(0, notifications_listener.notification_count());
@@ -206,7 +212,7 @@
   // meaning in the detector.
   FastForwardBy(base::TimeDelta::FromHours(1));
 
-  TestUpgradeDetectorImpl upgrade_detector(GetMockTickClock());
+  TestUpgradeDetectorImpl upgrade_detector(GetMockClock(), GetMockTickClock());
   ::testing::StrictMock<MockUpgradeObserver> mock_observer(&upgrade_detector);
 
   // Changing the period when no upgrade has been detected updates the
@@ -367,7 +373,7 @@
 
 // Appends the time and stage from detector to |notifications|.
 ACTION_P2(AppendTicksAndStage, detector, notifications) {
-  notifications->emplace_back(detector->tick_clock()->NowTicks(),
+  notifications->emplace_back(detector->clock()->Now(),
                               detector->upgrade_notification_stage());
 }
 
@@ -395,8 +401,7 @@
 // Tests that the notification timer is handled as desired.
 TEST_P(UpgradeDetectorImplTimerTest, TestNotificationTimer) {
   using TimeAndStage =
-      std::pair<base::TimeTicks,
-                UpgradeDetector::UpgradeNotificationAnnoyanceLevel>;
+      std::pair<base::Time, UpgradeDetector::UpgradeNotificationAnnoyanceLevel>;
   using Notifications = std::vector<TimeAndStage>;
   static constexpr base::TimeDelta kTwentyMinues =
       base::TimeDelta::FromMinutes(20);
@@ -405,7 +410,7 @@
   // meaning in the detector.
   FastForwardBy(base::TimeDelta::FromHours(1));
 
-  TestUpgradeDetectorImpl detector(GetMockTickClock());
+  TestUpgradeDetectorImpl detector(GetMockClock(), GetMockTickClock());
   ::testing::StrictMock<MockUpgradeObserver> mock_observer(&detector);
 
   // Cache the thresholds for the detector's annoyance levels.
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index 8ef9878..2e7fe42 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/usb/usb_blocklist.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "content/public/common/service_manager_connection.h"
 #include "device/usb/public/mojom/device.mojom.h"
 #include "device/usb/usb_ids.h"
@@ -30,8 +31,6 @@
 constexpr char kProductIdKey[] = "product-id";
 constexpr char kSerialNumberKey[] = "serial-number";
 constexpr char kVendorIdKey[] = "vendor-id";
-constexpr char kSourcePolicy[] = "policy";
-constexpr char kSourcePreference[] = "preference";
 
 // Reasons a permission may be closed. These are used in histograms so do not
 // remove/reorder entries. Only add at the end just before
@@ -202,10 +201,10 @@
   OnDeviceManagerConnectionError();
 }
 
-std::vector<std::unique_ptr<base::DictionaryValue>>
+std::vector<std::unique_ptr<ChooserContextBase::Object>>
 UsbChooserContext::GetGrantedObjects(const GURL& requesting_origin,
                                      const GURL& embedding_origin) {
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       ChooserContextBase::GetGrantedObjects(requesting_origin,
                                             embedding_origin);
 
@@ -221,7 +220,11 @@
         // which always returns after the device list initialization in this
         // class.
         DCHECK(base::ContainsKey(devices_, guid));
-        objects.push_back(DeviceInfoToDictValue(*devices_[guid]));
+        objects.push_back(std::make_unique<ChooserContextBase::Object>(
+            requesting_origin, embedding_origin,
+            DeviceInfoToDictValue(*devices_[guid]).get(),
+            content_settings::SettingSource::SETTING_SOURCE_USER,
+            is_incognito_));
       }
     }
   }
@@ -246,8 +249,8 @@
       // ChooserContextBase::Object constructor will swap the object.
       auto object = DeviceInfoToDictValue(*devices_[guid]);
       objects.push_back(std::make_unique<ChooserContextBase::Object>(
-          requesting_origin, embedding_origin, object.get(), kSourcePreference,
-          is_incognito_));
+          requesting_origin, embedding_origin, object.get(),
+          content_settings::SETTING_SOURCE_USER, is_incognito_));
     }
   }
 
@@ -257,11 +260,11 @@
   std::map<std::pair<int, int>, base::Value> device_ids_to_object_map;
   for (auto it = objects.begin(); it != objects.end();) {
     const Object& object = **it;
-    auto device_ids = GetDeviceIds(object.object);
+    auto device_ids = GetDeviceIds(object.value);
     const GURL& requesting_origin = object.requesting_origin;
     const GURL& embedding_origin = object.embedding_origin;
 
-    device_ids_to_object_map[device_ids] = object.object.Clone();
+    device_ids_to_object_map[device_ids] = object.value.Clone();
 
     if (usb_policy_allowed_devices_->IsDeviceAllowed(
             requesting_origin, embedding_origin, device_ids)) {
@@ -288,7 +291,8 @@
       }
 
       objects.push_back(std::make_unique<ChooserContextBase::Object>(
-          url_pair.first, url_pair.second, object.get(), kSourcePolicy,
+          url_pair.first, url_pair.second, object.get(),
+          content_settings::SettingSource::SETTING_SOURCE_POLICY,
           is_incognito_));
     }
   }
@@ -356,18 +360,18 @@
     return true;
   }
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> device_list =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> object_list =
       GetGrantedObjects(requesting_origin, embedding_origin);
-  for (const std::unique_ptr<base::DictionaryValue>& device_dict :
-       device_list) {
+  for (const auto& object : object_list) {
     int vendor_id;
     int product_id;
     base::string16 serial_number;
-    if (device_dict->GetInteger(kVendorIdKey, &vendor_id) &&
+    const base::DictionaryValue& device_dict = object->value;
+    if (device_dict.GetInteger(kVendorIdKey, &vendor_id) &&
         device_info.vendor_id == vendor_id &&
-        device_dict->GetInteger(kProductIdKey, &product_id) &&
+        device_dict.GetInteger(kProductIdKey, &product_id) &&
         device_info.product_id == product_id &&
-        device_dict->GetString(kSerialNumberKey, &serial_number) &&
+        device_dict.GetString(kSerialNumberKey, &serial_number) &&
         device_info.serial_number == serial_number) {
       return true;
     }
diff --git a/chrome/browser/usb/usb_chooser_context.h b/chrome/browser/usb/usb_chooser_context.h
index 8f55fb8b..2359f42b 100644
--- a/chrome/browser/usb/usb_chooser_context.h
+++ b/chrome/browser/usb/usb_chooser_context.h
@@ -41,7 +41,7 @@
 
   // These methods from ChooserContextBase are overridden in order to expose
   // ephemeral devices through the public interface.
-  std::vector<std::unique_ptr<base::DictionaryValue>> GetGrantedObjects(
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> GetGrantedObjects(
       const GURL& requesting_origin,
       const GURL& embedding_origin) override;
   std::vector<std::unique_ptr<ChooserContextBase::Object>>
diff --git a/chrome/browser/usb/usb_chooser_context_unittest.cc b/chrome/browser/usb/usb_chooser_context_unittest.cc
index 62f9a56..7f9aa2b 100644
--- a/chrome/browser/usb/usb_chooser_context_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_context_unittest.cc
@@ -78,19 +78,19 @@
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   store->GrantDevicePermission(origin, origin, *device_info);
   EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info));
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       store->GetGrantedObjects(origin, origin);
   ASSERT_EQ(1u, objects.size());
-  EXPECT_TRUE(object_dict.Equals(objects[0].get()));
+  EXPECT_EQ(object_dict, objects[0]->value);
   std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects =
       store->GetAllGrantedObjects();
   ASSERT_EQ(1u, all_origin_objects.size());
   EXPECT_EQ(origin, all_origin_objects[0]->requesting_origin);
   EXPECT_EQ(origin, all_origin_objects[0]->embedding_origin);
-  EXPECT_TRUE(object_dict.Equals(&all_origin_objects[0]->object));
+  EXPECT_EQ(object_dict, all_origin_objects[0]->value);
   EXPECT_FALSE(all_origin_objects[0]->incognito);
 
-  store->RevokeObjectPermission(origin, origin, *objects[0]);
+  store->RevokeObjectPermission(origin, origin, objects[0]->value);
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   objects = store->GetGrantedObjects(origin, origin);
   EXPECT_EQ(0u, objects.size());
@@ -117,19 +117,19 @@
   store->GrantDevicePermission(origin, origin, *device_info);
   EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info));
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *other_device_info));
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       store->GetGrantedObjects(origin, origin);
   EXPECT_EQ(1u, objects.size());
-  EXPECT_TRUE(object_dict.Equals(objects[0].get()));
+  EXPECT_EQ(object_dict, objects[0]->value);
   std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects =
       store->GetAllGrantedObjects();
   EXPECT_EQ(1u, all_origin_objects.size());
   EXPECT_EQ(origin, all_origin_objects[0]->requesting_origin);
   EXPECT_EQ(origin, all_origin_objects[0]->embedding_origin);
-  EXPECT_TRUE(object_dict.Equals(&all_origin_objects[0]->object));
+  EXPECT_EQ(object_dict, all_origin_objects[0]->value);
   EXPECT_FALSE(all_origin_objects[0]->incognito);
 
-  store->RevokeObjectPermission(origin, origin, *objects[0]);
+  store->RevokeObjectPermission(origin, origin, objects[0]->value);
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   objects = store->GetGrantedObjects(origin, origin);
   EXPECT_EQ(0u, objects.size());
@@ -147,7 +147,7 @@
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   store->GrantDevicePermission(origin, origin, *device_info);
   EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info));
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       store->GetGrantedObjects(origin, origin);
   EXPECT_EQ(1u, objects.size());
   std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects =
@@ -184,7 +184,7 @@
   EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info));
   store->GrantDevicePermission(origin, origin, *device_info);
   EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info));
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       store->GetGrantedObjects(origin, origin);
   EXPECT_EQ(1u, objects.size());
   std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects =
@@ -235,7 +235,7 @@
       incognito_store->HasDevicePermission(origin, origin, *device_info_2));
 
   {
-    std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+    std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
         store->GetGrantedObjects(origin, origin);
     EXPECT_EQ(1u, objects.size());
     std::vector<std::unique_ptr<ChooserContextBase::Object>>
@@ -244,7 +244,7 @@
     EXPECT_FALSE(all_origin_objects[0]->incognito);
   }
   {
-    std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+    std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
         incognito_store->GetGrantedObjects(origin, origin);
     EXPECT_EQ(1u, objects.size());
     std::vector<std::unique_ptr<ChooserContextBase::Object>>
@@ -273,7 +273,7 @@
   store->GrantDevicePermission(kBarOrigin, kBarOrigin, *device_info);
   store->GrantDevicePermission(kBarOrigin, kBarOrigin, *ephemeral_device_info);
 
-  std::vector<std::unique_ptr<base::DictionaryValue>> objects =
+  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
       store->GetGrantedObjects(kFooOrigin, kFooOrigin);
   EXPECT_EQ(0u, objects.size());
 
@@ -478,10 +478,6 @@
 
 namespace {
 
-// Permission sources
-constexpr char kPolicySource[] = "policy";
-constexpr char kPreferenceSource[] = "preference";
-
 // Test URLs
 const GURL kAnyDeviceOrigin("https://anydevice.com");
 const GURL kVendorOrigin("https://vendor.com");
@@ -512,7 +508,7 @@
 void ExpectChooserObjectInfo(const ChooserContextBase::Object* actual,
                              const GURL& requesting_origin,
                              const GURL& embedding_origin,
-                             const std::string& source,
+                             content_settings::SettingSource source,
                              bool incognito,
                              int vendor_id,
                              int product_id,
@@ -522,12 +518,12 @@
   EXPECT_EQ(actual->embedding_origin, embedding_origin);
   EXPECT_EQ(actual->source, source);
   EXPECT_EQ(actual->incognito, incognito);
-  ExpectDeviceObjectInfo(actual->object, vendor_id, product_id, name);
+  ExpectDeviceObjectInfo(actual->value, vendor_id, product_id, name);
 }
 
 void ExpectChooserObjectInfo(const ChooserContextBase::Object* actual,
                              const GURL& requesting_origin,
-                             const std::string& source,
+                             content_settings::SettingSource source,
                              bool incognito,
                              int vendor_id,
                              int product_id,
@@ -552,21 +548,21 @@
   // Wildcard IDs are represented by a value of -1, so they appear first.
   ExpectChooserObjectInfo(objects[0].get(),
                           /*requesting_origin=*/kAnyDeviceOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device [ffffffff:ffffffff]");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
@@ -574,7 +570,7 @@
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
@@ -602,7 +598,7 @@
   ASSERT_EQ(objects.size(), 6ul);
 
   for (const auto& object : objects) {
-    EXPECT_TRUE(store->IsValidObject(object->object));
+    EXPECT_TRUE(store->IsValidObject(object->value));
   }
 
   // The user granted permissions appear before the policy granted permissions.
@@ -611,7 +607,7 @@
   ExpectChooserObjectInfo(objects[0].get(),
                           /*requesting_origin=*/kGoogleOrigin,
                           /*embedding_origin=*/kGoogleOrigin,
-                          /*source=*/kPreferenceSource,
+                          /*source=*/content_settings::SETTING_SOURCE_USER,
                           /*incognito=*/false,
                           /*vendor_id=*/1000,
                           /*product_id=*/1,
@@ -619,28 +615,28 @@
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kGoogleOrigin,
                           /*embedding_origin=*/kGoogleOrigin,
-                          /*source=*/kPreferenceSource,
+                          /*source=*/content_settings::SETTING_SOURCE_USER,
                           /*incognito=*/false,
                           /*vendor_id=*/1000,
                           /*product_id=*/2,
                           /*name=*/"Gadget");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kAnyDeviceOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device [ffffffff:ffffffff]");
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device from Google Inc.");
   ExpectChooserObjectInfo(objects[4].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
@@ -648,7 +644,7 @@
   ExpectChooserObjectInfo(objects[5].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
@@ -672,26 +668,26 @@
   ASSERT_EQ(objects.size(), 4ul);
 
   for (const auto& object : objects) {
-    EXPECT_TRUE(store->IsValidObject(object->object));
+    EXPECT_TRUE(store->IsValidObject(object->value));
   }
 
   ExpectChooserObjectInfo(objects[0].get(),
                           /*requesting_origin=*/kAnyDeviceOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device [ffffffff:ffffffff]");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
@@ -699,13 +695,13 @@
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
                           /*name=*/"Unknown device [18d2:054d]");
   ASSERT_TRUE(persistent_device_info->product_name);
-  EXPECT_EQ(base::UTF8ToUTF16(store->GetObjectName(objects[2]->object)),
+  EXPECT_EQ(base::UTF8ToUTF16(store->GetObjectName(objects[2]->value)),
             persistent_device_info->product_name.value());
 }
 
@@ -726,26 +722,26 @@
   ASSERT_EQ(objects.size(), 4ul);
 
   for (const auto& object : objects) {
-    EXPECT_TRUE(store->IsValidObject(object->object));
+    EXPECT_TRUE(store->IsValidObject(object->value));
   }
 
   ExpectChooserObjectInfo(objects[0].get(),
                           /*requesting_origin=*/kAnyDeviceOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device [ffffffff:ffffffff]");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
@@ -753,7 +749,7 @@
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
@@ -777,26 +773,26 @@
   ASSERT_EQ(objects.size(), 4ul);
 
   for (const auto& object : objects) {
-    EXPECT_TRUE(store->IsValidObject(object->object));
+    EXPECT_TRUE(store->IsValidObject(object->value));
   }
 
   ExpectChooserObjectInfo(objects[0].get(),
                           /*requesting_origin=*/kAnyDeviceOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device [ffffffff:ffffffff]");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
                           /*name=*/"Unknown device from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
@@ -804,7 +800,7 @@
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
-                          /*source=*/kPolicySource,
+                          /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
diff --git a/chrome/browser/usb/web_usb_service_impl_unittest.cc b/chrome/browser/usb/web_usb_service_impl_unittest.cc
index 9333dfb..c4782739 100644
--- a/chrome/browser/usb/web_usb_service_impl_unittest.cc
+++ b/chrome/browser/usb/web_usb_service_impl_unittest.cc
@@ -317,7 +317,7 @@
       base::BindLambdaForTesting([&]() { device_ptr.reset(); }));
 
   auto objects = context->GetGrantedObjects(origin, origin);
-  context->RevokeObjectPermission(origin, origin, *objects[0]);
+  context->RevokeObjectPermission(origin, origin, objects[0]->value);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(device_ptr);
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 509539d..069ae578 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -660,8 +660,12 @@
   # easily skip tests at runtime).
   test("xr_browser_tests") {
     sources = [
+      "test/browser_test_browser_renderer_browser_interface.cc",
+      "test/browser_test_browser_renderer_browser_interface.h",
       "test/mock_openvr_device_hook_base.cc",
       "test/mock_openvr_device_hook_base.h",
+      "test/ui_utils.cc",
+      "test/ui_utils.h",
       "test/webvr_browser_test.cc",
       "test/webvr_browser_test.h",
       "test/webxr_browser_test.cc",
@@ -672,6 +676,7 @@
       "test/xr_browser_test.h",
       "webxr_vr_frame_pose_browser_test.cc",
       "webxr_vr_input_browser_test.cc",
+      "webxr_vr_permission_request_browser_test.cc",
       "webxr_vr_pixel_browser_test.cc",
       "webxr_vr_tab_browser_test.cc",
       "webxr_vr_transition_browser_test.cc",
@@ -680,6 +685,7 @@
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
     deps = [
+      ":vr_common",
       "//base",
       "//chrome/test:browser_tests_runner",
       "//chrome/test:test_support",
diff --git a/chrome/browser/vr/browser_renderer.cc b/chrome/browser/vr/browser_renderer.cc
index 721d35d..2494926 100644
--- a/chrome/browser/vr/browser_renderer.cc
+++ b/chrome/browser/vr/browser_renderer.cc
@@ -247,6 +247,11 @@
   ui_->AcceptDoffPromptForTesting();
 }
 
+void BrowserRenderer::SetBrowserRendererBrowserInterfaceForTesting(
+    BrowserRendererBrowserInterface* interface) {
+  browser_ = interface;
+}
+
 void BrowserRenderer::UpdateUi(const RenderInfo& render_info,
                                base::TimeTicks current_time,
                                FrameType frame_type) {
diff --git a/chrome/browser/vr/browser_renderer.h b/chrome/browser/vr/browser_renderer.h
index f1ec6a5ea..d3b6de7 100644
--- a/chrome/browser/vr/browser_renderer.h
+++ b/chrome/browser/vr/browser_renderer.h
@@ -77,6 +77,8 @@
   void WatchElementForVisibilityStatusForTesting(
       VisibilityChangeExpectation visibility_expectation);
   void AcceptDoffPromptForTesting();
+  void SetBrowserRendererBrowserInterfaceForTesting(
+      BrowserRendererBrowserInterface* interface);
   void ConnectPresentingService(
       device::mojom::VRDisplayInfoPtr display_info,
       device::mojom::XRRuntimeSessionOptionsPtr options);
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc
index c565b56..cdc4cb6 100644
--- a/chrome/browser/vr/elements/ui_element_name.cc
+++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -147,6 +147,7 @@
     "kControllerBatteryDot4",
     "kContentRepositionHitPlane",
     "kContentRepositionVisibilityToggle",
+    "kWebXrExternalPromptNotification",
 };
 
 static_assert(
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h
index 93b560f..42feb49 100644
--- a/chrome/browser/vr/elements/ui_element_name.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -148,6 +148,7 @@
   kControllerBatteryDot4,
   kContentRepositionHitPlane,
   kContentRepositionVisibilityToggle,
+  kWebXrExternalPromptNotification,
 
   // This must be last.
   kNumUiElementNames,
diff --git a/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.cc b/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.cc
new file mode 100644
index 0000000..6fc4023
--- /dev/null
+++ b/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.h"
+#include "base/bind.h"
+#include "chrome/browser/vr/browser_renderer.h"
+#include "chrome/browser/vr/test/ui_utils.h"
+
+namespace vr {
+
+BrowserTestBrowserRendererBrowserInterface::
+    BrowserTestBrowserRendererBrowserInterface(UiUtils* utils)
+    : utils_(utils) {}
+
+BrowserTestBrowserRendererBrowserInterface::
+    ~BrowserTestBrowserRendererBrowserInterface() = default;
+
+void BrowserTestBrowserRendererBrowserInterface::ForceExitVr() {}
+
+void BrowserTestBrowserRendererBrowserInterface::
+    ReportUiOperationResultForTesting(const UiTestOperationType& action_type,
+                                      const UiTestOperationResult& result) {
+  utils_->ReportUiOperationResult(action_type, result);
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.h b/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.h
new file mode 100644
index 0000000..912f2816
--- /dev/null
+++ b/chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VR_TEST_BROWSER_TEST_BROWSER_RENDERER_BROWSER_INTERFACE_H_
+#define CHROME_BROWSER_VR_TEST_BROWSER_TEST_BROWSER_RENDERER_BROWSER_INTERFACE_H_
+
+#include "base/single_thread_task_runner.h"
+#include "chrome/browser/vr/browser_renderer_browser_interface.h"
+#include "chrome/browser/vr/ui_test_input.h"
+
+namespace vr {
+
+class UiUtils;
+
+class BrowserTestBrowserRendererBrowserInterface
+    : public BrowserRendererBrowserInterface {
+ public:
+  explicit BrowserTestBrowserRendererBrowserInterface(UiUtils* utils);
+  ~BrowserTestBrowserRendererBrowserInterface() override;
+
+  // BrowserRendererBrowserInterface
+  void ForceExitVr() override;
+  void ReportUiOperationResultForTesting(
+      const UiTestOperationType& action_type,
+      const UiTestOperationResult& result) override;
+
+ private:
+  // Should not have to worry about the lifetime of this, as this should be a
+  // reference to the UiUtils that created this
+  // BrowserTestBrowserRendererBrowserInterface, and the interface should always
+  // be destroyed before the utils.
+  UiUtils* utils_;
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_TEST_BROWSER_TEST_BROWSER_RENDERER_BROWSER_INTERFACE_H_
diff --git a/chrome/browser/vr/test/ui_utils.cc b/chrome/browser/vr/test/ui_utils.cc
new file mode 100644
index 0000000..d434a059
--- /dev/null
+++ b/chrome/browser/vr/test/ui_utils.cc
@@ -0,0 +1,133 @@
+// 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 "base/run_loop.h"
+#include "base/threading/platform_thread.h"
+#ifdef OS_WIN
+#include "chrome/browser/vr/win/vr_browser_renderer_thread_win.h"
+#endif  // OS_WIN
+#include "chrome/browser/vr/test/browser_test_browser_renderer_browser_interface.h"
+#include "chrome/browser/vr/test/ui_utils.h"
+#include "chrome/browser/vr/test/xr_browser_test.h"
+
+namespace vr {
+
+UiUtils::UiUtils()
+    : ui_operation_results_(std::vector<UiTestOperationResult>(
+          static_cast<int>(UiTestOperationType::kNumUiTestOperationTypes))),
+      ui_operation_callbacks_(std::vector<base::OnceCallback<void()>>(
+          static_cast<int>(UiTestOperationType::kNumUiTestOperationTypes))),
+      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+  auto* renderer = GetBrowserRenderer();
+  DCHECK(renderer) << "Failed to get a BrowserRenderer. Consider using "
+                   << "UiUtils::Create() instead.";
+
+  interface_ =
+      std::make_unique<BrowserTestBrowserRendererBrowserInterface>(this);
+  renderer->SetBrowserRendererBrowserInterfaceForTesting(interface_.get());
+}
+
+UiUtils::~UiUtils() {
+  auto* renderer = GetBrowserRenderer();
+  if (renderer != nullptr) {
+    renderer->SetBrowserRendererBrowserInterfaceForTesting(nullptr);
+  }
+}
+
+std::unique_ptr<UiUtils> UiUtils::Create() {
+  base::RunLoop wait_loop(base::RunLoop::Type::kNestableTasksAllowed);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&UiUtils::PollForBrowserRenderer, &wait_loop));
+  wait_loop.Run();
+
+  return std::make_unique<UiUtils>();
+}
+
+void UiUtils::PollForBrowserRenderer(base::RunLoop* wait_loop) {
+  if (GetBrowserRenderer() == nullptr) {
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, base::BindOnce(&UiUtils::PollForBrowserRenderer, wait_loop),
+        XrBrowserTestBase::kPollCheckIntervalShort);
+    return;
+  }
+  wait_loop->Quit();
+}
+
+void UiUtils::PerformActionAndWaitForVisibilityStatus(
+    const UserFriendlyElementName& element_name,
+    const bool& visible,
+    base::OnceCallback<void()> action) {
+  ui_operation_results_[static_cast<int>(
+      UiTestOperationType::kElementVisibilityStatus)] =
+      UiTestOperationResult::kUnreported;
+  base::RunLoop wait_loop(base::RunLoop::Type::kNestableTasksAllowed);
+  ui_operation_callbacks_[static_cast<int>(
+      UiTestOperationType::kElementVisibilityStatus)] =
+      base::BindOnce([](base::RunLoop* loop) { loop->Quit(); }, &wait_loop);
+
+  VisibilityChangeExpectation visibility_expectation;
+  visibility_expectation.element_name = element_name;
+  visibility_expectation.timeout_ms = kDefaultUiQuiescenceTimeout;
+  visibility_expectation.visibility = visible;
+
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &BrowserRenderer::WatchElementForVisibilityStatusForTesting,
+          base::Unretained(GetBrowserRenderer()), visibility_expectation));
+
+  wait_loop.Run();
+
+  auto result = ui_operation_results_[static_cast<int>(
+      UiTestOperationType::kElementVisibilityStatus)];
+  CHECK(result == UiTestOperationResult::kVisibilityMatch)
+      << "UI reported non-visibility-matched result '"
+      << UiTestOperationResultToString(result) << "'";
+}
+
+void UiUtils::ReportUiOperationResult(const UiTestOperationType& action_type,
+                                      const UiTestOperationResult& result) {
+  ui_operation_results_[static_cast<int>(action_type)] = result;
+  std::move(ui_operation_callbacks_[static_cast<int>(action_type)]).Run();
+}
+
+std::string UiUtils::UiTestOperationResultToString(
+    UiTestOperationResult& result) {
+  switch (result) {
+    case UiTestOperationResult::kUnreported:
+      return "Unreported";
+    case UiTestOperationResult::kQuiescent:
+      return "Quiescent";
+    case UiTestOperationResult::kTimeoutNoStart:
+      return "Timeout (UI activity not started)";
+    case UiTestOperationResult::kTimeoutNoEnd:
+      return "Timeout (UI activity not stopped)";
+    case UiTestOperationResult::kVisibilityMatch:
+      return "Visibility match";
+    case UiTestOperationResult::kTimeoutNoVisibilityMatch:
+      return "Timeout (Element visibility did not match)";
+  }
+}
+
+VRBrowserRendererThreadWin* UiUtils::GetRendererThread() {
+#ifdef OS_WIN
+  return VRBrowserRendererThreadWin::GetInstanceForTesting();
+#else
+  NOTREACHED();
+#endif  // OS_WIN
+}
+
+BrowserRenderer* UiUtils::GetBrowserRenderer() {
+#ifdef OS_WIN
+  auto* renderer_thread = GetRendererThread();
+  if (renderer_thread == nullptr)
+    return nullptr;
+  return static_cast<VRBrowserRendererThreadWin*>(renderer_thread)
+      ->GetBrowserRendererForTesting();
+#else
+  NOTREACHED();
+#endif  // OS_WIN
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/test/ui_utils.h b/chrome/browser/vr/test/ui_utils.h
new file mode 100644
index 0000000..be1f1a4
--- /dev/null
+++ b/chrome/browser/vr/test/ui_utils.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VR_TEST_UI_UTILS_H_
+#define CHROME_BROWSER_VR_TEST_UI_UTILS_H_
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "chrome/browser/vr/ui_test_input.h"
+
+namespace vr {
+
+class BrowserRenderer;
+class BrowserTestBrowserRendererBrowserInterface;
+class VRBrowserRendererThreadWin;
+
+// Port of the equivalent NativeUiUtils.java for instrumentation tests. Contains
+// utility functions for interacting with the native VR UI, e.g. notifications
+// shown in the headset.
+class UiUtils {
+ public:
+  static constexpr int kDefaultUiQuiescenceTimeout = 2000;
+
+  // Ensures that the renderer thread and BrowserRenderer instance exist before
+  // creating a UiUtils.
+  // TODO(https://crbug.com/920697): Remove this once the BrowserRenderer's
+  // lifetime is tied to the renderer thread and we can assume that they both
+  // exist if we're in VR.
+  static std::unique_ptr<UiUtils> Create();
+
+  UiUtils();
+  ~UiUtils();
+
+  // Runs |action| and waits until the native UI reports that |element_name|'s
+  // visibility matches |visible|. Fails if the visibility is not matched in
+  // an allotted amount of time.
+  void PerformActionAndWaitForVisibilityStatus(
+      const UserFriendlyElementName& element_name,
+      const bool& visible,
+      base::OnceCallback<void()> action);
+
+  // Not meant to be called directly by a test.
+  void ReportUiOperationResult(const UiTestOperationType& action_type,
+                               const UiTestOperationResult& result);
+
+ private:
+  static void PollForBrowserRenderer(base::RunLoop* wait_loop);
+  static VRBrowserRendererThreadWin* GetRendererThread();
+  static BrowserRenderer* GetBrowserRenderer();
+
+  std::string UiTestOperationResultToString(UiTestOperationResult& result);
+
+  std::unique_ptr<BrowserTestBrowserRendererBrowserInterface> interface_;
+  std::vector<UiTestOperationResult> ui_operation_results_;
+  std::vector<base::OnceCallback<void()>> ui_operation_callbacks_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(UiUtils);
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_TEST_UI_UTILS_H_
diff --git a/chrome/browser/vr/test/xr_browser_test.cc b/chrome/browser/vr/test/xr_browser_test.cc
index a9faaf9..0f04cb1 100644
--- a/chrome/browser/vr/test/xr_browser_test.cc
+++ b/chrome/browser/vr/test/xr_browser_test.cc
@@ -39,11 +39,28 @@
 constexpr char XrBrowserTestBase::kVrConfigPathVal[];
 constexpr char XrBrowserTestBase::kVrLogPathEnvVar[];
 constexpr char XrBrowserTestBase::kVrLogPathVal[];
+constexpr char XrBrowserTestBase::kTestFileDir[];
 
 XrBrowserTestBase::XrBrowserTestBase() : env_(base::Environment::Create()) {}
 
 XrBrowserTestBase::~XrBrowserTestBase() = default;
 
+base::FilePath::StringType UTF8ToWideIfNecessary(std::string input) {
+#ifdef OS_WIN
+  return base::UTF8ToWide(input);
+#else
+  return input;
+#endif  // OS_WIN
+}
+
+std::string WideToUTF8IfNecessary(base::FilePath::StringType input) {
+#ifdef OS_WIN
+  return base::WideToUTF8(input);
+#else
+  return input;
+#endif  // OS_Win
+}
+
 // Returns an std::string consisting of the given path relative to the test
 // executable's path, e.g. if the executable is in out/Debug and the given path
 // is "test", the returned string should be out/Debug/test.
@@ -55,16 +72,10 @@
   // We need an std::string that is an absolute file path, which requires
   // platform-specific logic since Windows uses std::wstring instead of
   // std::string for FilePaths, but SetVar only accepts std::string.
-#ifdef OS_WIN
-  return base::WideToUTF8(
+  return WideToUTF8IfNecessary(
       base::MakeAbsoluteFilePath(
-          executable_path.Append(base::FilePath(base::UTF8ToWide(path))))
+          executable_path.Append(base::FilePath(UTF8ToWideIfNecessary(path))))
           .value());
-#else
-  return base::MakeAbsoluteFilePath(
-             executable_path.Append(base::FilePath(path)))
-      .value();
-#endif
 }
 
 void XrBrowserTestBase::SetUp() {
@@ -89,15 +100,30 @@
   InProcessBrowserTest::SetUp();
 }
 
-GURL XrBrowserTestBase::GetHtmlTestFile(const std::string& test_name) {
+GURL XrBrowserTestBase::GetFileUrlForHtmlTestFile(
+    const std::string& test_name) {
   return ui_test_utils::GetTestUrl(
       base::FilePath(FILE_PATH_LITERAL("xr/e2e_test_files/html")),
-#ifdef OS_WIN
-      base::FilePath(base::UTF8ToWide(test_name + ".html"))
-#else
-      base::FilePath(test_name + ".html")
-#endif
-          );
+      base::FilePath(UTF8ToWideIfNecessary(test_name + ".html")));
+}
+
+GURL XrBrowserTestBase::GetEmbeddedServerUrlForHtmlTestFile(
+    const std::string& test_name) {
+  // GetURL requires that the path start with /.
+  return GetEmbeddedServer()->GetURL(std::string("/") + kTestFileDir +
+                                     test_name + ".html");
+}
+
+net::EmbeddedTestServer* XrBrowserTestBase::GetEmbeddedServer() {
+  if (server_ == nullptr) {
+    server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::Type::TYPE_HTTPS);
+    // We need to serve from the root in order for the inclusion of the
+    // test harness from //third_party to work.
+    server_->ServeFilesFromSourceDirectory(".");
+    EXPECT_TRUE(server_->Start()) << "Failed to start embedded test server";
+  }
+  return server_.get();
 }
 
 content::WebContents* XrBrowserTestBase::GetFirstTabWebContents() {
diff --git a/chrome/browser/vr/test/xr_browser_test.h b/chrome/browser/vr/test/xr_browser_test.h
index 76bb1b617..cfa9f0f05 100644
--- a/chrome/browser/vr/test/xr_browser_test.h
+++ b/chrome/browser/vr/test/xr_browser_test.h
@@ -16,6 +16,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
 namespace vr {
@@ -45,6 +46,8 @@
   static constexpr char kVrConfigPathVal[] = "./";
   static constexpr char kVrLogPathEnvVar[] = "VR_LOG_PATH";
   static constexpr char kVrLogPathVal[] = "./";
+  static constexpr char kTestFileDir[] =
+      "chrome/test/data/xr/e2e_test_files/html/";
   enum class TestStatus {
     STATUS_RUNNING = 0,
     STATUS_PASSED = 1,
@@ -59,7 +62,15 @@
   // Returns a GURL to the XR test HTML file of the given name, e.g.
   // GetHtmlTestFile("foo") returns a GURL for the foo.html file in the XR
   // test HTML directory.
-  GURL GetHtmlTestFile(const std::string& test_name);
+  GURL GetFileUrlForHtmlTestFile(const std::string& test_name);
+
+  // Returns a GURL to the XR test HTML file of the given name served through
+  // the local server.
+  GURL GetEmbeddedServerUrlForHtmlTestFile(const std::string& test_name);
+
+  // Returns a pointer to the embedded test server capable of serving test
+  // HTML files, initializing and starting the server if necessary.
+  net::EmbeddedTestServer* GetEmbeddedServer();
 
   // Convenience function for accessing the WebContents belonging to the first
   // tab open in the browser.
@@ -180,6 +191,7 @@
   std::vector<std::string> append_switches_;
 
  private:
+  std::unique_ptr<net::EmbeddedTestServer> server_;
   base::test::ScopedFeatureList scoped_feature_list_;
   DISALLOW_COPY_AND_ASSIGN(XrBrowserTestBase);
 };
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index 1ec4ad05..e641fc8 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -86,6 +86,8 @@
       return kWebVrHostedUiContent;
     case UserFriendlyElementName::kMicrophonePermissionIndicator:
       return kAudioCaptureIndicator;
+    case UserFriendlyElementName::kWebXrExternalPromptNotification:
+      return kWebXrExternalPromptNotification;
     default:
       NOTREACHED();
       return kNone;
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
index 72c783775..81a96fc 100644
--- a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
+++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/vr/service/browser_xr_runtime.h"
 #include "chrome/browser/vr/service/xr_runtime_manager.h"
+#include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/browser/vr/win/vr_browser_renderer_thread_win.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/navigation_controller.h"
@@ -71,6 +72,11 @@
     permission_request_manager_ = nullptr;
   }
 
+  if (web_contents_)
+    VrTabHelper::SetIsContentDisplayedInHeadset(web_contents_, false);
+  if (contents)
+    VrTabHelper::SetIsContentDisplayedInHeadset(contents, true);
+
   web_contents_ = contents;
   if (contents) {
     StartUiRendering();
@@ -114,7 +120,6 @@
 
   DCHECK(info_);
   ui_rendering_thread_ = std::make_unique<VRBrowserRendererThreadWin>();
-  ui_rendering_thread_->Start();
   ui_rendering_thread_->SetVRDisplayInfo(info_.Clone());
 }
 
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index a86b5b2..88bb156 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -1438,6 +1438,7 @@
                 &ColorScheme::modal_prompt_background, &Rect::SetColor);
 
   auto scaler = Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kPromptDistance);
+  scaler->SetName(kWebXrExternalPromptNotification);
   scaler->SetType(kTypeScaledDepthAdjuster);
   scaler->AddChild(std::move(prompt_window));
   scaler->set_contributes_to_parent_bounds(false);
diff --git a/chrome/browser/vr/ui_test_input.h b/chrome/browser/vr/ui_test_input.h
index 5ece3ec3..0c9cbe6 100644
--- a/chrome/browser/vr/ui_test_input.h
+++ b/chrome/browser/vr/ui_test_input.h
@@ -38,8 +38,11 @@
                          // is in use.
   kWebXrHostedContent,   // Hosted content in a WebXR immersive session, e.g.
                          // permission prompts.
-  kMicrophonePermissionIndicator,  // The microphone icon that appears when a
-                                   // page is using the microphone permission.
+  kMicrophonePermissionIndicator,    // The microphone icon that appears when a
+                                     // page is using the microphone permission.
+  kWebXrExternalPromptNotification,  // The notification shown in the headset
+                                     // if a permission is requested while in
+                                     // immersive WebXR session.
 };
 
 // These are the types of actions that Java can request callbacks for once
diff --git a/chrome/browser/vr/vr_tab_helper.cc b/chrome/browser/vr/vr_tab_helper.cc
index d2cc528..29ba637 100644
--- a/chrome/browser/vr/vr_tab_helper.cc
+++ b/chrome/browser/vr/vr_tab_helper.cc
@@ -6,6 +6,8 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/common/web_preferences.h"
 #include "device/vr/buildflags/buildflags.h"
@@ -74,7 +76,21 @@
     VrTabHelper::CreateForWebContents(contents);
     vr_tab_helper = VrTabHelper::FromWebContents(contents);
   }
+  bool old_state = vr_tab_helper->IsContentDisplayedInHeadset(contents);
   vr_tab_helper->SetIsContentDisplayedInHeadset(state);
+  if (old_state != state) {
+#if !defined(OS_ANDROID)
+    Browser* browser = chrome::FindBrowserWithWebContents(contents);
+    if (browser) {
+      TabStripModel* tab_strip_model = browser->tab_strip_model();
+      if (tab_strip_model) {
+        tab_strip_model->UpdateWebContentsStateAt(
+            tab_strip_model->GetIndexOfWebContents(contents),
+            TabChangeType::kAll);
+      }
+    }
+#endif
+  }
 }
 
 void VrTabHelper::SetIsContentDisplayedInHeadset(bool state) {
diff --git a/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc b/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
index 88d72d0..70f20d9 100644
--- a/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
@@ -164,7 +164,7 @@
   MyOpenVRMock my_mock;
 
   // Load the test page, and enter presentation.
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->EnterSessionWithUserGestureOrFail();
 
   // Wait for JavaScript to submit at least one frame.
diff --git a/chrome/browser/vr/webxr_vr_input_browser_test.cc b/chrome/browser/vr/webxr_vr_input_browser_test.cc
index e682727..b13559a 100644
--- a/chrome/browser/vr/webxr_vr_input_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_input_browser_test.cc
@@ -20,7 +20,7 @@
 // input.
 void TestPresentationLocksFocusImpl(WebXrVrBrowserTestBase* t,
                                     std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->EnterSessionWithUserGestureOrFail();
   t->ExecuteStepAndWait("stepSetupFocusLoss()");
   t->EndTest();
@@ -97,7 +97,7 @@
 
   // Load the test page and enter presentation.
   this->LoadUrlAndAwaitInitialization(
-      this->GetHtmlTestFile("test_webxr_input"));
+      this->GetFileUrlForHtmlTestFile("test_webxr_input"));
   this->EnterSessionWithUserGestureOrFail();
 
   unsigned int num_iterations = 10;
@@ -132,7 +132,7 @@
 
   // Load the test page and enter presentation.
   this->LoadUrlAndAwaitInitialization(
-      this->GetHtmlTestFile("test_gamepad_button"));
+      this->GetFileUrlForHtmlTestFile("test_gamepad_button"));
   this->EnterSessionWithUserGestureOrFail();
 
   // We need to have this, otherwise the JavaScript side of the Gamepad API
diff --git a/chrome/browser/vr/webxr_vr_permission_request_browser_test.cc b/chrome/browser/vr/webxr_vr_permission_request_browser_test.cc
new file mode 100644
index 0000000..88e0f327
--- /dev/null
+++ b/chrome/browser/vr/webxr_vr_permission_request_browser_test.cc
@@ -0,0 +1,45 @@
+// 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 "base/bind_helpers.h"
+#include "chrome/browser/vr/test/ui_utils.h"
+#include "chrome/browser/vr/test/webxr_vr_browser_test.h"
+#include "chrome/browser/vr/ui_test_input.h"
+
+// Browser tests for testing permission requests that occur during a WebXR
+// immersive session.
+
+namespace vr {
+
+void InSessionPermissionNotificationCommon(WebXrVrBrowserTestStandard* t) {
+  // We need to use a local server for permission requests to not hit a DCHECK.
+  t->LoadUrlAndAwaitInitialization(
+      t->GetEmbeddedServerUrlForHtmlTestFile("generic_webxr_page"));
+  t->EnterSessionWithUserGestureOrFail();
+  // Use location instead of camera/microphone since those automatically reject
+  // if a suitable device is not connected.
+  // TODO(bsheedy): Find a way to support more permission types (maybe use
+  // MockPermissionPromptFactory?).
+  t->RunJavaScriptOrFail(
+      "navigator.geolocation.getCurrentPosition( ()=>{}, ()=>{} )");
+  auto utils = UiUtils::Create();
+  utils->PerformActionAndWaitForVisibilityStatus(
+      UserFriendlyElementName::kWebXrExternalPromptNotification,
+      true /* visible */, base::DoNothing::Once());
+}
+
+// Tests that permission requests that occur when in an immersive session cause
+// a notification to appear telling the user that a permission request is
+// visible in the browser and that closing the browser while this is still
+// displayed does not cause any issues.
+IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
+                       TestInSessionPermissionNotificationCloseWhileVisible) {
+  InSessionPermissionNotificationCommon(this);
+}
+
+// TODO(https://crbug.com/920697): Add tests verifying the notification
+// disappears when the permission is accepted/denied once we can query element
+// visibility at any time using PermissionRequestManagerTestApi.
+
+}  // namespace vr
diff --git a/chrome/browser/vr/webxr_vr_pixel_browser_test.cc b/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
index 1bc5622..048cf112 100644
--- a/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
@@ -59,7 +59,7 @@
   MyOpenVRMock my_mock;
 
   // Load the test page, and enter presentation.
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->EnterSessionWithUserGestureOrFail();
 
   // Wait for JavaScript to submit at least one frame.
diff --git a/chrome/browser/vr/webxr_vr_tab_browser_test.cc b/chrome/browser/vr/webxr_vr_tab_browser_test.cc
index 1b8d462..56b3ddb 100644
--- a/chrome/browser/vr/webxr_vr_tab_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_tab_browser_test.cc
@@ -17,7 +17,7 @@
 // Tests that non-focused tabs cannot get pose information from WebVR/WebXR.
 void TestPoseDataUnfocusedTabImpl(WebXrVrBrowserTestBase* t,
                                   std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->ExecuteStepAndWait("stepCheckFrameDataWhileFocusedTab()");
   chrome::AddTabAt(t->browser(), GURL(url::kAboutBlankURL),
                    -1 /* index, append to end */, true /* foreground */);
diff --git a/chrome/browser/vr/webxr_vr_transition_browser_test.cc b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
index a81ef7b..7fde682 100644
--- a/chrome/browser/vr/webxr_vr_transition_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
@@ -19,7 +19,7 @@
 // an immersive session.
 void TestPresentationEntryImpl(WebXrVrBrowserTestBase* t,
                                std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->EnterSessionWithUserGestureOrFail();
   t->AssertNoJavaScriptErrors();
 }
@@ -35,7 +35,7 @@
 // WebVR/WebXR presentation since the tab is still visible.
 void TestWindowRafFiresWhilePresentingImpl(WebXrVrBrowserTestBase* t,
                                            std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->ExecuteStepAndWait("stepVerifyBeforePresent()");
   t->EnterSessionWithUserGestureOrFail();
   t->ExecuteStepAndWait("stepVerifyDuringPresent()");
@@ -60,7 +60,7 @@
 // remove the GPU requirement.
 void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t,
                                        std::string filename) {
-  t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
+  t->LoadUrlAndAwaitInitialization(t->GetFileUrlForHtmlTestFile(filename));
   t->WaitOnJavaScriptStep();
   t->EndTest();
 }
@@ -80,7 +80,8 @@
 // Since WebVR isn't actually used, we can remove the GPU requirement.
 IN_PROC_BROWSER_TEST_F(WebVrBrowserTestOpenVrDisabled,
                        TestWebVrNoDevicesWithoutOpenVr) {
-  LoadUrlAndAwaitInitialization(GetHtmlTestFile("generic_webvr_page"));
+  LoadUrlAndAwaitInitialization(
+      GetFileUrlForHtmlTestFile("generic_webvr_page"));
   EXPECT_FALSE(XrDeviceFound())
       << "Found a VRDisplay even with OpenVR disabled";
   AssertNoJavaScriptErrors();
@@ -91,7 +92,7 @@
 IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestOpenVrDisabled,
                        TestWebXrNoDevicesWithoutOpenVr) {
   LoadUrlAndAwaitInitialization(
-      GetHtmlTestFile("test_webxr_does_not_return_device"));
+      GetFileUrlForHtmlTestFile("test_webxr_does_not_return_device"));
   WaitOnJavaScriptStep();
   EndTest();
 }
@@ -100,8 +101,8 @@
 // non-immersive WebXR session.
 IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
                        TestWindowRafFiresDuringNonImmersiveSession) {
-  LoadUrlAndAwaitInitialization(
-      GetHtmlTestFile("test_window_raf_fires_during_non_immersive_session"));
+  LoadUrlAndAwaitInitialization(GetFileUrlForHtmlTestFile(
+      "test_window_raf_fires_during_non_immersive_session"));
   WaitOnJavaScriptStep();
   EndTest();
 }
@@ -111,7 +112,7 @@
 IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
                        TestNonImmersiveStopsDuringImmersive) {
   LoadUrlAndAwaitInitialization(
-      GetHtmlTestFile("test_non_immersive_stops_during_immersive"));
+      GetFileUrlForHtmlTestFile("test_non_immersive_stops_during_immersive"));
   ExecuteStepAndWait("stepBeforeImmersive()");
   EnterSessionWithUserGestureOrFail();
   ExecuteStepAndWait("stepDuringImmersive()");
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
index fffc44d3..2cbf1300 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -22,45 +22,34 @@
 
 namespace vr {
 
-VRBrowserRendererThreadWin::VRBrowserRendererThreadWin()
-    : MaybeThread("VRBrowserRenderThread") {}
+VRBrowserRendererThreadWin* VRBrowserRendererThreadWin::instance_for_testing_ =
+    nullptr;
+
+VRBrowserRendererThreadWin::VRBrowserRendererThreadWin() {
+  DCHECK(instance_for_testing_ == nullptr);
+  instance_for_testing_ = this;
+}
 
 VRBrowserRendererThreadWin::~VRBrowserRendererThreadWin() {
-  Stop();
+  // Call Cleanup to ensure correct destruction order of VR-UI classes.
+  CleanUp();
+  instance_for_testing_ = nullptr;
+}
+
+void VRBrowserRendererThreadWin::CleanUp() {
+  browser_renderer_ = nullptr;
+  initializing_graphics_ = nullptr;
+  overlay_ = nullptr;
 }
 
 void VRBrowserRendererThreadWin::SetVRDisplayInfo(
     device::mojom::VRDisplayInfoPtr display_info) {
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VRBrowserRendererThreadWin::SetDisplayInfoOnRenderThread,
-                     base::Unretained(this), std::move(display_info)));
-}
-
-void VRBrowserRendererThreadWin::SetLocationInfo(GURL gurl) {
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VRBrowserRendererThreadWin::SetLocationInfoOnRenderThread,
-                     base::Unretained(this), std::move(gurl)));
-}
-
-void VRBrowserRendererThreadWin::SetVisibleExternalPromptNotification(
-    ExternalPromptNotificationType prompt) {
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VRBrowserRendererThreadWin::
-                         SetVisibleExternalPromptNotificationOnRenderThread,
-                     base::Unretained(this), prompt));
-}
-
-void VRBrowserRendererThreadWin::SetDisplayInfoOnRenderThread(
-    device::mojom::VRDisplayInfoPtr display_info) {
   display_info_ = std::move(display_info);
   if (graphics_)
     graphics_->SetVRDisplayInfo(display_info_.Clone());
 }
 
-void VRBrowserRendererThreadWin::SetLocationInfoOnRenderThread(GURL gurl) {
+void VRBrowserRendererThreadWin::SetLocationInfo(GURL gurl) {
   // TODO(https://crbug.com/905375): Set more of this state.  Only the GURL is
   // currently used, so its the only thing we are setting correctly.
   DCHECK(ui_) << "Must be called after StartOverlay";
@@ -71,9 +60,8 @@
   ui_->SetLocationBarState(state);
 }
 
-void VRBrowserRendererThreadWin::
-    SetVisibleExternalPromptNotificationOnRenderThread(
-        ExternalPromptNotificationType prompt) {
+void VRBrowserRendererThreadWin::SetVisibleExternalPromptNotification(
+    ExternalPromptNotificationType prompt) {
   bool currently_showing_ui = ShouldPauseWebXrAndDrawUI();
   current_external_prompt_notification_type_ = prompt;
   ui_->SetVisibleExternalPromptNotification(prompt);
@@ -90,35 +78,13 @@
   }
 }
 
-void VRBrowserRendererThreadWin::StartOverlay(
-    device::mojom::XRCompositorHost* compositor) {
-  device::mojom::ImmersiveOverlayPtrInfo overlay_info;
-  compositor->CreateImmersiveOverlay(mojo::MakeRequest(&overlay_info));
-
-  initializing_graphics_ = std::make_unique<GraphicsDelegateWin>();
-  if (!initializing_graphics_->InitializeOnMainThread()) {
-    return;
-  }
-
-  // Post a task to the thread to start an overlay.
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VRBrowserRendererThreadWin::StartOverlayOnRenderThread,
-                     base::Unretained(this), std::move(overlay_info)));
+VRBrowserRendererThreadWin*
+VRBrowserRendererThreadWin::GetInstanceForTesting() {
+  return instance_for_testing_;
 }
 
-void VRBrowserRendererThreadWin::StopOverlay() {
-  // Post a task to the thread to stop the overlay.
-  task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VRBrowserRendererThreadWin::StopOverlayOnRenderThread,
-                     base::Unretained(this)));
-}
-
-void VRBrowserRendererThreadWin::CleanUp() {
-  browser_renderer_ = nullptr;
-  initializing_graphics_ = nullptr;
-  overlay_ = nullptr;
+BrowserRenderer* VRBrowserRendererThreadWin::GetBrowserRendererForTesting() {
+  return browser_renderer_.get();
 }
 
 namespace {
@@ -157,9 +123,17 @@
   void ShowPageInfo() override {}
 };
 
-void VRBrowserRendererThreadWin::StartOverlayOnRenderThread(
-    device::mojom::ImmersiveOverlayPtrInfo overlay) {
-  overlay_.Bind(std::move(overlay));
+void VRBrowserRendererThreadWin::StartOverlay(
+    device::mojom::XRCompositorHost* compositor) {
+  device::mojom::ImmersiveOverlayPtrInfo overlay_info;
+  compositor->CreateImmersiveOverlay(mojo::MakeRequest(&overlay_info));
+
+  initializing_graphics_ = std::make_unique<GraphicsDelegateWin>();
+  if (!initializing_graphics_->InitializeOnMainThread()) {
+    return;
+  }
+
+  overlay_.Bind(std::move(overlay_info));
   initializing_graphics_->InitializeOnGLThread();
   initializing_graphics_->BindContext();
 
@@ -212,7 +186,7 @@
   graphics_->ClearContext();
 }
 
-void VRBrowserRendererThreadWin::StopOverlayOnRenderThread() {
+void VRBrowserRendererThreadWin::StopOverlay() {
   overlay_->SetOverlayAndWebXRVisibility(false, true);
   CleanUp();
 }
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
index 695180e..4fa8e053 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
@@ -21,39 +21,11 @@
 class SchedulerDelegateWin;
 class VRUiBrowserInterface;
 
-// TODO(https://crbug.com/902576) There were issues initializing gfx::FontList
-// on a background thread, so run UI on the main thread.
-#define VR_UI_ON_MAIN_THREAD
-
-#ifdef VR_UI_ON_MAIN_THREAD
-
-class MaybeThread {
- public:
-  explicit MaybeThread(std::string) {}
-  virtual ~MaybeThread() = default;
-  virtual void CleanUp() {}
-  void Start() {}
-  void Stop() { CleanUp(); }
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
-    return scoped_refptr<base::SingleThreadTaskRunner>(
-        base::ThreadTaskRunnerHandle::Get());
-  }
-};
-
-#else
-
-class MaybeThread : public base::Thread {
-  explicit MaybeThread(std::string name) : base::Thread(name) {}
-};
-
-#endif
-
-class VR_EXPORT VRBrowserRendererThreadWin : public MaybeThread {
+class VR_EXPORT VRBrowserRendererThreadWin {
  public:
   VRBrowserRendererThreadWin();
-  ~VRBrowserRendererThreadWin() override;
+  ~VRBrowserRendererThreadWin();
 
-  // Methods called on the browser's main thread.
   void StartOverlay(device::mojom::XRCompositorHost* host);
   void StopOverlay();
   void SetVRDisplayInfo(device::mojom::VRDisplayInfoPtr display_info);
@@ -61,19 +33,11 @@
   void SetVisibleExternalPromptNotification(
       ExternalPromptNotificationType prompt);
 
- private:
-  // base::Thread overrides
-  void CleanUp() override;
+  static VRBrowserRendererThreadWin* GetInstanceForTesting();
+  BrowserRenderer* GetBrowserRendererForTesting();
 
-  // Methods called on render thread.
-  void StartOverlayOnRenderThread(
-      device::mojom::ImmersiveOverlayPtrInfo overlay);
-  void StopOverlayOnRenderThread();
-  void SetDisplayInfoOnRenderThread(
-      device::mojom::VRDisplayInfoPtr display_info);
-  void SetLocationInfoOnRenderThread(GURL gurl);
-  void SetVisibleExternalPromptNotificationOnRenderThread(
-      ExternalPromptNotificationType prompt);
+ private:
+  void CleanUp();
   void OnPose(device::mojom::XRFrameDataPtr data);
   void SubmitResult(bool success);
   void SubmitFrame(device::mojom::XRFrameDataPtr data);
@@ -101,6 +65,11 @@
 
   device::mojom::ImmersiveOverlayPtr overlay_;
   device::mojom::VRDisplayInfoPtr display_info_;
+
+  // This class is effectively a singleton, although it's not actually
+  // implemented as one. Since tests need to access the thread to post tasks,
+  // just keep a static reference to the existing instance.
+  static VRBrowserRendererThreadWin* instance_for_testing_;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index e19347319d..4c5b21c 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -161,9 +161,14 @@
 
 bool ChromeAuthenticatorRequestDelegate::ShouldPermitIndividualAttestation(
     const std::string& relying_party_id) {
-  // If the RP ID is listed in the policy, signal that individual attestation is
-  // permitted.
-  return IsWebauthnRPIDListedInEnterprisePolicy(browser_context(),
+  constexpr char kGoogleCorpAppId[] =
+      "https://www.gstatic.com/securitykey/a/google.com/origins.json";
+
+  // If the RP ID is actually the Google corp App ID (because the request is
+  // actually a U2F request originating from cryptotoken), or is listed in the
+  // enterprise policy, signal that individual attestation is permitted.
+  return relying_party_id == kGoogleCorpAppId ||
+         IsWebauthnRPIDListedInEnterprisePolicy(browser_context(),
                                                 relying_party_id);
 }
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 9ff228e..c9a6c34 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -259,11 +259,6 @@
 #endif
 };
 
-#if defined(OS_CHROMEOS)
-const base::Feature kExperimentalCrostiniUI{"ExperimentalCrostiniUI",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 // If enabled, this feature's |kExternalInstallDefaultButtonKey| field trial
 // parameter value controls which |ExternalInstallBubbleAlert| button is the
 // default.
@@ -664,6 +659,10 @@
 // TODO(crbug.com/874630): Remove this kill-switch
 const base::Feature kUsbguard{"USBGuard", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable USB Bouncer for managing a device whitelist for USBGuard on Chrome OS.
+const base::Feature kUsbbouncer{"USBBouncer",
+                                base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable running shill in a minijail sandbox on Chrome OS.
 const base::Feature kShillSandboxing{"ShillSandboxing",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 75bda3b..7847ed2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -161,11 +161,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kExperimentalAppBanners;
 
-#if defined(OS_CHROMEOS)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kExperimentalCrostiniUI;
-#endif
-
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kExternalExtensionDefaultButtonControl;
 
@@ -440,6 +435,8 @@
 
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kUsbguard;
 
+COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kUsbbouncer;
+
 COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShillSandboxing;
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 8fac18c..d1c17b07 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -293,11 +293,6 @@
 const char kEnableExtensionActivityLogTesting[] =
     "enable-extension-activity-log-testing";
 
-// Enable the fast unload controller, which speeds up tab/window close by
-// running a tab's onunload js handler independently of the GUI -
-// crbug.com/142458 .
-const char kEnableFastUnload[] = "enable-fast-unload";
-
 // Runs the Native Client inside the renderer process and enables GPU plugin
 // (internally adds lEnableGpuPlugin to the command line).
 const char kEnableNaCl[]                    = "enable-nacl";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 6ed08842..990314b 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -96,7 +96,6 @@
 extern const char kEnableDomainReliability[];
 extern const char kEnableExtensionActivityLogging[];
 extern const char kEnableExtensionActivityLogTesting[];
-extern const char kEnableFastUnload[];
 extern const char kEnableNaCl[];
 extern const char kEnableNavigationTracing[];
 extern const char kEnableNetBenchmarking[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index c907fea..5e870cee 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1249,7 +1249,11 @@
 // TODO(https://crbug.com/837614): Remove this after a few releases (M69?).
 const char kClearedBlockedSiteNotificationChannels[] =
     "notifications.cleared_blocked_channels";
-#endif
+
+// Usage stats reporting opt-in.
+const char kUsageStatsEnabled[] = "usage_stats_reporting.enabled";
+
+#endif  // defined(OS_ANDROID)
 
 // Maps from app ids to origin + Service Worker registration ID.
 const char kPushMessagingAppIdentifierMap[] =
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 98be27a..46e01a4 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -417,6 +417,7 @@
 extern const char kNotificationsVibrateEnabled[];
 extern const char kMigratedToSiteNotificationChannels[];
 extern const char kClearedBlockedSiteNotificationChannels[];
+extern const char kUsageStatsEnabled[];
 #endif
 
 extern const char kPushMessagingAppIdentifierMap[];
@@ -903,6 +904,10 @@
 
 extern const char kEnterpriseHardwarePlatformAPIEnabled[];
 
+#if defined(OS_ANDROID)
+extern const char kUsageStatsEnabled[];
+#endif
+
 }  // namespace prefs
 
 #endif  // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 610a2a0..dc71a343 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -26,8 +26,6 @@
 const char kChromeUIBluetoothInternalsHost[] = "bluetooth-internals";
 const char kChromeUIBookmarksHost[] = "bookmarks";
 const char kChromeUIBookmarksURL[] = "chrome://bookmarks/";
-const char kChromeUICertificateViewerDialogHost[] = "view-cert-dialog";
-const char kChromeUICertificateViewerDialogURL[] = "chrome://view-cert-dialog/";
 const char kChromeUICertificateViewerHost[] = "view-cert";
 const char kChromeUICertificateViewerURL[] = "chrome://view-cert/";
 const char kChromeUIChromeSigninHost[] = "chrome-signin";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 01d72a81..105a8b7 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -33,8 +33,6 @@
 extern const char kChromeUIBluetoothInternalsHost[];
 extern const char kChromeUIBookmarksHost[];
 extern const char kChromeUIBookmarksURL[];
-extern const char kChromeUICertificateViewerDialogHost[];
-extern const char kChromeUICertificateViewerDialogURL[];
 extern const char kChromeUICertificateViewerHost[];
 extern const char kChromeUICertificateViewerURL[];
 extern const char kChromeUIChromeSigninHost[];
diff --git a/chrome/credential_provider/common/gcp_strings.cc b/chrome/credential_provider/common/gcp_strings.cc
index dbab6a2..c09c54f 100644
--- a/chrome/credential_provider/common/gcp_strings.cc
+++ b/chrome/credential_provider/common/gcp_strings.cc
@@ -8,6 +8,7 @@
 
 // Names of keys returned on json data from UI process.
 const char kKeyEmail[] = "email";
+const char kKeyPicture[] = "picture";
 const char kKeyFullname[] = "full_name";
 const char kKeyId[] = "id";
 const char kKeyMdmUrl[] = "mdm_url";
@@ -24,6 +25,7 @@
 const wchar_t kUserTokenHandle[] = L"th";
 const wchar_t kUserEmail[] = L"email";
 const wchar_t kUserId[] = L"id";
+const wchar_t kUserPictureUrl[] = L"pic";
 
 // Username and password key for special GAIA account to run GLS.
 const wchar_t kDefaultGaiaAccountName[] = L"gaia";
diff --git a/chrome/credential_provider/common/gcp_strings.h b/chrome/credential_provider/common/gcp_strings.h
index 9a26fe3..cb94ce9 100644
--- a/chrome/credential_provider/common/gcp_strings.h
+++ b/chrome/credential_provider/common/gcp_strings.h
@@ -40,6 +40,7 @@
 
 // Names of keys returned on json data from UI process.
 extern const char kKeyEmail[];
+extern const char kKeyPicture[];
 extern const char kKeyFullname[];
 extern const char kKeyId[];
 extern const char kKeyMdmUrl[];
@@ -56,6 +57,7 @@
 extern const wchar_t kUserTokenHandle[];
 extern const wchar_t kUserEmail[];
 extern const wchar_t kUserId[];
+extern const wchar_t kUserPictureUrl[];
 
 // Username and password key for special GAIA account to run GLS.
 extern const wchar_t kDefaultGaiaAccountName[];
diff --git a/chrome/credential_provider/gaiacp/BUILD.gn b/chrome/credential_provider/gaiacp/BUILD.gn
index 5cb651a..1b58b31f 100644
--- a/chrome/credential_provider/gaiacp/BUILD.gn
+++ b/chrome/credential_provider/gaiacp/BUILD.gn
@@ -81,6 +81,8 @@
     "scoped_user_profile.cc",
     "scoped_user_profile.h",
     "stdafx.h",
+    "win_http_url_fetcher.cc",
+    "win_http_url_fetcher.h",
   ]
   public_configs = [ ":gaiacp_config" ]
   public_deps = [
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index ef1c672..f40cf23 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -646,13 +646,6 @@
   // language.
   command_line->AppendSwitchNative("lang", GetSelectedLanguage());
 
-  // The gpu process will be running on an alternative desktop since it does not
-  // have access to the winlogon desktop. This mitigation is required merely to
-  // be able to start Chrome during winlogon. However, in this scenario no gpu
-  // rendering can be done to the screen, so all the gpu features need to be
-  // disabled. (crbug.com/904902)
-  command_line->AppendSwitch("disable-gpu");
-
   return S_OK;
 }
 
diff --git a/chrome/credential_provider/gaiacp/reg_utils.cc b/chrome/credential_provider/gaiacp/reg_utils.cc
index 7d99051..b32b89f 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.cc
+++ b/chrome/credential_provider/gaiacp/reg_utils.cc
@@ -7,11 +7,13 @@
 #include <Windows.h>
 
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/win/registry.h"
 #include "chrome/credential_provider/common/gcp_strings.h"
 
 namespace credential_provider {
 
+
 namespace {
 
 // Root registry key for GCP configuration and state.
@@ -22,6 +24,9 @@
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
 const wchar_t kGcpRootKeyName[] = CREDENTIAL_PROVIDER_REGISTRY_KEY;
+const wchar_t kAccountPicturesRootRegKey[] =
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AccountPicture\\Users";
+const wchar_t kImageRegKey[] = L"Image";
 
 HRESULT GetRegDWORD(const base::string16& key_name,
                     const base::string16& name,
@@ -38,10 +43,26 @@
   return S_OK;
 }
 
-HRESULT GetRegString(const base::string16& key_name,
-                     const base::string16& name,
-                     wchar_t* value,
-                     ULONG* length) {
+HRESULT SetRegDWORD(const base::string16& key_name,
+                    const base::string16& name,
+                    DWORD value) {
+  base::win::RegKey key;
+  LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_WRITE);
+  if (sts != ERROR_SUCCESS)
+    return HRESULT_FROM_WIN32(sts);
+
+  sts = key.WriteValue(name.c_str(), value);
+  if (sts != ERROR_SUCCESS)
+    return HRESULT_FROM_WIN32(sts);
+
+  return S_OK;
+}
+
+
+HRESULT GetMachineRegString(const base::string16& key_name,
+                            const base::string16& name,
+                            wchar_t* value,
+                            ULONG* length) {
   DCHECK(value);
   DCHECK(length);
   DCHECK_GT(*length, 0u);
@@ -70,24 +91,9 @@
   return S_OK;
 }
 
-HRESULT SetRegDWORD(const base::string16& key_name,
-                    const base::string16& name,
-                    DWORD value) {
-  base::win::RegKey key;
-  LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_WRITE);
-  if (sts != ERROR_SUCCESS)
-    return HRESULT_FROM_WIN32(sts);
-
-  sts = key.WriteValue(name.c_str(), value);
-  if (sts != ERROR_SUCCESS)
-    return HRESULT_FROM_WIN32(sts);
-
-  return S_OK;
-}
-
-HRESULT SetRegString(const base::string16& key_name,
-                     const base::string16& name,
-                     const base::string16& value) {
+HRESULT SetMachineRegString(const base::string16& key_name,
+                            const base::string16& name,
+                            const base::string16& value) {
   base::win::RegKey key;
   LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_WRITE);
   if (sts != ERROR_SUCCESS)
@@ -105,8 +111,36 @@
   return S_OK;
 }
 
+base::string16 GetImageRegKeyForSpecificSize(int image_size) {
+  return base::StringPrintf(L"%ls%i", kImageRegKey, image_size);
+}
+
+
+base::string16 GetAccountPictureRegPathForUSer(const base::string16& user_sid) {
+  return base::StringPrintf(L"%ls\\%ls", kAccountPicturesRootRegKey,
+                            user_sid.c_str());
+}
+
 }  // namespace
 
+HRESULT GetAccountPictureRegString(const base::string16& user_sid,
+  int image_size,
+  wchar_t* value,
+  ULONG* length) {
+  return GetMachineRegString(GetAccountPictureRegPathForUSer(user_sid),
+                             GetImageRegKeyForSpecificSize(image_size), value,
+      length);
+}
+
+// Sets a specific account picture registry key in HKEY_LOCAL_MACHINE
+HRESULT SetAccountPictureRegString(const base::string16& user_sid,
+                                   int image_size,
+  const base::string16& value) {
+
+  return SetMachineRegString(GetAccountPictureRegPathForUSer(user_sid),
+                             GetImageRegKeyForSpecificSize(image_size), value);
+}
+
 HRESULT GetGlobalFlag(const base::string16& name, DWORD* value) {
   return GetRegDWORD(kGcpRootKeyName, name, value);
 }
@@ -114,12 +148,12 @@
 HRESULT GetGlobalFlag(const base::string16& name,
                       wchar_t* value,
                       ULONG* length) {
-  return GetRegString(kGcpRootKeyName, name, value, length);
+  return GetMachineRegString(kGcpRootKeyName, name, value, length);
 }
 
 HRESULT SetGlobalFlagForTesting(const base::string16& name,
                                 const base::string16& value) {
-  return SetRegString(kGcpRootKeyName, name, value);
+  return SetMachineRegString(kGcpRootKeyName, name, value);
 }
 
 HRESULT GetUserProperty(const base::string16& sid,
@@ -138,7 +172,7 @@
   wchar_t key_name[128];
   swprintf_s(key_name, base::size(key_name), L"%s\\Users\\%s", kGcpRootKeyName,
              sid.c_str());
-  return GetRegString(key_name, name, value, length);
+  return GetMachineRegString(key_name, name, value, length);
 }
 
 HRESULT SetUserProperty(const base::string16& sid,
@@ -156,7 +190,7 @@
   wchar_t key_name[128];
   swprintf_s(key_name, base::size(key_name), L"%s\\Users\\%s", kGcpRootKeyName,
              sid.c_str());
-  return SetRegString(key_name, name, value);
+  return SetMachineRegString(key_name, name, value);
 }
 
 HRESULT RemoveAllUserProperties(const base::string16& sid) {
diff --git a/chrome/credential_provider/gaiacp/reg_utils.h b/chrome/credential_provider/gaiacp/reg_utils.h
index b7662404..eb56767 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.h
+++ b/chrome/credential_provider/gaiacp/reg_utils.h
@@ -68,6 +68,16 @@
 // Returns the root registry key that needs to be verified in unit tests.
 const wchar_t* GetUsersRootKeyForTesting();
 
+// Gets a specific account picture registry key in HKEY_LOCAL_MACHINE
+HRESULT GetAccountPictureRegString(const base::string16& user_sid,
+                                   int image_size,
+                            wchar_t* value,
+                            ULONG* length);
+
+// Sets a specific account picture registry key in HKEY_LOCAL_MACHINE
+HRESULT SetAccountPictureRegString(const base::string16& user_sid,
+                                   int image_size,
+                            const base::string16& value);
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_REG_UTILS_H_
diff --git a/chrome/credential_provider/gaiacp/scoped_user_profile.cc b/chrome/credential_provider/gaiacp/scoped_user_profile.cc
index 757c5d8..a8b04da 100644
--- a/chrome/credential_provider/gaiacp/scoped_user_profile.cc
+++ b/chrome/credential_provider/gaiacp/scoped_user_profile.cc
@@ -5,18 +5,24 @@
 #include "chrome/credential_provider/gaiacp/scoped_user_profile.h"
 
 #include <Windows.h>
+
+#include <atlconv.h>
 #include <dpapi.h>
 #include <security.h>
+#include <shlobj.h>
 #include <userenv.h>
-#include <atlconv.h>
 
+#include "base/files/file_util.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/registry.h"
+#include "base/win/windows_version.h"
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
+#include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
 
 namespace credential_provider {
 
@@ -29,6 +35,10 @@
 // retrying would not be needed, but this notification does not exist.
 const int kWaitForProfileCreationRetryCount = 30;
 
+constexpr wchar_t kDefaultProfilePictureFileExtension[] = L".jpg";
+
+constexpr int kProfilePictureSizes[] = {32, 40, 48, 96, 192, 240, 448};
+
 std::string GetEncryptedRefreshToken(
     base::win::ScopedHandle::Handle logon_handle,
     const base::DictionaryValue& properties) {
@@ -68,6 +78,142 @@
   return encrypted_data;
 }
 
+HRESULT GetBaseAccountPicturePath(base::FilePath* base_path) {
+  DCHECK(base_path);
+  base_path->clear();
+  LPWSTR path;
+  HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_PublicUserTiles, 0, NULL, &path);
+  if (FAILED(hr)) {
+    LOGFN(ERROR) << "SHGetKnownFolderPath=" << putHR(hr);
+    return hr;
+  }
+  *base_path = base::FilePath(path);
+  ::CoTaskMemFree(path);
+  return S_OK;
+}
+
+HRESULT UpdateProfilePicturesForWindows8AndNewer(
+    const base::string16& sid,
+    const base::string16& picture_url,
+    bool force_update) {
+  DCHECK(!sid.empty());
+  DCHECK(!picture_url.empty());
+  DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN8);
+
+  // Try to download profile pictures of all required sizes for windows.
+  // Needed profile picture sizes are in |kProfilePictureSizes|.
+  // The way Windows8+ stores sets profile pictures is the following:
+  // In |reg_utils.cc:kAccountPicturesRootRegKey| there is a registry key
+  // for each resolution of profile picture needed. The keys are names
+  // "Image[x]" where [x] is the resolution of the picture.
+  // Each key points to a profile picture of the correct resolution on disk.
+  // Generally the profile pictures are stored under:
+  // FOLDERID_PublicUserTiles\\{user sid}
+
+  base::string16 picture_url_path = base::UTF8ToUTF16(GURL(picture_url).path());
+  if (picture_url_path.size() <= 1) {
+    LOGFN(ERROR) << "Invalid picture url=" << picture_url;
+    return E_FAIL;
+  }
+
+  base::FilePath account_picture_base_path;
+  HRESULT hr = GetBaseAccountPicturePath(&account_picture_base_path);
+  if (FAILED(hr)) {
+    LOGFN(ERROR) << "Failed to get account picture known folder=" << putHR(hr);
+    return E_FAIL;
+  }
+
+  base::FilePath account_picture_path =
+      account_picture_base_path.Append(sid.c_str());
+
+  if (!base::PathExists(account_picture_path) &&
+      !base::CreateDirectory(account_picture_path)) {
+    LOGFN(ERROR) << "Failed to create profile picture directory="
+                 << account_picture_path;
+    return E_FAIL;
+  }
+
+  constexpr wchar_t kBasePictureFilename[] = L"GoogleAccountPicture";
+
+  base::string16 base_picture_extension = kDefaultProfilePictureFileExtension;
+  base::string16 base_picture_filename = kBasePictureFilename;
+  base_picture_filename += L"_";
+
+  size_t last_period = picture_url_path.find_last_of('.');
+  if (last_period != std::string::npos) {
+    base_picture_extension = picture_url_path.substr(last_period);
+  }
+
+  for (auto image_size : kProfilePictureSizes) {
+    base::string16 image_size_postfix =
+        base::StringPrintf(L"%i", image_size);
+    base::FilePath target_picture_path = account_picture_path.Append(
+        base_picture_filename + image_size_postfix + base_picture_extension);
+
+    // Skip if the file already exists and an update is not forced.
+    if (!force_update && base::PathExists(target_picture_path)) {
+      // Update the reg string for the image if it is not up to date.
+      wchar_t old_picture_path[MAX_PATH];
+      ULONG path_size = base::size(old_picture_path);
+      HRESULT hr = GetAccountPictureRegString(sid, image_size, old_picture_path,
+                                              &path_size);
+      if (FAILED(hr) || target_picture_path.value() != old_picture_path) {
+        HRESULT hr = SetAccountPictureRegString(sid, image_size,
+                                                target_picture_path.value());
+        if (FAILED(hr))
+          LOGFN(ERROR) << "SetAccountPictureRegString(pic) hr=" << putHR(hr);
+      }
+      continue;
+    }
+
+    std::string current_picture_url = base::UTF16ToUTF8(picture_url) +
+                                      base::StringPrintf("?sz=%i", image_size);
+
+    auto fetcher = WinHttpUrlFetcher::Create(GURL(current_picture_url));
+    if (!fetcher) {
+      LOGFN(ERROR) << "Failed to create fetcher for=" << current_picture_url;
+      continue;
+    }
+
+    std::vector<char> response;
+    HRESULT hr = fetcher->Fetch(&response);
+    if (FAILED(hr)) {
+      LOGFN(INFO) << "fetcher.Fetch hr=" << putHR(hr);
+      continue;
+    }
+
+    // Make the file visible in case it is hidden or else WriteFile will fail
+    // to overwrite the existing file.
+    DWORD file_attributes =
+        ::GetFileAttributes(target_picture_path.value().c_str());
+    if (file_attributes != INVALID_FILE_ATTRIBUTES) {
+      ::SetFileAttributes(target_picture_path.value().c_str(),
+                          file_attributes & ~FILE_ATTRIBUTE_HIDDEN);
+    }
+    if (base::WriteFile(target_picture_path, response.data(),
+                        response.size()) != static_cast<int>(response.size())) {
+      LOGFN(ERROR) << "Failed to write profile picture to file="
+                   << target_picture_path;
+      continue;
+    }
+
+    // Make the picture file hidden just like the system would normally.
+    file_attributes = ::GetFileAttributes(target_picture_path.value().c_str());
+    if (file_attributes != INVALID_FILE_ATTRIBUTES) {
+      ::SetFileAttributes(target_picture_path.value().c_str(),
+                          file_attributes | FILE_ATTRIBUTE_HIDDEN);
+    }
+
+    // Finally update the registry to point to this profile picture.
+    hr = SetAccountPictureRegString(sid, image_size,
+                                    target_picture_path.value());
+    if (FAILED(hr))
+      LOGFN(ERROR) << "SetAccountPictureRegString(pic) hr=" << putHR(hr);
+  }
+
+  return S_OK;
+}
+
 }  // namespace
 
 // static
@@ -145,20 +291,19 @@
 
   // Save token handle.  This handle will be used later to determine if the
   // the user has changed their password since the account was created.
-  HRESULT hr = SetUserProperty(sid.c_str(), kUserTokenHandle,
-                               token_handle.c_str());
+  HRESULT hr = SetUserProperty(sid, kUserTokenHandle, token_handle);
   if (FAILED(hr)) {
     LOGFN(ERROR) << "SetUserProperty(th) hr=" << putHR(hr);
     return hr;
   }
 
-  hr = SetUserProperty(sid.c_str(), kUserId, id.c_str());
+  hr = SetUserProperty(sid, kUserId, id);
   if (FAILED(hr)) {
     LOGFN(ERROR) << "SetUserProperty(id) hr=" << putHR(hr);
     return hr;
   }
 
-  hr = SetUserProperty(sid.c_str(), kUserEmail, email.c_str());
+  hr = SetUserProperty(sid, kUserEmail, email);
   if (FAILED(hr)) {
     LOGFN(ERROR) << "SetUserProperty(email) hr=" << putHR(hr);
     return hr;
@@ -206,6 +351,24 @@
     }
   }
 
+  // This code for setting profile pictures is specific for windows 8+.
+  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+    base::string16 picture_url = GetDictString(&properties, kKeyPicture);
+    if (!picture_url.empty() && !sid.empty()) {
+      wchar_t old_picture_url[512];
+      ULONG url_size = base::size(old_picture_url);
+      hr = GetUserProperty(sid, kUserPictureUrl, old_picture_url, &url_size);
+
+      UpdateProfilePicturesForWindows8AndNewer(
+          sid, picture_url, FAILED(hr) || old_picture_url != picture_url);
+      hr = SetUserProperty(sid.c_str(), kUserPictureUrl, picture_url.c_str());
+      if (FAILED(hr)) {
+        LOGFN(ERROR) << "SetUserProperty(pic) hr=" << putHR(hr);
+        return hr;
+      }
+    }
+  }
+
   return S_OK;
 }
 
diff --git a/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc b/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc
new file mode 100644
index 0000000..d5eca95e
--- /dev/null
+++ b/chrome/credential_provider/gaiacp/win_http_url_fetcher.cc
@@ -0,0 +1,186 @@
+// Copyright 2018 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/credential_provider/gaiacp/win_http_url_fetcher.h"
+
+#include <Windows.h>
+#include <winhttp.h>
+
+#include <atlconv.h>
+
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/credential_provider/gaiacp/logging.h"
+
+namespace credential_provider {
+
+// static
+WinHttpUrlFetcher::CreatorCallback*
+WinHttpUrlFetcher::GetCreatorFunctionStorage() {
+  static CreatorCallback creator_for_testing;
+  return &creator_for_testing;
+}
+
+// static
+std::unique_ptr<WinHttpUrlFetcher> WinHttpUrlFetcher::Create(const GURL& url) {
+  return !GetCreatorFunctionStorage()->is_null()
+             ? GetCreatorFunctionStorage()->Run(url)
+             : std::unique_ptr<WinHttpUrlFetcher>(new WinHttpUrlFetcher(url));
+}
+
+WinHttpUrlFetcher::WinHttpUrlFetcher(const GURL& url)
+    : url_(url), session_(nullptr), request_(nullptr) {
+  LOGFN(INFO) << "url=" << url.spec() << " (scheme and port ignored)";
+
+  ScopedWinHttpHandle::Handle session = ::WinHttpOpen(
+      L"GaiaCP/1.0 (Windows NT)", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
+      WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
+  if (!session) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    LOGFN(ERROR) << "WinHttpOpen hr=" << putHR(hr);
+  }
+  session_.Set(session);
+}
+
+WinHttpUrlFetcher::WinHttpUrlFetcher() {}
+
+WinHttpUrlFetcher::~WinHttpUrlFetcher() {
+  // Closing the session handle closes all derived handles too.
+}
+
+bool WinHttpUrlFetcher::IsValid() const {
+  return session_.IsValid();
+}
+
+HRESULT WinHttpUrlFetcher::SetRequestHeader(const char* name,
+                                            const char* value) {
+  DCHECK(name);
+  DCHECK(value);
+
+  // TODO(rogerta): does not support multivalued headers.
+  request_headers_[name] = value;
+  return S_OK;
+}
+
+HRESULT WinHttpUrlFetcher::SetRequestBody(const char* body) {
+  DCHECK(body);
+  body_ = body;
+  return S_OK;
+}
+
+HRESULT WinHttpUrlFetcher::Fetch(std::vector<char>* response) {
+  USES_CONVERSION;
+  DCHECK(response);
+
+  response->clear();
+
+  if (!session_.IsValid()) {
+    LOGFN(ERROR) << "Invalid fetcher";
+    return E_UNEXPECTED;
+  }
+
+  // Open a connection to the server.
+  ScopedWinHttpHandle connect;
+  {
+    ScopedWinHttpHandle::Handle connect_tmp = ::WinHttpConnect(
+        session_.Get(), A2CW(url_.host().c_str()), INTERNET_DEFAULT_PORT, 0);
+    if (!connect_tmp) {
+      HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+      LOGFN(ERROR) << "WinHttpConnect hr=" << putHR(hr);
+      return hr;
+    }
+    connect.Set(connect_tmp);
+  }
+
+  {
+    bool use_post = !body_.empty();
+    ScopedWinHttpHandle::Handle request = ::WinHttpOpenRequest(
+        connect.Get(), use_post ? L"POST" : L"GET",
+        use_post ? A2CW(url_.path().c_str())
+                 : A2CW(url_.PathForRequest().c_str()),
+        nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
+        WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE);
+    if (!request) {
+      HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+      LOGFN(ERROR) << "WinHttpOpenRequest hr=" << putHR(hr);
+      return hr;
+    }
+    request_.Set(request);
+  }
+
+  // Add request headers.
+
+  for (const auto& kv : request_headers_) {
+    const wchar_t* key = A2CW(kv.first.c_str());
+    const wchar_t* value = A2CW(kv.second.c_str());
+    base::string16 header = base::StringPrintf(L"%ls: %ls", key, value);
+    if (!::WinHttpAddRequestHeaders(
+            request_.Get(), header.c_str(), header.length(),
+            WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+      HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+      LOGFN(ERROR) << "WinHttpAddRequestHeaders name=" << kv.first
+                   << " hr=" << putHR(hr);
+      return hr;
+    }
+  }
+
+  // Write request body if needed.
+
+  if (!::WinHttpSendRequest(request_.Get(), WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+                            const_cast<char*>(body_.c_str()), body_.length(),
+                            body_.length(),
+                            reinterpret_cast<DWORD_PTR>(nullptr))) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    LOGFN(ERROR) << "WinHttpSendRequest hr=" << putHR(hr);
+    return hr;
+  }
+
+  // Wait for the response.
+
+  if (!::WinHttpReceiveResponse(request_.Get(), nullptr)) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    LOGFN(ERROR) << "WinHttpReceiveResponse hr=" << putHR(hr);
+    return hr;
+  }
+
+  DWORD length = 0;
+  if (!::WinHttpQueryDataAvailable(request_.Get(), &length)) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    LOGFN(ERROR) << "WinHttpQueryDataAvailable hr=" << putHR(hr);
+    return hr;
+  }
+
+  // 256k max response size to make sure bad data does not crash GCPW.
+  // This fetcher is only used to retrieve small information such as token
+  // handle status and profile picture images so it should not need a larger
+  // buffer than 256k.
+  constexpr size_t kMaxResponseSize = 256 * 1024 * 1024;
+  // Read the response.
+  std::unique_ptr<char> buffer(new char[length]);
+  DWORD actual = 0;
+  do {
+    if (!::WinHttpReadData(request_.Get(), buffer.get(), length, &actual)) {
+      HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+      LOGFN(ERROR) << "WinHttpReadData hr=" << putHR(hr);
+      return hr;
+    }
+
+    size_t current_size = response->size();
+    response->resize(response->size() + actual);
+    memcpy(response->data() + current_size, buffer.get(), actual);
+    if (response->size() >= kMaxResponseSize) {
+      LOGFN(ERROR) << "Response has exceeded max size=" << kMaxResponseSize;
+      return E_OUTOFMEMORY;
+    }
+  } while (actual);
+
+  return S_OK;
+}
+
+HRESULT WinHttpUrlFetcher::Close() {
+  request_.Close();
+  return S_OK;
+}
+
+}  // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/win_http_url_fetcher.h b/chrome/credential_provider/gaiacp/win_http_url_fetcher.h
new file mode 100644
index 0000000..341f6c449
--- /dev/null
+++ b/chrome/credential_provider/gaiacp/win_http_url_fetcher.h
@@ -0,0 +1,61 @@
+// Copyright 2018 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_CREDENTIAL_PROVIDER_GAIACP_WIN_HTTP_URL_FETCHER_H_
+#define CHROME_CREDENTIAL_PROVIDER_GAIACP_WIN_HTTP_URL_FETCHER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "chrome/credential_provider/gaiacp/scoped_handle.h"
+#include "url/gurl.h"
+
+namespace credential_provider {
+
+class FakeWinHttpUrlFetcherFactory;
+
+// A synchronous URL fetcher for small requests.
+class WinHttpUrlFetcher {
+ public:
+  static std::unique_ptr<WinHttpUrlFetcher> Create(const GURL& url);
+
+  virtual ~WinHttpUrlFetcher();
+
+  virtual bool IsValid() const;
+
+  virtual HRESULT SetRequestHeader(const char* name, const char* value);
+  virtual HRESULT SetRequestBody(const char* body);
+  virtual HRESULT Fetch(std::vector<char>* response);
+  virtual HRESULT Close();
+
+ protected:
+  using Headers = std::map<std::string, std::string>;
+
+  // This constructor is used by the derived fake class to bypass the
+  // initialization code in the public constructor that will fail because the
+  // tests are not running elevated.
+  WinHttpUrlFetcher();
+
+ private:
+  friend class FakeWinHttpUrlFetcherFactory;
+
+  explicit WinHttpUrlFetcher(const GURL& url);
+
+  GURL url_;
+  Headers request_headers_;
+  std::string body_;
+  ScopedWinHttpHandle session_;
+  ScopedWinHttpHandle request_;
+
+  // Gets storage of the function pointer used to create instances of this
+  // class for tests.
+  using CreatorFunc = decltype(Create);
+  using CreatorCallback = base::RepeatingCallback<CreatorFunc>;
+  static CreatorCallback* GetCreatorFunctionStorage();
+};
+
+}  // namespace credential_provider
+
+#endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_WIN_HTTP_URL_FETCHER_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3717f21..0158734 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2354,6 +2354,7 @@
     "../browser/android/download/available_offline_content_provider_unittest.cc",
     "../browser/android/download/download_manager_service_unittest.cc",
     "../browser/android/explore_sites/blacklist_site_task_unittest.cc",
+    "../browser/android/explore_sites/clear_activities_task_unittest.cc",
     "../browser/android/explore_sites/explore_sites_feature_unittest.cc",
     "../browser/android/explore_sites/explore_sites_fetcher_unittest.cc",
     "../browser/android/explore_sites/explore_sites_schema_unittest.cc",
@@ -2430,6 +2431,7 @@
     "../browser/chrome_process_singleton_win_unittest.cc",
     "../browser/client_hints/client_hints_unittest.cc",
     "../browser/command_updater_impl_unittest.cc",
+    "../browser/complex_tasks/task_tab_helper_unittest.cc",
     "../browser/component_updater/chrome_component_updater_configurator_unittest.cc",
     "../browser/component_updater/optimization_hints_component_installer_unittest.cc",
     "../browser/component_updater/origin_trials_component_installer_unittest.cc",
@@ -3038,7 +3040,6 @@
   if (is_android) {
     sources += [
       "../browser/android/search_permissions/search_permissions_service_unittest.cc",
-      "../browser/android/tasks/task_tab_helper_unittest.cc",
       "../browser/autofill/autofill_credit_card_filling_infobar_delegate_mobile_unittest.cc",
       "../browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc",
       "../browser/autofill/manual_filling_controller_impl_unittest.cc",
@@ -5175,6 +5176,11 @@
     } else {
       sources += [ "../browser/metrics/desktop_session_duration/chrome_visibility_observer_interactive_uitest.cc" ]
     }
+
+    if (use_atk && toolkit_views) {
+      sources += [ "../browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc" ]
+      configs += [ "//build/config/linux/atk" ]
+    }
   }
 }
 
@@ -5217,7 +5223,6 @@
       "../browser/extensions/updater/extension_cache_fake.cc",
       "../browser/extensions/updater/extension_cache_fake.h",
       "base/browser_perf_tests_main.cc",
-      "perf/url_parse_perftest.cc",
     ]
 
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 960f8fa..1a82e9e 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -28,7 +28,6 @@
     "javatests/src/org/chromium/chrome/test/util/browser/contextmenu/ContextMenuUtils.java",
     "javatests/src/org/chromium/chrome/test/util/browser/Features.java",
     "javatests/src/org/chromium/chrome/test/util/browser/LocationSettingsTestUtil.java",
-    "javatests/src/org/chromium/chrome/test/util/browser/modelutil/FakeViewProvider.java",
     "javatests/src/org/chromium/chrome/test/util/browser/notifications/MockNotificationManagerProxy.java",
     "javatests/src/org/chromium/chrome/test/util/browser/offlinepages/FakeOfflinePageBridge.java",
     "javatests/src/org/chromium/chrome/test/util/browser/signin/MockChangeEventChecker.java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java
index a6846e9c..377ecc5a 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java
@@ -4,13 +4,13 @@
 
 package org.chromium.chrome.test.util.browser.suggestions;
 
-import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo;
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
 import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCardLayout;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
 import org.chromium.chrome.browser.suggestions.ContentSuggestionsAdditionalAction;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index e195821..c5e028b14 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -172,6 +172,7 @@
           callback) override {}
   std::string GetWorkspace() const override;
   bool IsVisibleOnAllWorkspaces() const override;
+  void ShowEmojiPanel() override {}
 
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
   void ShowInProductHelpPromo(InProductHelpFeature iph_feature) override {}
diff --git a/chrome/test/base/view_event_test_base.cc b/chrome/test/base/view_event_test_base.cc
index 4b69c3f..15ed429 100644
--- a/chrome/test/base/view_event_test_base.cc
+++ b/chrome/test/base/view_event_test_base.cc
@@ -56,9 +56,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestView);
 };
 
-// Delay in background thread before posting mouse move.
-const int kMouseMoveDelayMS = 200;
-
 }  // namespace
 
 ViewEventTestBase::ViewEventTestBase()
@@ -174,18 +171,17 @@
 }
 
 void ViewEventTestBase::ScheduleMouseMoveInBackground(int x, int y) {
-  if (!dnd_thread_.get()) {
-    dnd_thread_.reset(new base::Thread("mouse-move-thread"));
+  if (!dnd_thread_) {
+    dnd_thread_ = std::make_unique<base::Thread>("mouse-move-thread");
     dnd_thread_->Start();
   }
-  dnd_thread_->task_runner()->PostDelayedTask(
+  dnd_thread_->task_runner()->PostTask(
       FROM_HERE,
-      base::Bind(base::IgnoreResult(&ui_controls::SendMouseMove), x, y),
-      base::TimeDelta::FromMilliseconds(kMouseMoveDelayMS));
+      base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove), x, y));
 }
 
 void ViewEventTestBase::StopBackgroundThread() {
-  dnd_thread_.reset(NULL);
+  dnd_thread_.reset();
 }
 
 void ViewEventTestBase::RunTestMethod(const base::Closure& task) {
diff --git a/chrome/test/base/view_event_test_base.h b/chrome/test/base/view_event_test_base.h
index b9ccf41e..566c1c6 100644
--- a/chrome/test/base/view_event_test_base.h
+++ b/chrome/test/base/view_event_test_base.h
@@ -60,16 +60,17 @@
 // Once you have created a ViewEventTestBase use the macro VIEW_TEST to define
 // the fixture.
 //
-// I encountered weird timing problems in initiating dragging and drop that
-// necessitated ugly hacks. In particular when the hook installed by
-// ui_controls received the mouse event and posted a task that task was not
-// processed. To work around this use the following pattern when initiating
-// dnd:
+// Testing drag and drop is tricky because the mouse move that initiates drag
+// and drop may trigger a nested native event loop that waits for more mouse
+// messages.  Thus code needs an initial mouse move to start the drag, then a
+// mouse move enqueued from a background thread to finish the drag (since tasks
+// on the main thread may be stalled waiting for the nested loop to terminate).
+// You can do this with a pattern like:
 //   // Schedule the mouse move at a location slightly different from where
-//   // you really want to move to.
+//   // you really want to move to to start the drag.
 //   ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
 //       base::BindOnce(&YYY, this));
-//   // Then use this to schedule another mouse move.
+//   // Then schedule another mouse move to finish it.
 //   ScheduleMouseMoveInBackground(loc.x, loc.y);
 
 class ViewEventTestBase : public views::WidgetDelegate,
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index b2fbb1e..ffa3d15 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -387,10 +387,18 @@
     printf("Usage: %s [OPTIONS]\n\nOptions\n%s", argv[0], options.c_str());
     return 0;
   }
+  bool early_exit = false;
   if (cmd_line->HasSwitch("v") || cmd_line->HasSwitch("version")) {
     printf("ChromeDriver %s\n", kChromeDriverVersion);
-    return 0;
+    early_exit = true;
   }
+  if (cmd_line->HasSwitch("minimum-chrome-version")) {
+    printf("minimum supported Chrome version: %s\n",
+           GetMinimumSupportedChromeVersion().c_str());
+    early_exit = true;
+  }
+  if (early_exit)
+    return 0;
   if (cmd_line->HasSwitch("port")) {
     int cmd_line_port;
     if (!base::StringToInt(cmd_line->GetSwitchValueASCII("port"),
@@ -465,12 +473,6 @@
     return 1;
   }
 
-  if (cmd_line->HasSwitch("minimum-chrome-version")) {
-    printf("minimum supported Chrome version: %s\n",
-           GetMinimumSupportedChromeVersion().c_str());
-    return 0;
-  }
-
   mojo::core::Init();
 
   base::TaskScheduler::CreateAndStartWithDefaultParams("ChromeDriver");
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 896a5c36..1d2c2b59 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -245,6 +245,9 @@
         'HeadlessInvalidCertificateTest.*',
         # Tests of the desktop Chrome launch process.
         'LaunchDesktopTest.*',
+        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2737
+        'ChromeDriverTest.testTakeElementScreenshot',
+        'ChromeDriverTest.testTakeElementScreenshotInIframe',
     ]
 )
 _ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 93ab02c..535e8c0 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -185,6 +185,9 @@
 
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2611
     'CorrectEventFiringTest.testClickAnElementThatDisappear',
+
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2697
+    'CorrectEventFiringTest.testShouldEmitClickEventWhenClickingOnATextInputElement',
 ]
 
 _OS_NEGATIVE_FILTER['android:chrome_stable'] = (
diff --git a/chrome/test/data/ads_observer/ad_with_incomplete_resource.html b/chrome/test/data/ads_observer/ad_with_incomplete_resource.html
new file mode 100644
index 0000000..38a8c05
--- /dev/null
+++ b/chrome/test/data/ads_observer/ad_with_incomplete_resource.html
@@ -0,0 +1,12 @@
+<html>
+<body>
+<script src="ad_iframe_writer.js"></script>
+<!-- Should be counted as same-origin even though it's about:contents. -->
+<script>
+  let adIframe = createAdIframe();
+  // "incomplete_resource.js" should never be a complete resource load.
+  adIframe.srcdoc = "<link rel='preload' href='incomplete_resource.js' as='script'>";
+</script>
+
+</body>
+</html>
diff --git a/chrome/test/data/autofill/captured_sites/timberland.test b/chrome/test/data/autofill/captured_sites/timberland.test
index fa8a56a..12d782a 100644
--- a/chrome/test/data/autofill/captured_sites/timberland.test
+++ b/chrome/test/data/autofill/captured_sites/timberland.test
@@ -1,6 +1,7 @@
 {
   "name": "Timberland | Ferndale 22-Liter Color Block Water-Resistant Backpack",
   "startingURL": "https://www.timberland.com/webapp/wcs/stores/servlet/VFShippingAddressView?langId=-1&storeId=7101&krypto=YB39ii4Huip6o%2BGaiYkPKfqe74b928D5C%2BQc0xFg5ndVY3BEmWyC1cgbYlwZ0Q0SrYAlFHIhPekodo431Cb6gk6VuW40fwKKQdr8mAmmu9AoubZXq6YNXFvhJam9ban%2FGr9eQNmr79XJn9mkmkjDoODvqQXVgHV%2BL%2B2JOEs%2BBXhqenUPb7QqPvbixt1e1h9aEU1OyieFFCEee4a6MNqSCcwxHaSdDVSwRlBfKrZN7C3IvEpH1zrs0CRESRY8%2BPFIKQ8Yj%2F9GKsB5zaOephr8NSWfnVUO24CaZQAKW7Rb6EFPRSSF4vC%2BWmAaCmER4kKaaRajDmrPmYCat0JX5eYpFE9TxZQb9ynwCZyaEgNBBw42FCCKMy1T6Y9oNS5lGRK4Q%2FfCx6J4MXTFNQ7sXgDQv8hUEoDIch77uERil7KeU5X3xjz%2FTzjnUBZvuWactZO6yfdazfbTMeReyOtWRRPGMGV6V3RndqAchCIvrOyz%2BTJiiMnNrSWlF0ooPrQgbSzCXYt%2FXzi7BESCCrXAIi8GnHlqvBvI%2BgOAFfm7g2T%2FI%2BLclLbYsa8nlC5uGO%2BnMKKx&ddkey=https%3AVFAVSAddressAdd",
+  "dismissBeforeUnload": true,
   "autofillProfile": [
     {
       "type": "ADDRESS_HOME_CITY",
diff --git a/chrome/test/data/chromedriver/touch_action_tests.html b/chrome/test/data/chromedriver/touch_action_tests.html
index 95c89f5..6bcaf30 100644
--- a/chrome/test/data/chromedriver/touch_action_tests.html
+++ b/chrome/test/data/chromedriver/touch_action_tests.html
@@ -6,7 +6,7 @@
     <title>Touch Action Test Page</title>
   </head>
   <body>
-    <div id="target">
+    <div id="target" style="height:200px">
       Events are logged when tests touch this div.
     </div>
     <!-- use "white-space:nowrap;" to prevent page reflows while swiping, see
diff --git a/chrome/test/data/chromeos/file_manager/video_long.ogv b/chrome/test/data/chromeos/file_manager/video_long.ogv
new file mode 100644
index 0000000..eae17261
--- /dev/null
+++ b/chrome/test/data/chromeos/file_manager/video_long.ogv
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js
index 44a2b9ee..2ee7da9 100644
--- a/chrome/test/data/extensions/api_test/debugger/background.js
+++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -325,6 +325,46 @@
     });
   },
 
+  // http://crbug.com/824174
+  function getResponseBodyInvalidChar() {
+    let requestId;
+
+    function onEvent(debuggeeId, message, params) {
+      if (message === 'Network.responseReceived' &&
+          params.response.url.endsWith('invalid_char.html')) {
+        requestId = params.requestId;
+      } else if (message === 'Network.loadingFinished' &&
+                 params.requestId === requestId) {
+        chrome.debugger.sendCommand(
+            debuggeeId, 'Network.getResponseBody',
+            {requestId: params.requestId}, function(responseBody) {
+              chrome.debugger.onEvent.removeListener(onEvent);
+              chrome.debugger.detach(debuggeeId);
+              chrome.test.succeed();
+            });
+      }
+    }
+
+    chrome.debugger.onEvent.addListener(onEvent);
+    chrome.tabs.create({url: 'inspected.html'}, function(tab) {
+      const debuggee = {tabId: tab.id};
+      chrome.debugger.attach(debuggee, protocolVersion, function() {
+        chrome.debugger.sendCommand(
+            debuggee, 'Network.enable', null, function() {
+              chrome.debugger.sendCommand(
+                  debuggee, 'Page.enable', null, function() {
+                    // Navigate to a new page after attaching so we don't miss
+                    // any protocol events that we might have missed while
+                    // attaching to the first page.
+                    chrome.debugger.sendCommand(
+                        debuggee, 'Page.navigate',
+                        {url: window.location.origin + '/fetch.html'});
+                  });
+            });
+      });
+    });
+  },
+
   function offlineErrorPage() {
     const url = 'http://127.0.0.1//extensions/api_test/debugger/inspected.html';
     chrome.tabs.create({url: url}, function(tab) {
diff --git a/chrome/test/data/extensions/api_test/debugger/fetch.html b/chrome/test/data/extensions/api_test/debugger/fetch.html
new file mode 100644
index 0000000..8861b113
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/debugger/fetch.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>fetch page</title>
+    <script src="/fetch.js"></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/chrome/test/data/extensions/api_test/debugger/fetch.js b/chrome/test/data/extensions/api_test/debugger/fetch.js
new file mode 100644
index 0000000..fcadb6f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/debugger/fetch.js
@@ -0,0 +1,5 @@
+window.addEventListener('load', () => {
+  fetch('/invalid_char.html').catch(err => {
+    console.err(`fetch('/invalid_char.html') failed with error: ${err}`);
+  });
+});
diff --git a/chrome/test/data/extensions/api_test/debugger/invalid_char.html b/chrome/test/data/extensions/api_test/debugger/invalid_char.html
new file mode 100644
index 0000000..5b25430
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/debugger/invalid_char.html
@@ -0,0 +1,3 @@
+<html>
+<p>￿</p>
+</html>
diff --git a/chrome/test/data/pdf/annotations_feature_enabled_test.js b/chrome/test/data/pdf/annotations_feature_enabled_test.js
index 65d7bf2..181765b 100644
--- a/chrome/test/data/pdf/annotations_feature_enabled_test.js
+++ b/chrome/test/data/pdf/annotations_feature_enabled_test.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+window.onerror = e => chrome.test.fail(e.stack);
+window.onunhandledrejection = e => chrome.test.fail(e.reason);
+
 function animationFrame() {
   return new Promise(resolve => requestAnimationFrame(resolve));
 }
@@ -19,7 +22,7 @@
     await f();
     chrome.test.succeed();
   } catch (e) {
-    chrome.test.fail(e);
+    chrome.test.fail(e.stack);
   }
 }
 
@@ -140,6 +143,7 @@
     testAsync(async () => {
       chrome.test.assertTrue(isAnnotationMode());
       const inkHost = contentElement();
+      inkHost.resetPenMode();
       const events = [];
       inkHost.ink_.dispatchPointerEvent = (type, init) =>
           events.push({type: type, init: init});
@@ -179,6 +183,7 @@
       ]);
 
       // Multi-touch gesture should cancel and suppress first pointer.
+      inkHost.resetPenMode();
       inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
       inkHost.dispatchEvent(new PointerEvent('pointerdown', touch2));
       inkHost.dispatchEvent(new PointerEvent('pointermove', touch1));
@@ -189,6 +194,7 @@
       ]);
 
       // Pointers which are not active should be suppressed.
+      inkHost.resetPenMode();
       inkHost.dispatchEvent(new PointerEvent('pointerdown', mouse));
       inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
       inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
@@ -221,6 +227,7 @@
       ]);
 
       // Browser will cancel touch on pen input
+      inkHost.resetPenMode();
       inkHost.dispatchEvent(new PointerEvent('pointerdown', touch1));
       inkHost.dispatchEvent(new PointerEvent('pointercancel', touch1));
       inkHost.dispatchEvent(new PointerEvent('pointerdown', pen));
@@ -233,17 +240,89 @@
       ]);
     });
   },
-  function testPreventDefaultTouchStart() {
+  function testTouchPanGestures() {
     testAsync(async () => {
+      // Ensure that we have an out-of-bounds area.
+      viewer.viewport_.setZoom(0.5);
       chrome.test.assertTrue(isAnnotationMode());
       const inkHost = contentElement();
-      let called = false;
-      inkHost.onTouchStart_({
-        preventDefault() {
-          called = true;
-        }
-      });
-      chrome.test.assertTrue(called);
+      function dispatchPointerEvent(type, init) {
+        const pointerEvent = new PointerEvent(type, init);
+        inkHost.dispatchEvent(pointerEvent);
+        return pointerEvent;
+      }
+      function dispatchPointerAndTouchEvents(type, init) {
+        const pointerEvent = dispatchPointerEvent(type, init);
+        let touchPrevented = false;
+        inkHost.onTouchStart_({
+          timeStamp: pointerEvent.timeStamp,
+          preventDefault() {
+            touchPrevented = true;
+          }
+        });
+        return touchPrevented;
+      }
+
+      const pen = {
+        pointerId: 2,
+        pointerType: 'pen',
+        pressure: 0.5,
+        clientX: innerWidth / 2,
+        clientY: innerHeight / 2,
+      };
+
+      const outOfBoundsPen = {
+        pointerId: 2,
+        pointerType: 'pen',
+        pressure: 0.5,
+        clientX: 2,
+        clientY: 3,
+      };
+
+      const touch = {
+        pointerId: 3,
+        pointerType: 'touch',
+        pressure: 0.5,
+        clientX: innerWidth / 2,
+        clientY: innerHeight / 2,
+      };
+
+      const outOfBoundsTouch = {
+        pointerId: 4,
+        pointerType: 'touch',
+        pressure: 0.5,
+        clientX: 4,
+        clientY: 5,
+      };
+
+      inkHost.resetPenMode();
+      let prevented = dispatchPointerAndTouchEvents('pointerdown', touch);
+      dispatchPointerEvent('pointerup', touch);
+      chrome.test.assertTrue(
+          prevented, 'in document touch should prevent default');
+
+      prevented =
+          dispatchPointerAndTouchEvents('pointerdown', outOfBoundsTouch);
+      dispatchPointerEvent('pointerup', outOfBoundsTouch);
+      chrome.test.assertFalse(
+          prevented, 'out of bounds touch should start gesture');
+
+      prevented = dispatchPointerAndTouchEvents('pointerdown', pen);
+      dispatchPointerEvent('pointerup', pen);
+      chrome.test.assertTrue(
+          prevented, 'in document pen should prevent default');
+
+      prevented = dispatchPointerAndTouchEvents('pointerdown', outOfBoundsPen);
+      dispatchPointerEvent('pointerup', outOfBoundsPen);
+      chrome.test.assertFalse(
+          prevented, 'out of bounds pen should start gesture');
+
+      chrome.test.assertTrue(
+          inkHost.penMode_, 'pen input should switch to pen mode');
+      prevented = dispatchPointerAndTouchEvents('pointerdown', touch);
+      dispatchPointerEvent('pointerup', touch);
+      chrome.test.assertFalse(
+          prevented, 'in document touch in pen mode should start gesture');
     });
   },
   function testExitAnnotationMode() {
diff --git a/chrome/test/data/pdf/test-offset-cross-site-iframe.html b/chrome/test/data/pdf/test-offset-cross-site-iframe.html
new file mode 100644
index 0000000..d4e2860
--- /dev/null
+++ b/chrome/test/data/pdf/test-offset-cross-site-iframe.html
@@ -0,0 +1,2 @@
+<iframe src="/cross-site/1.com/pdf/test-bookmarks.pdf"
+        style="top: 400px; left: 100px; height: 30px; width: 30px"></iframe>
diff --git a/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js b/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
index 1c9e458..bbafece 100644
--- a/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
+++ b/chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js
@@ -55,8 +55,8 @@
               // As coordinates, use the center of the element, minus the
               // window offset in case the element is outside the view.
               rect.left + rect.width / 2, rect.top + rect.height / 2);
-          isReady &= target.contains(topElement) ||
-                     target.isSameNode(topElement);
+          isReady = target.contains(topElement) ||
+                      target.isSameNode(topElement);
         }
       }
 
diff --git a/chrome/test/data/webui/certificate_viewer_dialog_test.js b/chrome/test/data/webui/certificate_viewer_dialog_test.js
index 833c31e..1fadb46 100644
--- a/chrome/test/data/webui/certificate_viewer_dialog_test.js
+++ b/chrome/test/data/webui/certificate_viewer_dialog_test.js
@@ -75,24 +75,6 @@
   }
 };
 
-/**
- * Test fixture for ChromeOS modal dialog version of certificate viewer.
- * @extends {CertificateViewerUITest}
- */
-function CertificateViewerModalUITest() {}
-
-CertificateViewerModalUITest.prototype = {
-  __proto__: CertificateViewerUITest.prototype,
-
-  /**
-   * Show the certificate viewer dialog.
-   */
-  testGenPreamble: function() {
-    GEN('#if defined(OS_CHROMEOS)');
-    GEN('ShowModalCertificateViewer();');
-    GEN('#endif');
-  },
-};
 
 /**
  * Test fixture for asynchronous tests.
@@ -108,19 +90,6 @@
 };
 
 
-/**
- * Test fixture for ChromeOS modal dialog version for asynchronous tests.
- * @extends {CertificateViewerUITest}
- */
-function CertificateViewerModalUITestAsync() {}
-
-CertificateViewerModalUITestAsync.prototype = {
-  __proto__: CertificateViewerModalUITest.prototype,
-
-  /** @inheritDoc */
-  isAsync: true,
-};
-
 // Include the bulk of c++ code.
 // Certificate viewer UI tests are disabled on platforms with native certificate
 // viewers.
@@ -154,27 +123,6 @@
   this.testDetails();
 });
 
-// Same tests as above but running within modal (instead of constrained) dialog
-// version of certificate viewer UI.
-GEN('#if defined(OS_CHROMEOS)');
-
-TEST_F('CertificateViewerModalUITest', 'testDialogURL', function() {
-  this.testDialogUrl();
-});
-
-TEST_F('CertificateViewerModalUITest', 'testCN', function() {
-  this.testCN();
-});
-
-/**
- * Disabled due to flakiness on Linux ChromiumOS bot: http://crbug/669597
- */
-TEST_F('CertificateViewerModalUITestAsync', 'DISABLED_testDetails', function() {
-  this.testDetails();
-});
-
-GEN('#endif');
-
 ////////////////////////////////////////////////////////////////////////////////
 // Support functions
 
diff --git a/chrome/test/data/webui/certificate_viewer_ui_test-inl.h b/chrome/test/data/webui/certificate_viewer_ui_test-inl.h
index 705749f..ef83f514 100644
--- a/chrome/test/data/webui/certificate_viewer_ui_test-inl.h
+++ b/chrome/test/data/webui/certificate_viewer_ui_test-inl.h
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
+#ifndef CHROME_TEST_DATA_WEBUI_CERTIFICATE_VIEWER_UI_TEST_INL_H_
+#define CHROME_TEST_DATA_WEBUI_CERTIFICATE_VIEWER_UI_TEST_INL_H_
+
 #include "chrome/browser/certificate_viewer.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/certificate_viewer_webui.h"
-#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/base/web_ui_browser_test.h"
@@ -26,18 +27,9 @@
   ~CertificateViewerUITest() override;
 
  protected:
-  void CreateCertViewerDialog();
   void ShowCertificateViewer();
-#if defined(OS_CHROMEOS)
-  void ShowModalCertificateViewer();
-#endif
 };
 
-
-void CertificateViewerUITest::CreateCertViewerDialog() {
-
-}
-
 void CertificateViewerUITest::ShowCertificateViewer() {
   net::ScopedCERTCertificate google_cert(
       net::x509_util::CreateCERTCertificateFromBytes(google_der,
@@ -49,12 +41,10 @@
   ASSERT_TRUE(browser());
   ASSERT_TRUE(browser()->window());
 
-  CertificateViewerDialog* dialog =
-      new CertificateViewerDialog(std::move(certs));
-  dialog->Show(browser()->tab_strip_model()->GetActiveWebContents(),
-               browser()->window()->GetNativeWindow());
-  content::WebContents* webui_webcontents =
-      dialog->GetWebUI()->GetWebContents();
+  CertificateViewerDialog* dialog = CertificateViewerDialog::ShowConstrained(
+      std::move(certs), browser()->tab_strip_model()->GetActiveWebContents(),
+      browser()->window()->GetNativeWindow());
+  content::WebContents* webui_webcontents = dialog->webui_->GetWebContents();
   content::WaitForLoadStop(webui_webcontents);
   content::WebUI* webui = webui_webcontents->GetWebUI();
   webui_webcontents->GetRenderViewHost()->SetWebUIProperty(
@@ -62,28 +52,4 @@
   SetWebUIInstance(webui);
 }
 
-#if defined(OS_CHROMEOS)
-void CertificateViewerUITest::ShowModalCertificateViewer() {
-  net::ScopedCERTCertificate google_cert(
-      net::x509_util::CreateCERTCertificateFromBytes(google_der,
-                                                     sizeof(google_der)));
-  ASSERT_TRUE(google_cert);
-  net::ScopedCERTCertificateList certs;
-  certs.push_back(net::x509_util::DupCERTCertificate(google_cert.get()));
-
-  ASSERT_TRUE(browser());
-  ASSERT_TRUE(browser()->window());
-
-  CertificateViewerModalDialog* dialog =
-      new CertificateViewerModalDialog(std::move(certs));
-  dialog->Show(browser()->tab_strip_model()->GetActiveWebContents(),
-               browser()->window()->GetNativeWindow());
-  content::WebContents* webui_webcontents =
-      dialog->GetWebUI()->GetWebContents();
-  content::WaitForLoadStop(webui_webcontents);
-  content::WebUI* webui = webui_webcontents->GetWebUI();
-  webui_webcontents->GetRenderViewHost()->SetWebUIProperty(
-      "expectedUrl", chrome::kChromeUICertificateViewerDialogURL);
-  SetWebUIInstance(webui);
-}
-#endif
+#endif  // CHROME_TEST_DATA_WEBUI_CERTIFICATE_VIEWER_UI_TEST_INL_H_
diff --git a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.js b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.js
index ae8ec5ed..e4cd58d1 100644
--- a/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_searchable_drop_down_tests.js
@@ -100,4 +100,36 @@
     search('ta');
     assertEquals('ta', dropDown.value);
   });
+
+  // If the error-message-allowed flag is passed and the |errorMessage| property
+  // is set, then the error message should be displayed. If the |errorMessage|
+  // property is not set or |errorMessageAllowed| is false, no error message
+  // should be displayed.
+  test('error message is displayed if set and allowed', function() {
+    dropDown.errorMessageAllowed = true;
+    dropDown.errorMessage = 'error message';
+
+    const input = dropDown.$.search;
+
+    assertEquals(dropDown.errorMessage, input.$.error.textContent);
+    assertTrue(input.invalid);
+
+    // Set |errorMessageAllowed| to false and verify no error message is shown.
+    dropDown.errorMessageAllowed = false;
+
+    assertNotEquals(dropDown.errorMessage, input.$.error.textContent);
+    assertFalse(input.invalid);
+
+    // Set |errorMessageAllowed| to true and verify it is displayed again.
+    dropDown.errorMessageAllowed = true;
+
+    assertEquals(dropDown.errorMessage, input.$.error.textContent);
+    assertTrue(input.invalid);
+
+    // Clearing |errorMessage| hides the error.
+    dropDown.errorMessage = '';
+
+    assertEquals(dropDown.errorMessage, input.$.error.textContent);
+    assertFalse(input.invalid);
+  });
 });
diff --git a/chrome/test/data/webui/extensions/activity_log_item_test.js b/chrome/test/data/webui/extensions/activity_log_item_test.js
index e4cc3dc..385539cf 100644
--- a/chrome/test/data/webui/extensions/activity_log_item_test.js
+++ b/chrome/test/data/webui/extensions/activity_log_item_test.js
@@ -12,23 +12,23 @@
   let testVisible;
 
   /**
-   * ApiGroup data for the activityLogItem
-   * @type {extensions.ApiGroup}
+   * ActivityGroup data for the activityLogItem
+   * @type {extensions.ActivityGroup}
    */
-  let testApiGroup;
+  let testActivityGroup;
 
   // Initialize an extension activity log item before each test.
   setup(function() {
     PolymerTest.clearBody();
-    testApiGroup = {
-      apiCall: 'i18n.getUILanguage',
+    testActivityGroup = {
+      key: 'i18n.getUILanguage',
       count: 1,
       activityType: chrome.activityLogPrivate.ExtensionActivityFilter.API_CALL,
       countsByUrl: new Map()
     };
 
     activityLogItem = new extensions.ActivityLogItem();
-    activityLogItem.data = testApiGroup;
+    activityLogItem.data = testActivityGroup;
     testVisible = extension_test_util.testVisible.bind(null, activityLogItem);
 
     document.body.appendChild(activityLogItem);
@@ -69,14 +69,15 @@
   test('count not shown when there is only 1 page url', function() {
     const countsByUrl = new Map([['google.com', 1]]);
 
-    testApiGroup = {
-      apiCall: 'Storage.getItem',
+    testActivityGroup = {
+      key: 'Storage.getItem',
       count: 3,
       activityType:
           chrome.activityLogPrivate.ExtensionActivityFilter.DOM_ACCESS,
       countsByUrl
     };
-    activityLogItem.set('data', testApiGroup);
+
+    activityLogItem.set('data', testActivityGroup);
     activityLogItem.$$('#activity-item-main-row').click();
 
     Polymer.dom.flush();
@@ -90,14 +91,14 @@
     const countsByUrl =
         new Map([['google.com', 5], ['chrome://extensions', 10]]);
 
-    testApiGroup = {
-      apiCall: 'Storage.getItem',
+    testActivityGroup = {
+      key: 'Storage.getItem',
       count: 15,
       activityType:
           chrome.activityLogPrivate.ExtensionActivityFilter.DOM_ACCESS,
       countsByUrl
     };
-    activityLogItem.set('data', testApiGroup);
+    activityLogItem.set('data', testActivityGroup);
     activityLogItem.$$('#activity-item-main-row').click();
 
     Polymer.dom.flush();
diff --git a/chrome/test/data/webui/extensions/activity_log_test.js b/chrome/test/data/webui/extensions/activity_log_test.js
index ac0cb98..be4203b1 100644
--- a/chrome/test/data/webui/extensions/activity_log_test.js
+++ b/chrome/test/data/webui/extensions/activity_log_test.js
@@ -54,6 +54,66 @@
     ]
   };
 
+  // Sample activities representing content script invocations. Activities with
+  // missing args will not be processed.
+  const testContentScriptActivities = {
+    activities: [
+      {
+        activityId: '288',
+        activityType: 'content_script',
+        apiCall: '',
+        args: `["script1.js","script2.js"]`,
+        count: 1,
+        extensionId: EXTENSION_ID,
+        pageTitle: 'Test Extension',
+        pageUrl: 'https://www.google.com/search'
+      },
+      {
+        activityId: '290',
+        activityType: 'content_script',
+        apiCall: '',
+        count: 1,
+        extensionId: EXTENSION_ID,
+        pageTitle: 'Test Extension',
+        pageUrl: 'https://www.google.com/search'
+      },
+    ]
+  };
+
+  // Sample activities representing web requests. Activities with valid fields
+  // in other.webRequest should be split into multiple entries; one for every
+  // field. Activities with empty fields will have the group name be just the
+  // web request API call.
+  const testWebRequestActivities = {
+    activities: [
+      {
+        activityId: '1337',
+        activityType: 'web_request',
+        apiCall: 'webRequest.onBeforeSendHeaders',
+        args: 'null',
+        count: 300,
+        extensionId: EXTENSION_ID,
+        other: {
+          webRequest:
+              `{"modified_request_headers":true, "added_request_headers":"a"}`
+        },
+        pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
+        time: 1546499283237.616
+      },
+      {
+        activityId: '1339',
+        activityType: 'web_request',
+        apiCall: 'webRequest.noWebRequestObject',
+        args: 'null',
+        count: 3,
+        extensionId: EXTENSION_ID,
+        other: {},
+        pageUrl: `chrome-extension://${EXTENSION_ID}/index.html`,
+        time: 1546499283237.616
+      },
+    ]
+  };
+
   // Initialize an extension activity log before each test.
   setup(function() {
     PolymerTest.clearBody();
@@ -90,14 +150,65 @@
     // file because the logic to group activity log items by their API call
     // is in activity_log.js.
     expectEquals(
-        activityLogItems[0].$$('#api-call').innerText, 'i18n.getUILanguage');
+        activityLogItems[0].$$('#activity-key').innerText,
+        'i18n.getUILanguage');
     expectEquals(activityLogItems[0].$$('#activity-count').innerText, '40');
 
     expectEquals(
-        activityLogItems[1].$$('#api-call').innerText, 'Storage.getItem');
+        activityLogItems[1].$$('#activity-key').innerText, 'Storage.getItem');
     expectEquals(activityLogItems[1].$$('#activity-count').innerText, '35');
   });
 
+  test('script names shown for content script activities', function() {
+    proxyDelegate.resetResolver('getExtensionActivityLog');
+    proxyDelegate.testActivities = testContentScriptActivities;
+
+    activityLog.refreshActivities().then(() => {
+      Polymer.dom.flush();
+      const activityLogItems =
+          activityLog.shadowRoot.querySelectorAll('activity-log-item');
+
+      // One activity should be shown for each content script name.
+      expectEquals(activityLogItems.length, 2);
+
+      expectEquals(
+          activityLogItems[0].$$('#activity-key').innerText, 'script1.js');
+      expectEquals(
+          activityLogItems[1].$$('#activity-key').innerText, 'script2.js');
+    });
+  });
+
+  test('other.webRequest fields shown for web request activities', function() {
+    proxyDelegate.resetResolver('getExtensionActivityLog');
+    proxyDelegate.testActivities = testWebRequestActivities;
+
+    activityLog.refreshActivities().then(() => {
+      Polymer.dom.flush();
+      const activityLogItems =
+          activityLog.shadowRoot.querySelectorAll('activity-log-item');
+
+      // First activity should be split into two groups as it has two actions
+      // recorded in the other.webRequest object. We display the names of these
+      // actions along with the API call. Second activity should fall back
+      // to using just the API call as the key. Hence we end up with three
+      // activity log items.
+      const expectedItemKeys = [
+        'webRequest.onBeforeSendHeaders (added_request_headers)',
+        'webRequest.onBeforeSendHeaders (modified_request_headers)',
+        'webRequest.noWebRequestObject'
+      ];
+      const expectedNumItems = expectedItemKeys.length;
+
+      expectEquals(activityLogItems.length, expectedNumItems);
+
+      for (let idx = 0; idx < expectedNumItems; ++idx) {
+        expectEquals(
+            activityLogItems[idx].$$('#activity-key').innerText,
+            expectedItemKeys[idx]);
+      }
+    });
+  });
+
   test('activities shown match search query', function() {
     const search = activityLog.$$('cr-search-field');
     assertTrue(!!search);
@@ -117,7 +228,7 @@
           // activity log entries are grouped by their API call.
           expectEquals(activityLogItems.length, 1);
           expectEquals(
-              activityLogItems[0].$$('#api-call').innerText,
+              activityLogItems[0].$$('#activity-key').innerText,
               'i18n.getUILanguage');
 
           // Change search query so no results match.
@@ -202,7 +313,7 @@
       currentPage = newPage;
     });
 
-    activityLog.$$('#close-button').click();
+    activityLog.$$('#closeButton').click();
     expectDeepEquals(
         currentPage, {page: Page.DETAILS, extensionId: EXTENSION_ID});
   });
diff --git a/chrome/test/data/webui/extensions/detail_view_test.js b/chrome/test/data/webui/extensions/detail_view_test.js
index fd121c9..6ef4655 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.js
+++ b/chrome/test/data/webui/extensions/detail_view_test.js
@@ -116,10 +116,10 @@
       item.set('data.optionsPage', {openInTab: true, url: optionsUrl});
       expectTrue(testIsVisible('#extensions-options'));
 
-      expectFalse(testIsVisible('#extensions-activity-log-link'));
+      expectFalse(testIsVisible('#extensionsActivityLogLink'));
       item.set('showActivityLog', true);
       Polymer.dom.flush();
-      expectTrue(testIsVisible('#extensions-activity-log-link'));
+      expectTrue(testIsVisible('#extensionsActivityLogLink'));
 
       item.set('data.manifestHomePageUrl', 'http://example.com');
       Polymer.dom.flush();
@@ -250,7 +250,7 @@
       // redirect the page back to the details view is in manager.js.
       // Since this behavior does not happen in the testing environment,
       // we test the behavior in manager_test.js.
-      MockInteractions.tap(item.$$('#extensions-activity-log-link'));
+      MockInteractions.tap(item.$$('#extensionsActivityLogLink'));
       expectDeepEquals(
           currentPage,
           {page: Page.ACTIVITY_LOG, extensionId: extensionData.id});
diff --git a/chrome/test/data/webui/md_history/history_list_test.js b/chrome/test/data/webui/md_history/history_list_test.js
index be3fba5c..1cbbedb 100644
--- a/chrome/test/data/webui/md_history/history_list_test.js
+++ b/chrome/test/data/webui/md_history/history_list_test.js
@@ -269,14 +269,14 @@
       const item = element.$$('history-item');
       assertTrue(item.isCardStart);
       const heading = item.$$('#date-accessed').textContent;
-      const title = item.$.title;
+      const title = item.$.link;
 
       // Check that the card title displays the search term somewhere.
       const index = heading.indexOf('Google');
       assertTrue(index != -1);
 
       // Check that the search term is bolded correctly in the history-item.
-      assertGT(title.children[0].innerHTML.indexOf('<b>google</b>'), -1);
+      assertGT(title.children[1].innerHTML.indexOf('<b>google</b>'), -1);
     });
   });
 
@@ -562,7 +562,7 @@
 
           return new Promise(resolve => {
             registerMessageCallback('navigateToUrl', this, resolve);
-            items[0].$.title.click();
+            items[0].$.link.click();
           });
         })
         .then(function(info) {
diff --git a/chrome/test/data/webui/md_history/history_metrics_test.js b/chrome/test/data/webui/md_history/history_metrics_test.js
index d28e9f8..eda761b7 100644
--- a/chrome/test/data/webui/md_history/history_metrics_test.js
+++ b/chrome/test/data/webui/md_history/history_metrics_test.js
@@ -95,7 +95,7 @@
           const items = polymerSelectAll(app.$.history, 'history-item');
           MockInteractions.tap(items[1].$$('#bookmark-star'));
           assertEquals(1, actionMap['BookmarkStarClicked']);
-          MockInteractions.tap(items[1].$.title);
+          MockInteractions.tap(items[1].$.link);
           assertEquals(1, actionMap['EntryLinkClick']);
           assertEquals(1, histogramMap['HistoryPage.ClickPosition'][1]);
           assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset'][1]);
@@ -112,7 +112,7 @@
         })
         .then(() => {
           items = polymerSelectAll(app.$.history, 'history-item');
-          MockInteractions.tap(items[0].$.title);
+          MockInteractions.tap(items[0].$.link);
           assertEquals(1, actionMap['SearchResultClick']);
           assertEquals(1, histogramMap['HistoryPage.ClickPosition'][0]);
           assertEquals(1, histogramMap['HistoryPage.ClickPositionSubset'][0]);
@@ -186,7 +186,7 @@
           assertEquals(1, histogram[SyncedTabsHistogram.COLLAPSE_SESSION]);
           MockInteractions.tap(cards[0].$['card-heading']);
           assertEquals(1, histogram[SyncedTabsHistogram.EXPAND_SESSION]);
-          MockInteractions.tap(polymerSelectAll(cards[0], '.website-title')[0]);
+          MockInteractions.tap(polymerSelectAll(cards[0], '.website-link')[0]);
           assertEquals(1, histogram[SyncedTabsHistogram.LINK_CLICKED]);
 
           menuButton = cards[0].$['menu-button'];
diff --git a/chrome/test/data/webui/md_history/history_synced_tabs_test.js b/chrome/test/data/webui/md_history/history_synced_tabs_test.js
index 83282b0..d9c94496 100644
--- a/chrome/test/data/webui/md_history/history_synced_tabs_test.js
+++ b/chrome/test/data/webui/md_history/history_synced_tabs_test.js
@@ -45,7 +45,6 @@
           'http://www.google.com',
           Polymer.dom(card.root)
               .querySelectorAll('.website-title')[0]
-              .children[0]
               .textContent.trim());
       assertEquals(2, card.tabs.length);
     });
@@ -111,7 +110,6 @@
               'http://crbug.com/new',
               Polymer.dom(cards[0].root)
                   .querySelectorAll('.website-title')[1]
-                  .children[0]
                   .textContent.trim());
         });
   });
@@ -160,7 +158,6 @@
               'http://www.google.com',
               Polymer.dom(cards[0].root)
                   .querySelectorAll('.website-title')[0]
-                  .children[0]
                   .textContent.trim());
 
           element.searchTerm = 'Sans';
diff --git a/chrome/test/data/webui/md_history/md_history_browsertest.js b/chrome/test/data/webui/md_history/md_history_browsertest.js
index eafbe18..be6f995c 100644
--- a/chrome/test/data/webui/md_history/md_history_browsertest.js
+++ b/chrome/test/data/webui/md_history/md_history_browsertest.js
@@ -232,3 +232,17 @@
 TEST_F('MaterialHistoryToolbarTest', 'All', function() {
   mocha.run();
 });
+
+function MaterialHistorySearchedLabelTest() {}
+
+MaterialHistorySearchedLabelTest.prototype = {
+  __proto__: MaterialHistoryBrowserTest.prototype,
+
+  extraLibraries: MaterialHistoryBrowserTest.prototype.extraLibraries.concat([
+    'searched_label_test.js',
+  ]),
+};
+
+TEST_F('MaterialHistorySearchedLabelTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/md_history/md_history_focus_test.js b/chrome/test/data/webui/md_history/md_history_focus_test.js
index a5753bd..f4a4c00e 100644
--- a/chrome/test/data/webui/md_history/md_history_focus_test.js
+++ b/chrome/test/data/webui/md_history/md_history_focus_test.js
@@ -134,14 +134,14 @@
             MockInteractions.pressAndReleaseKeyOn(
                 focused, 39, [], 'ArrowRight');
             Polymer.dom.flush();
-            focused = items[2].$.title;
+            focused = items[2].$.link;
             assertEquals(focused, element.lastFocused_);
             assertTrue(items[2].row_.isActive());
             assertFalse(items[3].row_.isActive());
 
             MockInteractions.pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
             Polymer.dom.flush();
-            focused = items[3].$.title;
+            focused = items[3].$.link;
             assertEquals(focused, element.lastFocused_);
             assertFalse(items[2].row_.isActive());
             assertTrue(items[3].row_.isActive());
@@ -170,7 +170,7 @@
 
             MockInteractions.pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
             Polymer.dom.flush();
-            focused = items[3].$.title;
+            focused = items[3].$.link;
             assertEquals(focused, element.lastFocused_);
             assertFalse(items[2].row_.isActive());
             assertTrue(items[3].row_.isActive());
@@ -223,7 +223,7 @@
 
             // Go to the first url.
             MockInteractions.pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
-            focused = polymerSelectAll(cards[0], '.website-title')[0];
+            focused = polymerSelectAll(cards[0], '.website-link')[0];
             assertEquals(focused, lastFocused);
 
             // Collapse the first card.
@@ -247,7 +247,7 @@
           .then(function() {
             // First card's urls are focusable again.
             MockInteractions.pressAndReleaseKeyOn(focused, 40, [], 'ArrowDown');
-            focused = polymerSelectAll(cards[0], '.website-title')[0];
+            focused = polymerSelectAll(cards[0], '.website-link')[0];
             assertEquals(focused, lastFocused);
 
             // Remove the second URL from the first card.
@@ -264,7 +264,7 @@
             assertEquals(focused, lastFocused);
 
             MockInteractions.pressAndReleaseKeyOn(focused, 38, [], 'ArrowUp');
-            focused = polymerSelectAll(cards[0], '.website-title')[0];
+            focused = polymerSelectAll(cards[0], '.website-link')[0];
             assertEquals(focused, lastFocused);
 
             // Remove the second card.
diff --git a/chrome/test/data/webui/md_history/searched_label_test.js b/chrome/test/data/webui/md_history/searched_label_test.js
new file mode 100644
index 0000000..c4c5f900
--- /dev/null
+++ b/chrome/test/data/webui/md_history/searched_label_test.js
@@ -0,0 +1,31 @@
+// 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.
+
+suite('<history-searched-label> unit test', function() {
+  /** @type {?HistorySearchedLabelElement} */
+  let label;
+
+  setup(function() {
+    label = document.createElement('history-searched-label');
+    replaceBody(label);
+  });
+
+  test('matching query sets bold', function() {
+    assertEquals(0, document.querySelectorAll('b').length);
+    // Note: When the page is reloaded with a search query, |searchTerm| will be
+    // initialized before |title|. Keep this ordering as a regression test for
+    // https://crbug.com/921455.
+    label.searchTerm = 'f';
+    label.title = 'foo';
+
+    Polymer.dom.flush();
+    const boldItems = document.querySelectorAll('b');
+    assertEquals(1, boldItems.length);
+    assertEquals(label.searchTerm, boldItems[0].textContent);
+
+    label.searchTerm = 'g';
+    Polymer.dom.flush();
+    assertEquals(0, document.querySelectorAll('b').length);
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index 41532c15..6cb2ea0 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -21,9 +21,6 @@
     /** @type {?PrintPreviewDestinationSettingsElement} */
     let destinationSettings = null;
 
-    /** @type {?PrintPreviewModelElement} */
-    let model = null;
-
     /** @type {?print_preview.NativeLayer} */
     let nativeLayer = null;
 
@@ -125,17 +122,6 @@
                              print_preview.DestinationOrigin.LOCAL;
     }
 
-    // When destination store updates the current destination, update this in
-    // model. Normally this happens in app.js.
-    function updateDestination(event) {
-      const destination = event.target.selectedDestination;
-      if (!destination) {
-        return;
-      }
-
-      model.destination = destination;
-    }
-
     /**
      * Initializes the destination store and destination settings using
      * |destinations| and |recentDestinations|.
@@ -149,9 +135,6 @@
           '' /* serializedDefaultDestinationSelectionRulesStr */,
           recentDestinations);
       destinationStore.setCloudPrintInterface(cloudPrintInterface);
-      destinationStore.addEventListener(
-          print_preview.DestinationStore.EventType.DESTINATION_SELECT,
-          destination => updateDestination(destination));
 
       // Initialize destination settings.
       const defaultId = recentDestinations.length > 0 ?
@@ -174,20 +157,6 @@
       destinationSettings.state = print_preview_new.State.READY;
       destinationSettings.cloudPrintState =
           print_preview.CloudPrintState.ENABLED;
-
-      model = document.createElement('print-preview-model');
-      model.initialized_ = true;
-      model.destination = destinationSettings.destination;
-      model.recentDestinations = destinationSettings.recentDestinations;
-      model.documentSettings = {
-        isModifiable: true,
-        hasCssMediaStyles: true,
-        hasSelection: false,
-        isScalingDisabled: false,
-      };
-      test_util.fakeDataBind(model, destinationSettings, 'destination');
-      test_util.fakeDataBind(model, destinationSettings, 'recent-destinations');
-      document.body.appendChild(model);
     }
 
     /** Simulates a user signing in to Chrome. */
@@ -212,7 +181,7 @@
      * @param {!Array<string>} expectedDestinations An array of the expected
      *     destinations in the dropdown.
      */
-    function validate(expectedDestinations) {
+    function assertDropdownItems(expectedDestinations) {
       let options =
           destinationSettings.$.destinationSelect.shadowRoot.querySelectorAll(
               'option:not([hidden])');
@@ -229,27 +198,16 @@
     test(assert(TestNames.NoRecentDestinations), function() {
       initialize();
       assertFalse(destinationSettings.$.destinationSelect.disabled);
+      assertDropdownItems(['Save as PDF/local/']);
 
-      // Wait for printer capabilities for default destination.
-      return nativeLayer.whenCalled('getPrinterCapabilities')
-          .then(() => {
-            return test_util.waitForRender(destinationSettings);
-          })
-          .then(() => {
-            validate(
-                [makeLocalDestinationKey('FooDevice'), 'Save as PDF/local/']);
-
-            // If the user is signed in, Save to Drive should be displayed.
-            signIn();
-            return test_util.waitForRender(destinationSettings);
-          })
-          .then(() => {
-            validate([
-              makeLocalDestinationKey('FooDevice'),
-              'Save as PDF/local/',
-              '__google__docs/cookies/foo@chromium.org',
-            ]);
-          });
+      // If the user is signed in, Save to Drive should be displayed.
+      signIn();
+      return test_util.waitForRender(destinationSettings).then(() => {
+        assertDropdownItems([
+          'Save as PDF/local/',
+          '__google__docs/cookies/foo@chromium.org',
+        ]);
+      });
     });
 
     // Tests that the dropdown contains the appropriate destinations when there
@@ -261,12 +219,13 @@
       initialize();
       assertFalse(destinationSettings.$.destinationSelect.disabled);
 
+      // Wait for the destinations to be inserted into the store.
       return nativeLayer.whenCalled('getPrinterCapabilities')
           .then(() => {
             return test_util.waitForRender(destinationSettings);
           })
           .then(() => {
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               makeLocalDestinationKey('ID2'),
               makeLocalDestinationKey('ID3'),
@@ -275,7 +234,7 @@
 
             // If the user is signed in, Save to Drive should be displayed.
             signIn();
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               makeLocalDestinationKey('ID2'),
               makeLocalDestinationKey('ID3'),
@@ -302,7 +261,7 @@
             return test_util.waitForRender(destinationSettings);
           })
           .then(() => {
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
@@ -310,7 +269,7 @@
 
             // If the user is signed in, Save to Drive should be displayed.
             signIn();
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
@@ -338,7 +297,7 @@
           .then(() => {
             // Google Drive does not show up even though it is recent, since the
             // user is not signed in and the destination is not available.
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               makeLocalDestinationKey('ID3'),
               'Save as PDF/local/',
@@ -346,7 +305,7 @@
 
             // If the user is signed in, Save to Drive should be displayed.
             signIn();
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               '__google__docs/cookies/foo@chromium.org',
               makeLocalDestinationKey('ID3'),
@@ -375,7 +334,7 @@
             return test_util.waitForRender(destinationSettings);
           })
           .then(() => {
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
@@ -388,21 +347,14 @@
                 print_preview.DestinationStore.EventType.DESTINATION_SELECT,
                 destinationSettings.destinationStore);
             dropdown.fire('selected-option-change', 'Save as PDF/local/');
+
+            // Ensure this fires the destination select event.
             return whenDestinationSelect;
           })
           .then(() => {
-            // Wait for the listener to run and update the UI.
-            return test_util.waitForRender(destinationSettings);
-          })
-          .then(() => {
             assertEquals(
                 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
-                destinationSettings.destination.id);
-            validate([
-              'Save as PDF/local/',
-              makeLocalDestinationKey('ID1'),
-              makeLocalDestinationKey('ID3'),
-            ]);
+                destinationSettings.destinationStore.selectedDestination_.id);
           });
     });
 
@@ -427,7 +379,7 @@
           .then(() => {
             // If the user is signed in, Save to Drive should be displayed.
             signIn();
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               '__google__docs/cookies/foo@chromium.org',
               makeLocalDestinationKey('ID3'),
@@ -446,19 +398,9 @@
             return whenDestinationSelect;
           })
           .then(() => {
-            // Wait for the listener to run and update the UI.
-            return test_util.waitForRender(destinationSettings);
-          })
-          .then(() => {
             assertEquals(
                 print_preview.Destination.GooglePromotedId.DOCS,
-                destinationSettings.destination.id);
-            validate([
-              '__google__docs/cookies/foo@chromium.org',
-              makeLocalDestinationKey('ID1'),
-              makeLocalDestinationKey('ID3'),
-              'Save as PDF/local/',
-            ]);
+                destinationSettings.destinationStore.selectedDestination_.id);
           });
     });
 
@@ -477,7 +419,7 @@
             return test_util.waitForRender(destinationSettings);
           })
           .then(() => {
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               makeLocalDestinationKey('ID2'),
               makeLocalDestinationKey('ID3'),
@@ -495,17 +437,9 @@
             return whenDestinationSelect;
           })
           .then(() => {
-            // Wait for the listener to run and update the UI.
-            return test_util.waitForRender(destinationSettings);
-          })
-          .then(() => {
-            assertEquals('ID2', destinationSettings.destination.id);
-            validate([
-              makeLocalDestinationKey('ID2'),
-              makeLocalDestinationKey('ID1'),
-              makeLocalDestinationKey('ID3'),
-              'Save as PDF/local/',
-            ]);
+            assertEquals(
+                'ID2',
+                destinationSettings.destinationStore.selectedDestination_.id);
           });
     });
 
@@ -522,7 +456,7 @@
             return test_util.waitForRender(destinationSettings);
           })
           .then(() => {
-            validate([
+            assertDropdownItems([
               makeLocalDestinationKey('ID1'),
               makeLocalDestinationKey('ID2'),
               makeLocalDestinationKey('ID3'),
diff --git a/chrome/test/data/webui/print_preview/model_test.js b/chrome/test/data/webui/print_preview/model_test.js
index b29c36f3..5756f35e 100644
--- a/chrome/test/data/webui/print_preview/model_test.js
+++ b/chrome/test/data/webui/print_preview/model_test.js
@@ -9,6 +9,7 @@
     SetPolicySettings: 'set policy settings',
     GetPrintTicket: 'get print ticket',
     GetCloudPrintTicket: 'get cloud print ticket',
+    UpdateRecentDestinations: 'update recent destinations',
   };
 
   const suiteName = 'ModelTest';
@@ -376,6 +377,55 @@
       });
       expectEquals(expectedNewTicket, newTicket);
     });
+
+    /**
+     * @param {!Array<string>} expectedDestinationIds An array of the expected
+     *     recent destination ids.
+     */
+    function assertRecentDestinations(expectedDestinationIds) {
+      assertEquals(
+          expectedDestinationIds.length, model.recentDestinations.length);
+      expectedDestinationIds.forEach((expectedId, index) => {
+        assertEquals(expectedId, model.recentDestinations[index].id);
+      });
+    }
+
+    /**
+     * Tests that the destination being set correctly updates the recent
+     * destinations array.
+     */
+    test(assert(TestNames.UpdateRecentDestinations), function() {
+      initializeModel();
+      model.applyStickySettings();
+
+      let localDestinations = [];
+      let destinations =
+          print_preview_test_utils.getDestinations(null, localDestinations);
+
+      // Recent destinations start out empty.
+      assertRecentDestinations([]);
+
+      // Simulate setting a destination.
+      model.destination = destinations[0];
+      assertRecentDestinations(['ID1']);
+
+      // Set a new destination
+      model.destination = destinations[1];
+      assertRecentDestinations(['ID2', 'ID1']);
+
+      // Reselect a recent destination. Still 2 destinations, but in a
+      // different order.
+      model.destination = destinations[0];
+      assertRecentDestinations(['ID1', 'ID2']);
+
+      // Select a third destination
+      model.destination = destinations[2];
+      assertRecentDestinations(['ID3', 'ID1', 'ID2']);
+
+      // Select a fourth destination. List does not grow.
+      model.destination = destinations[3];
+      assertRecentDestinations(['ID4', 'ID3', 'ID1']);
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index b9c75ee..75ebc7b6 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -381,6 +381,10 @@
   this.runMochaTest(model_test.TestNames.GetCloudPrintTicket);
 });
 
+TEST_F('PrintPreviewModelTest', 'UpdateRecentDestinations', function() {
+  this.runMochaTest(model_test.TestNames.UpdateRecentDestinations);
+});
+
 PrintPreviewPreviewGenerationTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
index 3a527c3..ac96c53 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -231,8 +231,8 @@
 
   /**
    * Creates 5 local destinations, adds them to |localDestinations| and
-   * sets the capabilities in |nativeLayer|.
-   * @param {!print_preview.NativeLayerStub} nativeLayer
+   * sets the capabilities in |nativeLayer|, if it is non-null.
+   * @param {?print_preview.NativeLayerStub} nativeLayer
    * @param {!Array<!print_preview.LocalDestinationInfo>} localDestinations
    * @return {!Array<!print_preview.Destination>}
    */
@@ -248,8 +248,10 @@
           const destination = new print_preview.Destination(
               info.id, print_preview.DestinationType.LOCAL, origin, info.name,
               false, print_preview.DestinationConnectionStatus.ONLINE);
-          nativeLayer.setLocalDestinationCapabilities(
-              print_preview_test_utils.getCddTemplate(info.id, info.name));
+          if (nativeLayer) {
+            nativeLayer.setLocalDestinationCapabilities(
+                print_preview_test_utils.getCddTemplate(info.id, info.name));
+          }
           localDestinations.push({printerName: info.name, deviceName: info.id});
           destinations.push(destination);
         });
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index e8401057..9228aa2 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -2207,7 +2207,7 @@
   browsePreload: 'chrome://settings/crostini_page/crostini_page.html',
 
   /** @override */
-  featureList: ['features::kExperimentalCrostiniUI', ''],
+  featureList: ['features::kCrostini', ''],
 
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js',
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index e37707a2..6e52b0a 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -297,6 +297,11 @@
       printerMakeAndModel: '',
       printerName: 'Test Printer',
       printerPPDPath: '',
+      printerPpdReference: {
+        userSuppliedPpdUrl: '',
+        effectiveMakeAndModel: '',
+        autoconf: false,
+      },
       printerProtocol: 'ipps',
       printerQueue: 'moreinfohere',
       printerStatus: '',
@@ -363,6 +368,11 @@
       printerMakeAndModel: '',
       printerName: 'printer',
       printerPPDPath: '',
+      printerPpdReference: {
+        userSuppliedPpdUrl: '',
+        effectiveMakeAndModel: '',
+        autoconf: false,
+      },
       printerProtocol: 'usb',
       printerQueue: 'moreinfohere',
       printerStatus: '',
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.js b/chrome/test/data/webui/settings/sync_account_control_test.js
index 44570a14e..745ca08 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.js
+++ b/chrome/test/data/webui/settings/sync_account_control_test.js
@@ -87,6 +87,23 @@
           });
     });
 
+    test('promo header has the correct class', function() {
+      testElement.syncStatus = {signedIn: false, signedInUsername: ''};
+      testElement.promoLabelWithNoAccount = testElement.promoLabelWithAccount =
+          'title';
+      sync_test_util.simulateStoredAccounts([]);
+      assertVisible(testElement.$$('#promo-header'), true);
+      // When there is no secondary label, the settings box is one line.
+      assertFalse(
+          testElement.$$('#promo-header').classList.contains('two-line'));
+
+      testElement.promoSecondaryLabelWithNoAccount =
+          testElement.promoSecondaryLabelWithAccount = 'subtitle';
+      // When there is a secondary label, the settings box is two line.
+      assertTrue(
+          testElement.$$('#promo-header').classList.contains('two-line'));
+    });
+
     test('not signed in and no stored accounts', function() {
       testElement.syncStatus = {signedIn: false, signedInUsername: ''};
       sync_test_util.simulateStoredAccounts([]);
diff --git a/chrome/test/data/webui/webui_resource_browsertest.cc b/chrome/test/data/webui/webui_resource_browsertest.cc
index 3905b09..9381511 100644
--- a/chrome/test/data/webui/webui_resource_browsertest.cc
+++ b/chrome/test/data/webui/webui_resource_browsertest.cc
@@ -94,7 +94,19 @@
   LoadResource(IDR_WEBUI_TEST_I18N_PROCESS_CSS_TEST);
 }
 
-IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, I18nProcessTest) {
+class WebUIResourceBrowserTestV0 : public WebUIResourceBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // TODO(yoichio): This is temporary switch to support chrome internal
+    // components migration from the old web APIs.
+    // After completion of the migration, we should remove this.
+    // See crbug.com/911943 for detail.
+    command_line->AppendSwitchASCII("enable-blink-features", "HTMLImports");
+    command_line->AppendSwitchASCII("enable-blink-features", "ShadowDOMV0");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTestV0, I18nProcessTest) {
   AddLibrary(IDR_WEBUI_JS_LOAD_TIME_DATA);
   AddLibrary(IDR_WEBUI_JS_I18N_TEMPLATE_NO_PROCESS);
   AddLibrary(IDR_WEBUI_JS_UTIL);
diff --git a/chrome/test/perf/url_parse_perftest.cc b/chrome/test/perf/url_parse_perftest.cc
deleted file mode 100644
index 34065c12..0000000
--- a/chrome/test/perf/url_parse_perftest.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/perf_time_logger.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-#include "url/third_party/mozilla/url_parse.h"
-#include "url/url_canon.h"
-#include "url/url_canon_stdstring.h"
-
-// TODO(darin): chrome code should not depend on WebCore innards
-#if 0
-#pragma warning(push, 0)
-
-// This is because we have multiple headers called "CString.h" and KURL.cpp
-// can grab the wrong one.
-#include "webkit/third_party/WebCore/platform/CString.h"
-
-#define KURL WebKitKURL
-#include "KURL.cpp"
-#include "KURL.h"
-#pragma warning(pop)
-
-TEST(URLParse, FullURL) {
-  const char url[] = "http://me:pass@host/foo/bar.html;param?query=yes#ref";
-  int url_len = static_cast<int>(strlen(url));
-
-  url::Parsed parsed;
-  base::PerfTimeLogger timer("Full_URL_Parse_AMillion");
-
-  for (int i = 0; i < 1000000; i++)
-    url::ParseStandardURL(url, url_len, &parsed);
-  timer.Done();
-}
-
-namespace {
-
-const char typical_url1[] = "http://www.google.com/search?q=url+parsing&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a";
-int typical_url1_len = static_cast<int>(strlen(typical_url1));
-
-const char typical_url2[] = "http://www.amazon.com/Stephen-King-Thrillers-Horror-People/dp/0766012336/ref=sr_1_2/133-4144931-4505264?ie=UTF8&s=books&qid=2144880915&sr=8-2";
-int typical_url2_len = static_cast<int>(strlen(typical_url2));
-
-const char typical_url3[] = "http://store.apple.com/1-800-MY-APPLE/WebObjects/AppleStore.woa/wa/RSLID?nnmm=browse&mco=578E9744&node=home/desktop/mac_pro";
-int typical_url3_len = static_cast<int>(strlen(typical_url3));
-
-}  // namespace
-
-TEST(URLParse, TypicalURLParse) {
-  url::Parsed parsed1;
-  url::Parsed parsed2;
-  url::Parsed parsed3;
-
-  // Do this 1/3 of a million times since we do 3 different URLs.
-  base::PerfTimeLogger parse_timer("Typical_URL_Parse_AMillion");
-  for (int i = 0; i < 333333; i++) {
-    url::ParseStandardURL(typical_url1, typical_url1_len, &parsed1);
-    url::ParseStandardURL(typical_url2, typical_url2_len, &parsed2);
-    url::ParseStandardURL(typical_url3, typical_url3_len, &parsed3);
-  }
-  parse_timer.Done();
-}
-
-// Includes both parsing and canonicalization with no mallocs.
-TEST(URLParse, TypicalURLParseCanon) {
-  url::Parsed parsed1;
-  url::Parsed parsed2;
-  url::Parsed parsed3;
-
-  base::PerfTimeLogger canon_timer("Typical_Parse_Canon_AMillion");
-  url::Parsed out_parsed;
-  url::RawCanonOutput<1024> output;
-  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
-    url::ParseStandardURL(typical_url1, typical_url1_len, &parsed1);
-    output.set_length(0);
-    url::CanonicalizeStandardURL(typical_url1, typical_url1_len, parsed1,
-                                       NULL, &output, &out_parsed);
-
-    url::ParseStandardURL(typical_url2, typical_url2_len, &parsed2);
-    output.set_length(0);
-    url::CanonicalizeStandardURL(typical_url2, typical_url2_len, parsed2,
-                                       NULL, &output, &out_parsed);
-
-    url::ParseStandardURL(typical_url3, typical_url3_len, &parsed3);
-    output.set_length(0);
-    url::CanonicalizeStandardURL(typical_url3, typical_url3_len, parsed3,
-                                       NULL, &output, &out_parsed);
-  }
-  canon_timer.Done();
-}
-
-// Includes both parsing and canonicalization, and mallocs for the output.
-TEST(URLParse, TypicalURLParseCanonStdString) {
-  url::Parsed parsed1;
-  url::Parsed parsed2;
-  url::Parsed parsed3;
-
-  base::PerfTimeLogger canon_timer("Typical_Parse_Canon_AMillion");
-  url::Parsed out_parsed;
-  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
-    url::ParseStandardURL(typical_url1, typical_url1_len, &parsed1);
-    std::string out1;
-    url::StdStringCanonOutput output1(&out1);
-    url::CanonicalizeStandardURL(typical_url1, typical_url1_len, parsed1,
-                                       NULL, &output1, &out_parsed);
-
-    url::ParseStandardURL(typical_url2, typical_url2_len, &parsed2);
-    std::string out2;
-    url::StdStringCanonOutput output2(&out2);
-    url::CanonicalizeStandardURL(typical_url2, typical_url2_len, parsed2,
-                                       NULL, &output2, &out_parsed);
-
-    url::ParseStandardURL(typical_url3, typical_url3_len, &parsed3);
-    std::string out3;
-    url::StdStringCanonOutput output3(&out3);
-    url::CanonicalizeStandardURL(typical_url3, typical_url3_len, parsed3,
-                                       NULL, &output3, &out_parsed);
-  }
-  canon_timer.Done();
-}
-
-TEST(URLParse, GURL) {
-  // Don't want to time creating the input strings.
-  std::string stdurl1(typical_url1);
-  std::string stdurl2(typical_url2);
-  std::string stdurl3(typical_url3);
-
-  base::PerfTimeLogger gurl_timer("Typical_GURL_AMillion");
-  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
-    GURL gurl1(stdurl1);
-    GURL gurl2(stdurl2);
-    GURL gurl3(stdurl3);
-  }
-  gurl_timer.Done();
-}
-
-// TODO(darin): chrome code should not depend on WebCore innards
-TEST(URLParse, KURL) {
-  base::PerfTimeLogger timer_kurl("Typical_KURL_AMillion");
-  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
-    WebCore::WebKitKURL kurl1(typical_url1);
-    WebCore::WebKitKURL kurl2(typical_url2);
-    WebCore::WebKitKURL kurl3(typical_url3);
-  }
-  timer_kurl.Done();
-}
-
-#endif
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index ac5b0b84..8a6f840 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -256,6 +256,10 @@
     # the cast IsSupporedVideoConfigs ignore the transfer function parameter.
     # (b/36984215).
     gtest_excludes = [ "*.IsSupportedVideoConfig_VP9TransferFunctions" ]
+    if (is_android_things) {
+      gtest_excludes +=
+          [ "FontUniqueNameLookupTest.TestMatchPostScriptNameTtc" ]
+    }
     if (target_os == "linux" && !is_cast_desktop_build) {
       # DesktopCaptureDeviceTest.*: No capture device on Eureka
       # Disable PepperGamepadHostTest.WaitForReply (pepper not supported on Eureka)
@@ -580,6 +584,7 @@
     "ENABLE_CHROMECAST_EXTENSIONS=$enable_chromecast_extensions",
     "ENABLE_HEADLESS_MUSIC_MODE=$enable_headless_music_mode",
     "ENABLE_PLAYREADY=$enable_playready",
+    "ENABLE_VIDEO_CAPTURE_SERVICE=$enable_video_capture_service",
     "ENABLE_VOLUME_TABLES_ACCESS=$enable_volume_tables_access",
     "IS_ANDROID_THINGS=$is_android_things",
     "IS_ANDROID_THINGS_NON_PUBLIC=$is_android_things_non_public",
diff --git a/chromecast/app/cast_main_delegate.cc b/chromecast/app/cast_main_delegate.cc
index 1e43be0..80e7e2f1 100644
--- a/chromecast/app/cast_main_delegate.cc
+++ b/chromecast/app/cast_main_delegate.cc
@@ -177,7 +177,7 @@
 
   // Note: Android must handle running its own browser process.
   // See ChromeMainDelegateAndroid::RunProcess.
-  browser_runner_.reset(content::BrowserMainRunner::Create());
+  browser_runner_ = content::BrowserMainRunner::Create();
   return browser_runner_->Initialize(main_function_params);
 #else
   return -1;
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index df3f8ae..935b759 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -195,6 +195,9 @@
 #if defined(OS_ANDROID)
         // TODO(awolter): Remove this once the feature is on by default.
         features::kAudioServiceAudioStreams,
+#if BUILDFLAG(ENABLE_VIDEO_CAPTURE_SERVICE)
+        features::kMojoVideoCapture,
+#endif  // BUILDFLAG(ENABLE_VIDEO_CAPTURE_SERVICE)
 #endif
   });
 
diff --git a/chromecast/cast_shell_sandbox_policy b/chromecast/cast_shell_sandbox_policy
index 9e81d3f..9b79c65 100644
--- a/chromecast/cast_shell_sandbox_policy
+++ b/chromecast/cast_shell_sandbox_policy
@@ -11,6 +11,7 @@
       "fuchsia.net.LegacySocketProvider",
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
+      "fuchsia.sys.Launcher",
       "fuchsia.ui.input.ImeService",
       "fuchsia.ui.input.ImeVisibilityService",
       "fuchsia.ui.policy.Presenter",
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni
index d23449b..f1063f0b 100644
--- a/chromecast/chromecast.gni
+++ b/chromecast/chromecast.gni
@@ -109,6 +109,9 @@
   # that do not have a mixer in the CMA backend.
   enable_cast_audio_manager_mixer =
       chromecast_branding == "google" && !is_cast_audio_only && !is_android
+
+  # Set to true to enable video capture service for video input and output.
+  enable_video_capture_service = false
 }
 
 declare_args() {
diff --git a/chromecast/graphics/cast_focus_client_aura.cc b/chromecast/graphics/cast_focus_client_aura.cc
index fb339d10..8b91b46 100644
--- a/chromecast/graphics/cast_focus_client_aura.cc
+++ b/chromecast/graphics/cast_focus_client_aura.cc
@@ -207,7 +207,8 @@
   return next;
 }
 
-aura::Window* CastFocusClientAura::GetZOrderWindow(aura::Window* window) {
+const aura::Window* CastFocusClientAura::GetZOrderWindow(
+    const aura::Window* window) const {
   while (window->parent() && !window->parent()->IsRootWindow()) {
     window = window->parent();
   }
@@ -235,15 +236,17 @@
   return nullptr;
 }
 
-aura::Window* CastFocusClientAura::GetActivatableWindow(aura::Window* window) {
+aura::Window* CastFocusClientAura::GetActivatableWindow(
+    aura::Window* window) const {
   return window;
 }
 
-aura::Window* CastFocusClientAura::GetToplevelWindow(aura::Window* window) {
+const aura::Window* CastFocusClientAura::GetToplevelWindow(
+    const aura::Window* window) const {
   return GetZOrderWindow(window);
 }
 
-bool CastFocusClientAura::CanActivateWindow(aura::Window* window) const {
+bool CastFocusClientAura::CanActivateWindow(const aura::Window* window) const {
   return true;
 }
 
diff --git a/chromecast/graphics/cast_focus_client_aura.h b/chromecast/graphics/cast_focus_client_aura.h
index 0cd659a7..ec4f8d6 100644
--- a/chromecast/graphics/cast_focus_client_aura.h
+++ b/chromecast/graphics/cast_focus_client_aura.h
@@ -40,9 +40,10 @@
   void ActivateWindow(aura::Window* window) override;
   void DeactivateWindow(aura::Window* window) override;
   const aura::Window* GetActiveWindow() const override;
-  aura::Window* GetActivatableWindow(aura::Window* window) override;
-  aura::Window* GetToplevelWindow(aura::Window* window) override;
-  bool CanActivateWindow(aura::Window* window) const override;
+  aura::Window* GetActivatableWindow(aura::Window* window) const override;
+  const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const override;
+  bool CanActivateWindow(const aura::Window* window) const override;
 
  private:
   // aura::WindowObserver implementation:
@@ -57,7 +58,11 @@
   // Get the top-most window in a window's hierarchy to determine z order.
   // This is the window directly under the root window (a child window of the
   // root window).
-  aura::Window* GetZOrderWindow(aura::Window* window);
+  aura::Window* GetZOrderWindow(aura::Window* window) const {
+    return const_cast<aura::Window*>(
+        GetZOrderWindow(const_cast<const aura::Window*>(window)));
+  }
+  const aura::Window* GetZOrderWindow(const aura::Window* window) const;
 
   base::ObserverList<aura::client::FocusChangeObserver>::Unchecked
       focus_observers_;
diff --git a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
index dac62ab..2e26ce8e 100644
--- a/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
+++ b/chromeos/components/proximity_auth/smart_lock_metrics_recorder.h
@@ -71,7 +71,7 @@
     kNoScreenlockStateHandler = 14,
     kPhoneLockedAndRssiTooLow = 15,
     kForcedReauth = 16,
-    kRequiredForLogin = 17,
+    kLoginWithSmartLockDisabled = 17,
     kPhoneNotLockable = 18,
     kMaxValue = kPhoneNotLockable
   };
diff --git a/chromeos/dbus/proto/media_perception.proto b/chromeos/dbus/proto/media_perception.proto
index 5a27efc..ddd5904 100644
--- a/chromeos/dbus/proto/media_perception.proto
+++ b/chromeos/dbus/proto/media_perception.proto
@@ -95,6 +95,7 @@
     FEATURE_HOTWORD_DETECTION = 2;
     FEATURE_OCCUPANCY_DETECTION = 3;
     FEATURE_EDGE_EMBEDDINGS = 4;
+    FEATURE_SOFTWARE_CROPPING = 5;
   }
 
   // A list of enabled media perception features.
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index 0aae45ae..846c97a 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -142,6 +142,18 @@
   PpdProvider::ResolvePpdCallback callback;
 };
 
+// Carried information for an inflight PPD reference resolution.
+struct PpdReferenceResolutionQueueEntry {
+  // Metadata used to resolve to a unique PpdReference object.
+  PpdProvider::PrinterSearchData search_data;
+
+  // If true, we have failed usb_index_resolution already.
+  bool usb_resolution_attempted = false;
+
+  // Callback to be invoked on completion.
+  PpdProvider::ResolvePpdReferenceCallback cb;
+};
+
 // Extract cupsFilter/cupsFilter2 filter names from a line from a ppd.
 
 // cupsFilter2 lines look like this:
@@ -372,11 +384,13 @@
   bool MaybeStartNextPpdReferenceResolution() {
     while (!ppd_reference_resolution_queue_.empty()) {
       auto& next = ppd_reference_resolution_queue_.front();
+      auto& search_data = next.search_data;
+
       // Have we successfully resolved next yet?
       bool resolved_next = false;
-      if (!next.first.make_and_model.empty()) {
+      if (!search_data.make_and_model.empty()) {
         // Check the index for each make-and-model guess.
-        for (const std::string& make_and_model : next.first.make_and_model) {
+        for (const std::string& make_and_model : search_data.make_and_model) {
           // Check if we need to load its ppd_index
           int ppd_index_shard = IndexShard(make_and_model);
           if (!base::ContainsKey(cached_ppd_idxs_, ppd_index_shard)) {
@@ -389,30 +403,30 @@
             Printer::PpdReference ret;
             ret.effective_make_and_model = make_and_model;
             base::SequencedTaskRunnerHandle::Get()->PostTask(
-                FROM_HERE, base::BindOnce(std::move(next.second),
-                                          PpdProvider::SUCCESS, ret));
+                FROM_HERE,
+                base::BindOnce(std::move(next.cb), PpdProvider::SUCCESS, ret));
             ppd_reference_resolution_queue_.pop_front();
             resolved_next = true;
             break;
           }
         }
       }
-      if (!resolved_next) {
-        // If we get to this point, either we don't have any make and model
-        // guesses for the front entry, or they all missed.  Try USB ids
-        // instead.  This entry will be completed when the usb fetch
-        // returns.
-        if (next.first.usb_vendor_id && next.first.usb_product_id) {
-          StartFetch(GetUsbURL(next.first.usb_vendor_id), FT_USB_DEVICES);
-          return true;
-        }
-        // We don't have anything else left to try.  NOT_FOUND it is.
-        base::SequencedTaskRunnerHandle::Get()->PostTask(
-            FROM_HERE,
-            base::BindOnce(std::move(next.second), PpdProvider::NOT_FOUND,
-                           Printer::PpdReference()));
-        ppd_reference_resolution_queue_.pop_front();
+      if (resolved_next)
+        continue;
+
+      // If we get to this point, either we don't have any make and model
+      // guesses for the front entry, or they all missed.  Try USB ids
+      // instead.
+      if (!next.usb_resolution_attempted && search_data.usb_vendor_id &&
+          search_data.usb_product_id) {
+        StartFetch(GetUsbURL(search_data.usb_vendor_id), FT_USB_DEVICES);
+        return true;
       }
+      // We don't have anything else left to try.  NOT_FOUND it is.
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(next.cb), PpdProvider::NOT_FOUND,
+                                    Printer::PpdReference()));
+      ppd_reference_resolution_queue_.pop_front();
     }
     // Didn't start any fetches.
     return false;
@@ -527,8 +541,10 @@
       make_and_model = base::ToLowerASCII(make_and_model);
     }
 
-    ppd_reference_resolution_queue_.push_back(
-        {lowercase_search_data, std::move(cb)});
+    PpdReferenceResolutionQueueEntry entry;
+    entry.search_data = lowercase_search_data;
+    entry.cb = std::move(cb);
+    ppd_reference_resolution_queue_.push_back(std::move(entry));
     MaybeStartFetch();
   }
 
@@ -981,7 +997,7 @@
     PpdProvider::CallbackResultCode result =
         ValidateAndGetResponseAsString(&buffer);
     int desired_device_id =
-        ppd_reference_resolution_queue_.front().first.usb_product_id;
+        ppd_reference_resolution_queue_.front().search_data.usb_product_id;
     if (result == PpdProvider::SUCCESS) {
       // Parse the JSON response.  This should be a list of the form
       // [
@@ -1023,13 +1039,14 @@
     Printer::PpdReference ret;
     if (result == PpdProvider::SUCCESS) {
       ret.effective_make_and_model = contents;
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(ppd_reference_resolution_queue_.front().cb),
+                         result, ret));
+      ppd_reference_resolution_queue_.pop_front();
+    } else {
+      ppd_reference_resolution_queue_.front().usb_resolution_attempted = true;
     }
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            std::move(ppd_reference_resolution_queue_.front().second), result,
-            ret));
-    ppd_reference_resolution_queue_.pop_front();
   }
 
   // Something went wrong during metadata fetches.  Fail all queued metadata
@@ -1065,10 +1082,9 @@
     // Everything in the PpdReference queue also depends on server information,
     // so should also be failed.
     auto task_runner = base::SequencedTaskRunnerHandle::Get();
-    for (auto& cb : ppd_reference_resolution_queue_) {
-      task_runner->PostTask(
-          FROM_HERE,
-          base::BindOnce(std::move(cb.second), code, Printer::PpdReference()));
+    for (auto& entry : ppd_reference_resolution_queue_) {
+      task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(entry.cb), code,
+                                                      Printer::PpdReference()));
     }
     ppd_reference_resolution_queue_.clear();
   }
@@ -1450,8 +1466,7 @@
   base::circular_deque<PpdResolutionQueueEntry> ppd_resolution_queue_;
 
   // Queued ResolvePpdReference() requests.
-  base::circular_deque<
-      std::pair<PrinterSearchData, ResolvePpdReferenceCallback>>
+  base::circular_deque<PpdReferenceResolutionQueueEntry>
       ppd_reference_resolution_queue_;
 
   // Queued ReverseIndex() calls.
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 069023ad..79b95ed 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -30,6 +30,10 @@
     "cryptauth_gcm_manager.h",
     "cryptauth_gcm_manager_impl.cc",
     "cryptauth_gcm_manager_impl.h",
+    "cryptauth_key.cc",
+    "cryptauth_key.h",
+    "cryptauth_key_bundle.cc",
+    "cryptauth_key_bundle.h",
     "device_sync_base.cc",
     "device_sync_base.h",
     "device_sync_impl.cc",
@@ -142,6 +146,8 @@
     "cryptauth_enroller_impl_unittest.cc",
     "cryptauth_enrollment_manager_impl_unittest.cc",
     "cryptauth_gcm_manager_impl_unittest.cc",
+    "cryptauth_key_bundle_unittest.cc",
+    "cryptauth_key_unittest.cc",
     "device_sync_service_unittest.cc",
     "remote_device_loader_unittest.cc",
     "remote_device_provider_impl_unittest.cc",
diff --git a/chromeos/services/device_sync/cryptauth_key.cc b/chromeos/services/device_sync/cryptauth_key.cc
new file mode 100644
index 0000000..34e81ad
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key.cc
@@ -0,0 +1,151 @@
+// 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 "chromeos/services/device_sync/cryptauth_key.h"
+
+#include "base/base64.h"
+#include "crypto/sha2.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+// Strings used as the DictionaryValue keys in As*DictionaryValue().
+const char kHandleDictKey[] = "handle";
+const char kStatusDictKey[] = "status";
+const char kTypeDictKey[] = "type";
+const char kSymmetricKeyDictKey[] = "symmetric_key";
+const char kPublicKeyDictKey[] = "public_key";
+const char kPrivateKeyDictKey[] = "private_key";
+
+// Returns the base64-encoded SHA256 hash of the input string.
+std::string CreateHandle(const std::string& string_to_hash) {
+  std::string handle;
+  base::Base64Encode(crypto::SHA256HashString(string_to_hash), &handle);
+  return handle;
+}
+
+bool IsSymmetricKeyType(cryptauthv2::KeyType type) {
+  return (type == cryptauthv2::KeyType::RAW128 ||
+          type == cryptauthv2::KeyType::RAW256);
+}
+
+bool IsAsymmetricKeyType(cryptauthv2::KeyType type) {
+  return (type == cryptauthv2::KeyType::P256 ||
+          type == cryptauthv2::KeyType::CURVE25519);
+}
+
+}  // namespace
+
+// static
+base::Optional<CryptAuthKey> CryptAuthKey::FromDictionary(
+    const base::Value& dict) {
+  if (!dict.is_dict())
+    return base::nullopt;
+
+  base::Optional<int> opt_status = dict.FindIntKey(kStatusDictKey);
+  if (!opt_status)
+    return base::nullopt;
+  CryptAuthKey::Status status = static_cast<CryptAuthKey::Status>(*opt_status);
+
+  base::Optional<int> opt_type = dict.FindIntKey(kTypeDictKey);
+  if (!opt_type || !cryptauthv2::KeyType_IsValid(*opt_type))
+    return base::nullopt;
+  cryptauthv2::KeyType type = static_cast<cryptauthv2::KeyType>(*opt_type);
+
+  const std::string* handle = dict.FindStringKey(kHandleDictKey);
+  if (!handle || handle->empty())
+    return base::nullopt;
+
+  if (IsSymmetricKeyType(type)) {
+    const std::string* symmetric_key = dict.FindStringKey(kSymmetricKeyDictKey);
+    if (!symmetric_key || symmetric_key->empty())
+      return base::nullopt;
+    return CryptAuthKey(*symmetric_key, status, type, *handle);
+  }
+
+  DCHECK(IsAsymmetricKeyType(type));
+  const std::string* public_key = dict.FindStringKey(kPublicKeyDictKey);
+  const std::string* private_key = dict.FindStringKey(kPrivateKeyDictKey);
+  if (!public_key || public_key->empty() || !private_key)
+    return base::nullopt;
+  return CryptAuthKey(*public_key, *private_key, status, type, *handle);
+}
+
+CryptAuthKey::CryptAuthKey(const std::string& symmetric_key,
+                           Status status,
+                           cryptauthv2::KeyType type,
+                           const base::Optional<std::string>& handle)
+    : symmetric_key_(symmetric_key),
+      status_(status),
+      type_(type),
+      handle_(handle ? *handle : CreateHandle(symmetric_key)) {
+  DCHECK(IsSymmetricKey());
+  DCHECK(!symmetric_key_.empty());
+  DCHECK(!handle_.empty());
+}
+
+CryptAuthKey::CryptAuthKey(const std::string& public_key,
+                           const std::string& private_key,
+                           Status status,
+                           cryptauthv2::KeyType type,
+                           const base::Optional<std::string>& handle)
+    : public_key_(public_key),
+      private_key_(private_key),
+      status_(status),
+      type_(type),
+      handle_(handle ? *handle : CreateHandle(public_key)) {
+  DCHECK(IsAsymmetricKey());
+  DCHECK(!public_key_.empty());
+  DCHECK(!handle_.empty());
+}
+
+CryptAuthKey::CryptAuthKey(const CryptAuthKey&) = default;
+
+CryptAuthKey::~CryptAuthKey() = default;
+
+bool CryptAuthKey::IsSymmetricKey() const {
+  return IsSymmetricKeyType(type_);
+}
+
+bool CryptAuthKey::IsAsymmetricKey() const {
+  return IsAsymmetricKeyType(type_);
+}
+
+base::Value CryptAuthKey::AsSymmetricKeyDictionary() const {
+  DCHECK(IsSymmetricKey());
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(kHandleDictKey, base::Value(handle_));
+  dict.SetKey(kStatusDictKey, base::Value(status_));
+  dict.SetKey(kTypeDictKey, base::Value(type_));
+  dict.SetKey(kSymmetricKeyDictKey, base::Value(symmetric_key_));
+  return dict;
+}
+
+base::Value CryptAuthKey::AsAsymmetricKeyDictionary() const {
+  DCHECK(IsAsymmetricKey());
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(kHandleDictKey, base::Value(handle_));
+  dict.SetKey(kStatusDictKey, base::Value(status_));
+  dict.SetKey(kTypeDictKey, base::Value(type_));
+  dict.SetKey(kPublicKeyDictKey, base::Value(public_key_));
+  dict.SetKey(kPrivateKeyDictKey, base::Value(private_key_));
+  return dict;
+}
+
+bool CryptAuthKey::operator==(const CryptAuthKey& other) const {
+  return handle_ == other.handle_ && status_ == other.status_ &&
+         type_ == other.type_ && symmetric_key_ == other.symmetric_key_ &&
+         public_key_ == other.public_key_ && private_key_ == other.private_key_;
+}
+
+bool CryptAuthKey::operator!=(const CryptAuthKey& other) const {
+  return !(*this == other);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_key.h b/chromeos/services/device_sync/cryptauth_key.h
new file mode 100644
index 0000000..eb91e58
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_H_
+
+#include "base/optional.h"
+#include "base/values.h"
+#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Holds information for a key managed by CryptAuth or an ephemeral key, such as
+// a Diffie-Hellman key-pair.
+class CryptAuthKey {
+ public:
+  // Specified by CryptAuth in the Enrollment protocol by
+  // SyncKeysResponse::SyncSingleKeyResponse::KeyAction or
+  // SyncKeysResponse::SyncSingleKeyResponse::KeyCreation. kActive denotes that
+  // the key should be used. kInactive denotes that the key should be retained
+  // for possible future activation but not used. For ephemeral keys not managed
+  // by CryptAuth but used locally for intermediate cryptographic operations,
+  // this status is meaningless.
+  enum Status { kActive, kInactive };
+
+  static base::Optional<CryptAuthKey> FromDictionary(const base::Value& dict);
+
+  // Constructor for symmetric keys.
+  CryptAuthKey(const std::string& symmetric_key,
+               Status status,
+               cryptauthv2::KeyType type,
+               const base::Optional<std::string>& handle = base::nullopt);
+
+  // Constructor for asymmetric keys. Note that |public_key| should be a
+  // serialized securemessage::GenericPublicKey proto.
+  CryptAuthKey(const std::string& public_key,
+               const std::string& private_key,
+               Status status,
+               cryptauthv2::KeyType type,
+               const base::Optional<std::string>& handle = base::nullopt);
+
+  CryptAuthKey(const CryptAuthKey&);
+
+  ~CryptAuthKey();
+
+  bool IsSymmetricKey() const;
+  bool IsAsymmetricKey() const;
+
+  Status status() const { return status_; }
+  cryptauthv2::KeyType type() const { return type_; }
+  const std::string& symmetric_key() const {
+    DCHECK(IsSymmetricKey());
+    return symmetric_key_;
+  }
+  const std::string& public_key() const {
+    DCHECK(IsAsymmetricKey());
+    return public_key_;
+  }
+  const std::string& private_key() const {
+    DCHECK(IsAsymmetricKey());
+    return private_key_;
+  }
+  const std::string& handle() const { return handle_; }
+
+  void set_status(Status status) { status_ = status; }
+
+  // Converts CryptAuthKey to a dictionary-type Value of the form
+  //   {
+  //     "handle": <handle_>
+  //     "status": <status_ as int>
+  //     "symmetric_key": <symmetric_key_>
+  //     "type": <type_ as int>
+  //   }
+  base::Value AsSymmetricKeyDictionary() const;
+
+  // Converts CryptAuthKey to a dictionary-type Value of the form
+  //   {
+  //     "handle": <handle_>
+  //     "private_key" : <private_key_>
+  //     "public_key" : <public_key_>
+  //     "status": <status_ as int>
+  //     "type": <type_ as int>
+  //   }
+  base::Value AsAsymmetricKeyDictionary() const;
+
+  bool operator==(const CryptAuthKey& other) const;
+  bool operator!=(const CryptAuthKey& other) const;
+
+ private:
+  std::string symmetric_key_;
+  std::string public_key_;
+  std::string private_key_;
+  Status status_;
+  cryptauthv2::KeyType type_;
+  std::string handle_;
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_H_
diff --git a/chromeos/services/device_sync/cryptauth_key_bundle.cc b/chromeos/services/device_sync/cryptauth_key_bundle.cc
new file mode 100644
index 0000000..3d88467
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key_bundle.cc
@@ -0,0 +1,218 @@
+// 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 "chromeos/services/device_sync/cryptauth_key_bundle.h"
+
+#include "base/base64.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "chromeos/components/multidevice/logging/logging.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+// The special string used in SyncSingleKeyRequest::key_name, which is
+// understood by CryptAuth to be the user's (non-rotated) key pair. If the user
+// already enrolled with CryptAuth v1, this string will correspond to the
+// existing key pair returned by
+// CryptAuthEnrollmentManager::GetUser{Public,Private}Key().
+const char kUserKeyPairName[] = "PublicKey";
+
+const char kBundleNameDictKey[] = "name";
+const char kKeyListDictKey[] = "keys";
+const char kKeyDirectiveDictKey[] = "key_directive";
+
+base::Optional<cryptauthv2::KeyDirective> KeyDirectiveFromPrefString(
+    const std::string& encoded_serialized_key_directive) {
+  std::string decoded_serialized_key_directive;
+  base::Base64Decode(encoded_serialized_key_directive,
+                     &decoded_serialized_key_directive);
+
+  cryptauthv2::KeyDirective key_directive;
+  if (!key_directive.ParseFromString(decoded_serialized_key_directive)) {
+    PA_LOG(ERROR) << "Error parsing KeyDirective from pref string";
+    return base::nullopt;
+  }
+
+  return key_directive;
+}
+
+std::string KeyDirectiveToPrefString(
+    const cryptauthv2::KeyDirective& key_directive) {
+  std::string encoded_serialized_key_directive;
+  base::Base64Encode(key_directive.SerializeAsString(),
+                     &encoded_serialized_key_directive);
+
+  return encoded_serialized_key_directive;
+}
+
+}  // namespace
+
+// static
+const base::flat_set<CryptAuthKeyBundle::Name>& CryptAuthKeyBundle::NameList() {
+  static const base::NoDestructor<base::flat_set<CryptAuthKeyBundle::Name>>
+      name_list({CryptAuthKeyBundle::Name::kUserKeyPair});
+  return *name_list;
+}
+
+// static
+std::string CryptAuthKeyBundle::KeyBundleNameEnumToString(
+    CryptAuthKeyBundle::Name name) {
+  switch (name) {
+    case CryptAuthKeyBundle::Name::kUserKeyPair:
+      return kUserKeyPairName;
+  }
+}
+
+// static
+base::Optional<CryptAuthKeyBundle::Name>
+CryptAuthKeyBundle::KeyBundleNameStringToEnum(const std::string& name) {
+  if (name == kUserKeyPairName)
+    return CryptAuthKeyBundle::Name::kUserKeyPair;
+
+  return base::nullopt;
+}
+
+// static
+base::Optional<CryptAuthKeyBundle> CryptAuthKeyBundle::FromDictionary(
+    const base::Value& dict) {
+  if (!dict.is_dict())
+    return base::nullopt;
+
+  const std::string* name_string = dict.FindStringKey(kBundleNameDictKey);
+  if (!name_string)
+    return base::nullopt;
+
+  base::Optional<CryptAuthKeyBundle::Name> name =
+      KeyBundleNameStringToEnum(*name_string);
+  if (!name)
+    return base::nullopt;
+
+  CryptAuthKeyBundle bundle(*name);
+
+  const base::Value* keys = dict.FindKey(kKeyListDictKey);
+  if (!keys || !keys->is_list())
+    return base::nullopt;
+
+  bool active_key_exists = false;
+  for (const base::Value& key_dict : keys->GetList()) {
+    base::Optional<CryptAuthKey> key = CryptAuthKey::FromDictionary(key_dict);
+    if (!key)
+      return base::nullopt;
+
+    // Return nullopt if there are multiple active keys.
+    if (key->status() == CryptAuthKey::Status::kActive) {
+      if (active_key_exists)
+        return base::nullopt;
+
+      active_key_exists = true;
+    }
+
+    // Return nullopt if duplicate handles exist.
+    if (base::ContainsKey(bundle.handle_to_key_map(), key->handle()))
+      return base::nullopt;
+
+    bundle.AddKey(*key);
+  }
+
+  const std::string* encoded_serialized_key_directive =
+      dict.FindStringKey(kKeyDirectiveDictKey);
+  if (encoded_serialized_key_directive) {
+    base::Optional<cryptauthv2::KeyDirective> key_directive =
+        KeyDirectiveFromPrefString(*encoded_serialized_key_directive);
+    if (!key_directive)
+      return base::nullopt;
+
+    bundle.set_key_directive(*key_directive);
+  }
+
+  return bundle;
+}
+
+CryptAuthKeyBundle::CryptAuthKeyBundle(Name name) : name_(name) {}
+
+CryptAuthKeyBundle::CryptAuthKeyBundle(const CryptAuthKeyBundle&) = default;
+
+CryptAuthKeyBundle::~CryptAuthKeyBundle() = default;
+
+const CryptAuthKey* CryptAuthKeyBundle::GetActiveKey() const {
+  const auto& it = std::find_if(
+      handle_to_key_map_.begin(), handle_to_key_map_.end(),
+      [](const std::pair<std::string, CryptAuthKey>& handle_key_pair) -> bool {
+        return handle_key_pair.second.status() == CryptAuthKey::Status::kActive;
+      });
+
+  if (it == handle_to_key_map_.end())
+    return nullptr;
+
+  return &it->second;
+}
+
+void CryptAuthKeyBundle::AddKey(const CryptAuthKey& key) {
+  if (key.status() == CryptAuthKey::Status::kActive)
+    DeactivateKeys();
+  handle_to_key_map_.insert_or_assign(key.handle(), key);
+}
+
+void CryptAuthKeyBundle::SetActiveKey(const std::string& handle) {
+  auto it = handle_to_key_map_.find(handle);
+  DCHECK(it != handle_to_key_map_.end());
+  DeactivateKeys();
+  it->second.set_status(CryptAuthKey::Status::kActive);
+}
+
+void CryptAuthKeyBundle::DeleteKey(const std::string& handle) {
+  handle_to_key_map_.erase(handle);
+}
+
+void CryptAuthKeyBundle::DeactivateKeys() {
+  for (auto& handle_key_pair : handle_to_key_map_)
+    handle_key_pair.second.set_status(CryptAuthKey::Status::kInactive);
+}
+
+base::Value CryptAuthKeyBundle::AsDictionary() const {
+  base::Value dict(base::Value::Type::DICTIONARY);
+
+  dict.SetKey(kBundleNameDictKey,
+              base::Value(KeyBundleNameEnumToString(name_)));
+
+  std::vector<base::Value> keys;
+  std::transform(
+      handle_to_key_map_.begin(), handle_to_key_map_.end(),
+      std::back_inserter(keys),
+      [](const std::pair<std::string, CryptAuthKey>& handle_key_pair) {
+        if (handle_key_pair.second.IsSymmetricKey())
+          return handle_key_pair.second.AsSymmetricKeyDictionary();
+
+        DCHECK(handle_key_pair.second.IsAsymmetricKey());
+        return handle_key_pair.second.AsAsymmetricKeyDictionary();
+      });
+  dict.SetKey(kKeyListDictKey, base::Value(keys));
+
+  if (key_directive_) {
+    dict.SetKey(kKeyDirectiveDictKey,
+                base::Value(KeyDirectiveToPrefString(*key_directive_)));
+  }
+
+  return dict;
+}
+
+bool CryptAuthKeyBundle::operator==(const CryptAuthKeyBundle& other) const {
+  return name_ == other.name_ &&
+         handle_to_key_map_ == other.handle_to_key_map_ &&
+         key_directive_.has_value() == other.key_directive_.has_value() &&
+         (!key_directive_ || key_directive_->SerializeAsString() ==
+                                 other.key_directive_->SerializeAsString());
+}
+
+bool CryptAuthKeyBundle::operator!=(const CryptAuthKeyBundle& other) const {
+  return !(*this == other);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_key_bundle.h b/chromeos/services/device_sync/cryptauth_key_bundle.h
new file mode 100644
index 0000000..ae3b8cec
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key_bundle.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/optional.h"
+#include "base/values.h"
+#include "chromeos/services/device_sync/cryptauth_key.h"
+#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// A group of related CryptAuthKeys, uniquely identified by their handles.
+//
+// No more than one key in the bundle can be active at a time, and only the
+// active key should be used for encryption, signing, etc. The inactive keys are
+// retained in case CryptAuth decides to activate them in a future via a
+// SyncSingleKeyResponse::KeyAction.
+//
+// All key bundles used in Chrome OS are enumerated in the Name enum class. The
+// corresponding name string that will be sent to CryptAuth in the
+// SyncSingleKeysRequest::key_name protobuf field can be retrieved via
+// KeyBundleNameEnumToString().
+class CryptAuthKeyBundle {
+ public:
+  // Names which uniquely define a CryptAuthKeyBundle.
+  // TODO(nohle): Add name for DeviceSync keys.
+  enum class Name { kUserKeyPair };
+  static const base::flat_set<CryptAuthKeyBundle::Name>& NameList();
+  static std::string KeyBundleNameEnumToString(CryptAuthKeyBundle::Name name);
+  static base::Optional<CryptAuthKeyBundle::Name> KeyBundleNameStringToEnum(
+      const std::string& name);
+
+  static base::Optional<CryptAuthKeyBundle> FromDictionary(
+      const base::Value& dict);
+
+  CryptAuthKeyBundle(Name name);
+
+  CryptAuthKeyBundle(const CryptAuthKeyBundle&);
+
+  ~CryptAuthKeyBundle();
+
+  Name name() const { return name_; }
+
+  const base::flat_map<std::string, CryptAuthKey>& handle_to_key_map() const {
+    return handle_to_key_map_;
+  }
+
+  const base::Optional<cryptauthv2::KeyDirective>& key_directive() const {
+    return key_directive_;
+  }
+
+  void set_key_directive(const cryptauthv2::KeyDirective& key_directive) {
+    key_directive_ = key_directive;
+  }
+
+  // Returns nullptr if there is no active key.
+  const CryptAuthKey* GetActiveKey() const;
+
+  // If the key being added is active, all other keys in the bundle will be
+  // deactivated. If the handle of the input key matches one in the bundle, the
+  // existing key will be overwritten.
+  void AddKey(const CryptAuthKey& key);
+
+  // Activates the key corresponding to |handle| in the bundle and deactivates
+  // the other keys.
+  void SetActiveKey(const std::string& handle);
+
+  // Sets all key statuses to kInactive.
+  void DeactivateKeys();
+
+  void DeleteKey(const std::string& handle);
+
+  base::Value AsDictionary() const;
+
+  bool operator==(const CryptAuthKeyBundle& other) const;
+  bool operator!=(const CryptAuthKeyBundle& other) const;
+
+ private:
+  Name name_;
+  base::flat_map<std::string, CryptAuthKey> handle_to_key_map_;
+  base::Optional<cryptauthv2::KeyDirective> key_directive_;
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_
diff --git a/chromeos/services/device_sync/cryptauth_key_bundle_unittest.cc b/chromeos/services/device_sync/cryptauth_key_bundle_unittest.cc
new file mode 100644
index 0000000..9eff5141
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key_bundle_unittest.cc
@@ -0,0 +1,254 @@
+// 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 "chromeos/services/device_sync/cryptauth_key_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+const char kFakeSymmetricKeyHandle[] = "fake-symmetric-key-handle";
+const char kFakeAsymmetricKeyHandle[] = "fake-asymmetric-key-handle";
+const char kFakeSymmetricKey[] = "fake-symmetric-key";
+const char kFakePublicKey[] = "fake-public-key";
+const char kFakePrivateKey[] = "fake-private-key";
+
+}  // namespace
+
+TEST(CryptAuthKeyBundleTest, CreateKeyBundle) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  EXPECT_EQ(bundle.name(), CryptAuthKeyBundle::Name::kUserKeyPair);
+  EXPECT_TRUE(bundle.handle_to_key_map().empty());
+  EXPECT_FALSE(bundle.key_directive());
+}
+
+TEST(CryptAuthKeyBundleTest, SetKeyDirective) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  cryptauthv2::KeyDirective key_directive;
+  bundle.set_key_directive(key_directive);
+  ASSERT_TRUE(bundle.key_directive());
+  EXPECT_EQ(bundle.key_directive()->SerializeAsString(),
+            key_directive.SerializeAsString());
+}
+
+TEST(CryptAuthKeyBundleTest, AddKey) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                   cryptauthv2::KeyType::RAW256, kFakeSymmetricKeyHandle);
+  bundle.AddKey(key);
+  EXPECT_TRUE(bundle.handle_to_key_map().size() == 1);
+
+  const auto& it = bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle);
+  ASSERT_TRUE(it != bundle.handle_to_key_map().end());
+  EXPECT_EQ(it->first, key.handle());
+  EXPECT_EQ(it->second, key);
+}
+
+TEST(CryptAuthKeyBundleTest, AddKey_Inactive) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kActive);
+
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kInactive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kActive);
+  EXPECT_EQ(bundle.handle_to_key_map()
+                .find(kFakeAsymmetricKeyHandle)
+                ->second.status(),
+            CryptAuthKey::Status::kInactive);
+}
+
+TEST(CryptAuthKeyBundleTest, AddKey_ActiveKeyDeactivatesOthers) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kActive);
+
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kInactive);
+  EXPECT_EQ(bundle.handle_to_key_map()
+                .find(kFakeAsymmetricKeyHandle)
+                ->second.status(),
+            CryptAuthKey::Status::kActive);
+}
+
+TEST(CryptAuthKeyBundleTest, AddKey_ReplaceKeyWithSameHandle) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256, "same-handle");
+  bundle.AddKey(symmetric_key);
+  EXPECT_EQ(bundle.handle_to_key_map().find("same-handle")->second,
+            symmetric_key);
+  CryptAuthKey asymmetric_key(kFakePublicKey, kFakePrivateKey,
+                              CryptAuthKey::Status::kActive,
+                              cryptauthv2::KeyType::P256, "same-handle");
+  bundle.AddKey(asymmetric_key);
+  EXPECT_TRUE(bundle.handle_to_key_map().size() == 1);
+  EXPECT_EQ(bundle.handle_to_key_map().find("same-handle")->second,
+            asymmetric_key);
+}
+
+TEST(CryptAuthKeyBundleTest, GetActiveKey_DoesNotExist) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  EXPECT_FALSE(bundle.GetActiveKey());
+
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  EXPECT_FALSE(bundle.GetActiveKey());
+}
+
+TEST(CryptAuthKeyBundleTest, GetActiveKey_Exists) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+
+  ASSERT_TRUE(bundle.GetActiveKey());
+  EXPECT_EQ(*bundle.GetActiveKey(), asymmetric_key);
+}
+
+TEST(CryptAuthKeyBundleTest, SetActiveKey_InactiveToActive) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+
+  bundle.SetActiveKey(kFakeSymmetricKeyHandle);
+
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kActive);
+  EXPECT_EQ(bundle.handle_to_key_map()
+                .find(kFakeAsymmetricKeyHandle)
+                ->second.status(),
+            CryptAuthKey::Status::kInactive);
+}
+
+TEST(CryptAuthKeyBundleTest, SetActiveKey_ActiveToActive) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+
+  bundle.SetActiveKey(kFakeAsymmetricKeyHandle);
+
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kInactive);
+  EXPECT_EQ(bundle.handle_to_key_map()
+                .find(kFakeAsymmetricKeyHandle)
+                ->second.status(),
+            CryptAuthKey::Status::kActive);
+}
+
+TEST(CryptAuthKeyBundleTest, DeactivateKeys) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+
+  bundle.DeactivateKeys();
+
+  EXPECT_EQ(
+      bundle.handle_to_key_map().find(kFakeSymmetricKeyHandle)->second.status(),
+      CryptAuthKey::Status::kInactive);
+  EXPECT_EQ(bundle.handle_to_key_map()
+                .find(kFakeAsymmetricKeyHandle)
+                ->second.status(),
+            CryptAuthKey::Status::kInactive);
+}
+
+TEST(CryptAuthKeyBundleTest, DeleteKey) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+
+  EXPECT_TRUE(bundle.handle_to_key_map().size() == 1);
+  bundle.DeleteKey("handle-does-not-exist");
+  EXPECT_TRUE(bundle.handle_to_key_map().size() == 1);
+  bundle.DeleteKey(kFakeSymmetricKeyHandle);
+  EXPECT_TRUE(bundle.handle_to_key_map().empty());
+}
+
+TEST(CryptAuthKeyBundleTest, ToAndFromDictionary_Trivial) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  base::Optional<CryptAuthKeyBundle> bundle_from_dict =
+      CryptAuthKeyBundle::FromDictionary(bundle.AsDictionary());
+  ASSERT_TRUE(bundle_from_dict);
+  EXPECT_EQ(*bundle_from_dict, bundle);
+}
+
+TEST(CryptAuthKeyBundleTest, ToAndFromDictionary) {
+  CryptAuthKeyBundle bundle(CryptAuthKeyBundle::Name::kUserKeyPair);
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                             cryptauthv2::KeyType::RAW256,
+                             kFakeSymmetricKeyHandle);
+  bundle.AddKey(symmetric_key);
+  CryptAuthKey asymmetric_key(
+      kFakePublicKey, kFakePrivateKey, CryptAuthKey::Status::kActive,
+      cryptauthv2::KeyType::P256, kFakeAsymmetricKeyHandle);
+  bundle.AddKey(asymmetric_key);
+
+  cryptauthv2::KeyDirective key_directive;
+  key_directive.mutable_policy_reference()->set_name("fake-policy-name");
+  key_directive.mutable_policy_reference()->set_version(42);
+  *key_directive.add_crossproof_key_names() = "fake-key-name-1";
+  *key_directive.add_crossproof_key_names() = "fake-key-name-2";
+  key_directive.set_enroll_time_millis(1000);
+  bundle.set_key_directive(key_directive);
+
+  base::Optional<CryptAuthKeyBundle> bundle_from_dict =
+      CryptAuthKeyBundle::FromDictionary(bundle.AsDictionary());
+  ASSERT_TRUE(bundle_from_dict);
+  EXPECT_EQ(*bundle_from_dict, bundle);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_key_unittest.cc b/chromeos/services/device_sync/cryptauth_key_unittest.cc
new file mode 100644
index 0000000..4d7e6bc3
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_key_unittest.cc
@@ -0,0 +1,221 @@
+// 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 "chromeos/services/device_sync/cryptauth_key.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+const char kFakeHandle[] = "fake-handle";
+const char kFakeSymmetricKey[] = "fake-symmetric-key";
+const char kFakePublicKey[] = "fake-public-key";
+const char kFakePrivateKey[] = "fake-private-key";
+const char kFakeSymmetricKeySha256HashBase64[] =
+    "+lh4oqYTenQmzyIY8XJreGDJ95A4Sk41c15BQPKOmCY=";
+const char kFakePublicKeySha256HashBase64[] =
+    "vj5oRVhZmlDrE4G4RKNV37Etgr/XuNOwEFAzb888/KM=";
+
+}  // namespace
+
+TEST(CryptAuthKeyTest, CreateSymmetricKey) {
+  CryptAuthKey key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                   cryptauthv2::KeyType::RAW256);
+
+  ASSERT_TRUE(key.IsSymmetricKey());
+  ASSERT_FALSE(key.IsAsymmetricKey());
+  EXPECT_EQ(key.symmetric_key(), kFakeSymmetricKey);
+  EXPECT_EQ(key.status(), CryptAuthKey::Status::kActive);
+  EXPECT_EQ(key.type(), cryptauthv2::KeyType::RAW256);
+  EXPECT_EQ(key.handle(), kFakeSymmetricKeySha256HashBase64);
+
+  CryptAuthKey key_given_handle(kFakeSymmetricKey,
+                                CryptAuthKey::Status::kActive,
+                                cryptauthv2::KeyType::RAW256, kFakeHandle);
+  EXPECT_EQ(key_given_handle.handle(), kFakeHandle);
+}
+
+TEST(CryptAuthKeyTest, CreateAsymmetricKey) {
+  CryptAuthKey key(kFakePublicKey, kFakePrivateKey,
+                   CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256);
+
+  ASSERT_FALSE(key.IsSymmetricKey());
+  ASSERT_TRUE(key.IsAsymmetricKey());
+  EXPECT_EQ(key.public_key(), kFakePublicKey);
+  EXPECT_EQ(key.private_key(), kFakePrivateKey);
+  EXPECT_EQ(key.status(), CryptAuthKey::Status::kActive);
+  EXPECT_EQ(key.type(), cryptauthv2::KeyType::P256);
+  EXPECT_EQ(key.handle(), kFakePublicKeySha256HashBase64);
+
+  CryptAuthKey key_given_handle(kFakePublicKey, kFakePrivateKey,
+                                CryptAuthKey::Status::kActive,
+                                cryptauthv2::KeyType::P256, kFakeHandle);
+  EXPECT_EQ(key_given_handle.handle(), kFakeHandle);
+}
+
+TEST(CryptAuthKeyTest, SymmetricKeyAsDictionary) {
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256, kFakeHandle);
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::RAW256));
+  dict.SetKey("symmetric_key", base::Value(kFakeSymmetricKey));
+
+  EXPECT_EQ(symmetric_key.AsSymmetricKeyDictionary(), dict);
+}
+
+TEST(CryptAuthKeyTest, AsymmetricKeyAsDictionary) {
+  CryptAuthKey asymmetric_key(kFakePublicKey, kFakePrivateKey,
+                              CryptAuthKey::Status::kActive,
+                              cryptauthv2::KeyType::P256, kFakeHandle);
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::P256));
+  dict.SetKey("public_key", base::Value(kFakePublicKey));
+  dict.SetKey("private_key", base::Value(kFakePrivateKey));
+
+  EXPECT_EQ(asymmetric_key.AsAsymmetricKeyDictionary(), dict);
+}
+
+TEST(CryptAuthKeyTest, SymmetricKeyFromDictionary) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::RAW256));
+  dict.SetKey("symmetric_key", base::Value(kFakeSymmetricKey));
+
+  base::Optional<CryptAuthKey> key = CryptAuthKey::FromDictionary(dict);
+  ASSERT_TRUE(key);
+  EXPECT_EQ(*key, CryptAuthKey(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                               cryptauthv2::KeyType::RAW256, kFakeHandle));
+}
+
+TEST(CryptAuthKeyTest, AsymmetricKeyFromDictionary) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::P256));
+  dict.SetKey("public_key", base::Value(kFakePublicKey));
+  dict.SetKey("private_key", base::Value(kFakePrivateKey));
+
+  base::Optional<CryptAuthKey> key = CryptAuthKey::FromDictionary(dict);
+  ASSERT_TRUE(key);
+  EXPECT_EQ(*key, CryptAuthKey(kFakePublicKey, kFakePrivateKey,
+                               CryptAuthKey::Status::kActive,
+                               cryptauthv2::KeyType::P256, kFakeHandle));
+}
+
+TEST(CryptAuthKeyTest, KeyFromDictionary_MissingHandle) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::RAW256));
+  dict.SetKey("symmetric_key", base::Value(kFakeSymmetricKey));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, KeyFromDictionary_MissingStatus) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::RAW256));
+  dict.SetKey("symmetric_key", base::Value(kFakeSymmetricKey));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, KeyFromDictionary_MissingType) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("symmetric_key", base::Value(kFakeSymmetricKey));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, SymmetricKeyFromDictionary_MissingSymmetricKey) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::RAW256));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, AsymmetricKeyFromDictionary_MissingPublicKey) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::P256));
+  dict.SetKey("private_key", base::Value(kFakePrivateKey));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, AsymmetricKeyFromDictionary_MissingPrivateKey) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("handle", base::Value(kFakeHandle));
+  dict.SetKey("status", base::Value(CryptAuthKey::Status::kActive));
+  dict.SetKey("type", base::Value(cryptauthv2::KeyType::P256));
+  dict.SetKey("public_key", base::Value(kFakePublicKey));
+
+  EXPECT_FALSE(CryptAuthKey::FromDictionary(dict));
+}
+
+TEST(CryptAuthKeyTest, Equality) {
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256);
+  CryptAuthKey asymmetric_key(kFakePublicKey, kFakePrivateKey,
+                              CryptAuthKey::Status::kActive,
+                              cryptauthv2::KeyType::P256);
+
+  EXPECT_EQ(symmetric_key,
+            CryptAuthKey(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                         cryptauthv2::KeyType::RAW256));
+  EXPECT_EQ(asymmetric_key, CryptAuthKey(kFakePublicKey, kFakePrivateKey,
+                                         CryptAuthKey::Status::kActive,
+                                         cryptauthv2::KeyType::P256));
+}
+
+TEST(CryptAuthKeyTest, NotEquality) {
+  CryptAuthKey symmetric_key(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                             cryptauthv2::KeyType::RAW256);
+  CryptAuthKey asymmetric_key(kFakePublicKey, kFakePrivateKey,
+                              CryptAuthKey::Status::kActive,
+                              cryptauthv2::KeyType::P256);
+  EXPECT_NE(symmetric_key, asymmetric_key);
+
+  EXPECT_NE(symmetric_key,
+            CryptAuthKey(kFakeSymmetricKey, CryptAuthKey::Status::kInactive,
+                         cryptauthv2::KeyType::RAW256));
+  EXPECT_NE(symmetric_key,
+            CryptAuthKey(kFakeSymmetricKey, CryptAuthKey::Status::kActive,
+                         cryptauthv2::KeyType::RAW128));
+  EXPECT_NE(symmetric_key,
+            CryptAuthKey("different-sym-key", CryptAuthKey::Status::kActive,
+                         cryptauthv2::KeyType::RAW256));
+
+  EXPECT_NE(asymmetric_key, CryptAuthKey(kFakePublicKey, kFakePrivateKey,
+                                         CryptAuthKey::Status::kInactive,
+                                         cryptauthv2::KeyType::P256));
+  EXPECT_NE(asymmetric_key, CryptAuthKey(kFakePublicKey, kFakePrivateKey,
+                                         CryptAuthKey::Status::kActive,
+                                         cryptauthv2::KeyType::CURVE25519));
+  EXPECT_NE(asymmetric_key, CryptAuthKey("different-pub-key", kFakePrivateKey,
+                                         CryptAuthKey::Status::kActive,
+                                         cryptauthv2::KeyType::P256));
+  EXPECT_NE(asymmetric_key, CryptAuthKey(kFakePublicKey, "different-priv-key",
+                                         CryptAuthKey::Status::kActive,
+                                         cryptauthv2::KeyType::P256));
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/components/BUILD.gn b/components/BUILD.gn
index d6a4fba9..f9e89774 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -580,7 +580,7 @@
   test("components_perftests") {
     sources = [
       "discardable_memory/common/discardable_shared_memory_heap_perftest.cc",
-      "leveldb_proto/proto_database_perftest.cc",
+      "leveldb_proto/internal/proto_database_perftest.cc",
       "omnibox/browser/history_quick_provider_performance_unittest.cc",
       "subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc",
       "test/run_all_perftests.cc",
diff --git a/components/autofill/core/browser/legacy_strike_database.cc b/components/autofill/core/browser/legacy_strike_database.cc
index e6df31e..e6fbef3 100644
--- a/components/autofill/core/browser/legacy_strike_database.cc
+++ b/components/autofill/core/browser/legacy_strike_database.cc
@@ -12,7 +12,7 @@
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/proto/strike_data.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace autofill {
 
@@ -23,7 +23,7 @@
 }  // namespace
 
 LegacyStrikeDatabase::LegacyStrikeDatabase(const base::FilePath& database_dir)
-    : db_(std::make_unique<leveldb_proto::ProtoDatabaseImpl<StrikeData>>(
+    : db_(leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<StrikeData>(
           base::CreateSequencedTaskRunnerWithTraits(
               {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
diff --git a/components/autofill/core/browser/legacy_strike_database.h b/components/autofill/core/browser/legacy_strike_database.h
index 57b0fe4..c0aca236 100644
--- a/components/autofill/core/browser/legacy_strike_database.h
+++ b/components/autofill/core/browser/legacy_strike_database.h
@@ -12,7 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace autofill {
 class StrikeData;
diff --git a/components/autofill/core/browser/strike_database.cc b/components/autofill/core/browser/strike_database.cc
index 6d8d3136..a3b85f5b 100644
--- a/components/autofill/core/browser/strike_database.cc
+++ b/components/autofill/core/browser/strike_database.cc
@@ -15,7 +15,7 @@
 #include "base/time/time.h"
 #include "components/autofill/core/browser/proto/strike_data.pb.h"
 #include "components/autofill/core/common/autofill_clock.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace autofill {
 
@@ -25,7 +25,7 @@
 }  // namespace
 
 StrikeDatabase::StrikeDatabase(const base::FilePath& database_dir)
-    : db_(std::make_unique<leveldb_proto::ProtoDatabaseImpl<StrikeData>>(
+    : db_(leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<StrikeData>(
           base::CreateSequencedTaskRunnerWithTraits(
               {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
diff --git a/components/autofill/core/browser/strike_database.h b/components/autofill/core/browser/strike_database.h
index c29b498..3049e0d8 100644
--- a/components/autofill/core/browser/strike_database.h
+++ b/components/autofill/core/browser/strike_database.h
@@ -13,7 +13,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace autofill {
 class StrikeData;
diff --git a/components/autofill/core/browser/strike_database_integrator_base.cc b/components/autofill/core/browser/strike_database_integrator_base.cc
index 56b62a21..04b9c90 100644
--- a/components/autofill/core/browser/strike_database_integrator_base.cc
+++ b/components/autofill/core/browser/strike_database_integrator_base.cc
@@ -15,7 +15,7 @@
 #include "base/time/time.h"
 #include "components/autofill/core/browser/proto/strike_data.pb.h"
 #include "components/autofill/core/common/autofill_clock.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/common/password_form_generation_data.h b/components/autofill/core/common/password_form_generation_data.h
index ffbc3ed..40fb197 100644
--- a/components/autofill/core/common/password_form_generation_data.h
+++ b/components/autofill/core/common/password_form_generation_data.h
@@ -9,7 +9,9 @@
 
 #include "base/optional.h"
 #include "base/strings/string16.h"
+#include "build/build_config.h"
 #include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/signatures_util.h"
 #include "url/gurl.h"
 
@@ -42,10 +44,16 @@
 // TODO(https://crbug.com/866444): Remove old PasswordFormGenerationData and
 // rename to PasswordFormGenerationData when the old parser is gone.
 struct NewPasswordFormGenerationData {
+#if defined(OS_IOS)
+  base::string16 form_name;
+  base::string16 new_password_element;
+  base::string16 confirmation_password_element;
+#else
   uint32_t new_password_renderer_id =
       FormFieldData::kNotSetFormControlRendererId;
   uint32_t confirmation_password_renderer_id =
       FormFieldData::kNotSetFormControlRendererId;
+#endif
 };
 
 }  // namespace autofill
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index b7e282a..802b1d03 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -340,8 +340,7 @@
     should_fail_after_checking_scripts_ = true;
     GetUiController()->ShowOverlay();
     GetUiController()->ShowStatusMessage(l10n_util::GetStringFUTF8(
-        IDS_AUTOFILL_ASSISTANT_LOADING,
-        base::UTF8ToUTF16(web_contents()->GetVisibleURL().host())));
+        IDS_AUTOFILL_ASSISTANT_LOADING, base::UTF8ToUTF16(initial_url.host())));
   }
 
   touchable_element_area_.SetOnUpdate(base::BindRepeating(
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index a740439..b3135e7 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -37,10 +37,10 @@
     base::TimeDelta::FromMilliseconds(200);
 
 // Timeout after roughly 10 seconds (50*200ms).
-static int kPeriodicBoxModelCheckRounds = 50;
+constexpr int kPeriodicBoxModelCheckRounds = 50;
 
 // Expiration time for the Autofill Assistant cookie.
-static int kCookieExpiresSeconds = 600;
+constexpr int kCookieExpiresSeconds = 600;
 
 // Name and value used for the static cookie.
 const char* const kAutofillAssistantCookieName = "autofill_assistant_cookie";
@@ -207,15 +207,18 @@
 }  // namespace
 
 WebController::ElementPositionGetter::ElementPositionGetter()
-    : visual_state_updated_(false), weak_ptr_factory_(this) {}
+    : weak_ptr_factory_(this) {}
 WebController::ElementPositionGetter::~ElementPositionGetter() = default;
 
 void WebController::ElementPositionGetter::Start(
     content::RenderFrameHost* frame_host,
     DevtoolsClient* devtools_client,
     std::string element_object_id,
-    base::OnceCallback<void(int, int)> callback) {
+    ElementPositionCallback callback) {
+  devtools_client_ = devtools_client;
+  object_id_ = element_object_id;
   callback_ = std::move(callback);
+  remaining_rounds_ = kPeriodicBoxModelCheckRounds;
 
   // Wait for a roundtrips through the renderer and compositor pipeline,
   // otherwise touch event may be dropped because of missing handler.
@@ -225,47 +228,32 @@
   frame_host->InsertVisualStateCallback(base::BindOnce(
       &WebController::ElementPositionGetter::OnVisualStateUpdatedCallback,
       weak_ptr_factory_.GetWeakPtr()));
-
-  // Set 'point_x' and 'point_y' to -1 to force one round of stable check.
-  GetAndWaitBoxModelStable(devtools_client, element_object_id,
-                           /* point_x= */ -1,
-                           /* point_y= */ -1, kPeriodicBoxModelCheckRounds);
+  GetAndWaitBoxModelStable();
 }
 
 void WebController::ElementPositionGetter::OnVisualStateUpdatedCallback(
-    bool state) {
-  if (state) {
+    bool success) {
+  if (success) {
     visual_state_updated_ = true;
     return;
   }
 
-  OnResult(-1, -1);
+  OnError();
 }
 
-void WebController::ElementPositionGetter::GetAndWaitBoxModelStable(
-    DevtoolsClient* devtools_client,
-    std::string object_id,
-    int point_x,
-    int point_y,
-    int remaining_rounds) {
-  devtools_client->GetDOM()->GetBoxModel(
-      dom::GetBoxModelParams::Builder().SetObjectId(object_id).Build(),
+void WebController::ElementPositionGetter::GetAndWaitBoxModelStable() {
+  devtools_client_->GetDOM()->GetBoxModel(
+      dom::GetBoxModelParams::Builder().SetObjectId(object_id_).Build(),
       base::BindOnce(
           &WebController::ElementPositionGetter::OnGetBoxModelForStableCheck,
-          weak_ptr_factory_.GetWeakPtr(), devtools_client, object_id, point_x,
-          point_y, remaining_rounds));
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebController::ElementPositionGetter::OnGetBoxModelForStableCheck(
-    DevtoolsClient* devtools_client,
-    std::string object_id,
-    int point_x,
-    int point_y,
-    int remaining_rounds,
     std::unique_ptr<dom::GetBoxModelResult> result) {
   if (!result || !result->GetModel() || !result->GetModel()->GetContent()) {
     DLOG(ERROR) << "Failed to get box model.";
-    OnResult(-1, -1);
+    OnError();
     return;
   }
 
@@ -281,10 +269,10 @@
   // 3*kPeriodicBoxModelCheckInterval) for visual state update callback since
   // it might take longer time to return or never return if no updates.
   DCHECK(kPeriodicBoxModelCheckRounds > 2 &&
-         kPeriodicBoxModelCheckRounds >= remaining_rounds);
-  if (new_point_x == point_x && new_point_y == point_y &&
+         kPeriodicBoxModelCheckRounds >= remaining_rounds_);
+  if (has_point_ && new_point_x == point_x_ && new_point_y == point_y_ &&
       (visual_state_updated_ ||
-       remaining_rounds + 2 < kPeriodicBoxModelCheckRounds)) {
+       remaining_rounds_ + 2 < kPeriodicBoxModelCheckRounds)) {
     // Note that there is still a chance that the element's position has been
     // changed after the last call of GetBoxModel, however, it might be safe
     // to assume the element's position will not be changed before issuing
@@ -295,65 +283,69 @@
     return;
   }
 
-  if (remaining_rounds <= 0) {
-    OnResult(-1, -1);
+  if (remaining_rounds_ <= 0) {
+    OnError();
     return;
   }
 
-  // Scroll the element into view again if it was moved out of view.
-  // Check 'point_x' amd 'point_y' are greater or equal than zero to escape the
-  // first round.
-  if (point_x >= 0 && point_y >= 0) {
+  bool is_first_round = !has_point_;
+  has_point_ = true;
+  point_x_ = new_point_x;
+  point_y_ = new_point_y;
+
+  // Scroll the element into view again if it was moved out of view, starting
+  // from the second round.
+  if (!is_first_round) {
     std::vector<std::unique_ptr<runtime::CallArgument>> argument;
     argument.emplace_back(
-        runtime::CallArgument::Builder().SetObjectId(object_id).Build());
-    devtools_client->GetRuntime()->CallFunctionOn(
+        runtime::CallArgument::Builder().SetObjectId(object_id_).Build());
+    devtools_client_->GetRuntime()->CallFunctionOn(
         runtime::CallFunctionOnParams::Builder()
-            .SetObjectId(object_id)
+            .SetObjectId(object_id_)
             .SetArguments(std::move(argument))
             .SetFunctionDeclaration(std::string(kScrollIntoViewIfNeededScript))
             .SetReturnByValue(true)
             .Build(),
         base::BindOnce(&WebController::ElementPositionGetter::OnScrollIntoView,
-                       weak_ptr_factory_.GetWeakPtr(), devtools_client,
-                       object_id, new_point_x, new_point_y, remaining_rounds));
+                       weak_ptr_factory_.GetWeakPtr()));
     return;
   }
 
+  --remaining_rounds_;
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
           &WebController::ElementPositionGetter::GetAndWaitBoxModelStable,
-          weak_ptr_factory_.GetWeakPtr(), devtools_client, object_id,
-          new_point_x, new_point_y, --remaining_rounds),
+          weak_ptr_factory_.GetWeakPtr()),
       kPeriodicBoxModelCheckInterval);
 }
 
 void WebController::ElementPositionGetter::OnScrollIntoView(
-    DevtoolsClient* devtools_client,
-    std::string object_id,
-    int point_x,
-    int point_y,
-    int remaining_rounds,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
   if (!result || result->HasExceptionDetails()) {
     DLOG(ERROR) << "Failed to scroll the element.";
-    OnResult(-1, -1);
+    OnError();
     return;
   }
 
+  --remaining_rounds_;
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
           &WebController::ElementPositionGetter::GetAndWaitBoxModelStable,
-          weak_ptr_factory_.GetWeakPtr(), devtools_client, object_id, point_x,
-          point_y, --remaining_rounds),
+          weak_ptr_factory_.GetWeakPtr()),
       kPeriodicBoxModelCheckInterval);
 }
 
 void WebController::ElementPositionGetter::OnResult(int x, int y) {
   if (callback_) {
-    std::move(callback_).Run(x, y);
+    std::move(callback_).Run(/* success= */ true, x, y);
+  }
+}
+
+void WebController::ElementPositionGetter::OnError() {
+  if (callback_) {
+    std::move(callback_).Run(/* success= */ false, /* x= */ 0, /* y= */ 0);
   }
 }
 
@@ -475,9 +467,10 @@
     std::unique_ptr<ElementPositionGetter> element_position_getter,
     base::OnceCallback<void(bool)> callback,
     bool is_a_click,
+    bool has_coordinates,
     int x,
     int y) {
-  if (x < 0 || y < 0) {
+  if (!has_coordinates) {
     DLOG(ERROR) << "Failed to get element position.";
     OnResult(false, std::move(callback));
     return;
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index e4e26ad..db6204f9 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -173,8 +173,16 @@
  private:
   friend class WebControllerBrowserTest;
 
-  // Helper class to get element's position when is stable and the frame it
-  // belongs finished visual update.
+  // Callback that receives the position that corresponds to the center
+  // of an element, from ElementPositionGetter.
+  //
+  // If the first element is false, the call failed. Otherwise, the second
+  // element contains the x position and the third the y position of the center
+  // of the element in viewport coordinates.
+  using ElementPositionCallback = base::OnceCallback<void(bool, int, int)>;
+
+  // Helper class to get element's position in viewport coordinates when is
+  // stable and the frame it belongs finished visual update.
   class ElementPositionGetter {
    public:
     ElementPositionGetter();
@@ -185,33 +193,31 @@
     void Start(content::RenderFrameHost* frame_host,
                DevtoolsClient* devtools_client,
                std::string element_object_id,
-               base::OnceCallback<void(int, int)> callback);
+               ElementPositionCallback callback);
 
    private:
-    void OnVisualStateUpdatedCallback(bool state);
-    void GetAndWaitBoxModelStable(DevtoolsClient* devtools_client,
-                                  std::string object_id,
-                                  int point_x,
-                                  int point_y,
-                                  int remaining_rounds);
+    void OnVisualStateUpdatedCallback(bool success);
+    void GetAndWaitBoxModelStable();
     void OnGetBoxModelForStableCheck(
-        DevtoolsClient* devtools_client,
-        std::string object_id,
-        int point_x,
-        int point_y,
-        int remaining_rounds,
         std::unique_ptr<dom::GetBoxModelResult> result);
     void OnScrollIntoView(
-        DevtoolsClient* devtools_client,
-        std::string object_id,
-        int point_x,
-        int point_y,
-        int remaining_rounds,
         std::unique_ptr<runtime::CallFunctionOnResult> result);
     void OnResult(int x, int y);
+    void OnError();
 
-    base::OnceCallback<void(int, int)> callback_;
-    bool visual_state_updated_;
+    DevtoolsClient* devtools_client_ = nullptr;
+    std::string object_id_;
+    int remaining_rounds_ = 0;
+    ElementPositionCallback callback_;
+    bool visual_state_updated_ = false;
+
+    // If |has_point_| is true, |point_x_| and |point_y_| contain the last
+    // computed center of the element, in viewport coordinates. Note that
+    // negative coordinates are valid, in case the element is above or to the
+    // left of the viewport.
+    bool has_point_ = false;
+    int point_x_ = 0;
+    int point_y_ = 0;
 
     base::WeakPtrFactory<ElementPositionGetter> weak_ptr_factory_;
     DISALLOW_COPY_AND_ASSIGN(ElementPositionGetter);
@@ -274,6 +280,7 @@
       std::unique_ptr<ElementPositionGetter> element_position_getter,
       base::OnceCallback<void(bool)> callback,
       bool is_a_click,
+      bool has_coordinates,
       int x,
       int y);
   void OnDispatchPressMouseEvent(
diff --git a/components/browser_sync/abstract_profile_sync_service_test.cc b/components/browser_sync/abstract_profile_sync_service_test.cc
index e4372af..58ba130f 100644
--- a/components/browser_sync/abstract_profile_sync_service_test.cc
+++ b/components/browser_sync/abstract_profile_sync_service_test.cc
@@ -115,31 +115,6 @@
 
 }  // namespace
 
-/* static */
-syncer::ImmutableChangeRecordList
-ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
-    int64_t node_id,
-    syncer::ChangeRecord::Action action) {
-  syncer::ChangeRecord record;
-  record.action = action;
-  record.id = node_id;
-  syncer::ChangeRecordList records(1, record);
-  return syncer::ImmutableChangeRecordList(&records);
-}
-
-/* static */
-syncer::ImmutableChangeRecordList
-ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList(
-    int64_t node_id,
-    const sync_pb::EntitySpecifics& specifics) {
-  syncer::ChangeRecord record;
-  record.action = syncer::ChangeRecord::ACTION_DELETE;
-  record.id = node_id;
-  record.specifics = specifics;
-  syncer::ChangeRecordList records(1, record);
-  return syncer::ImmutableChangeRecordList(&records);
-}
-
 AbstractProfileSyncServiceTest::AbstractProfileSyncServiceTest()
     : data_type_thread_("Extra thread") {
   EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
diff --git a/components/browser_sync/abstract_profile_sync_service_test.h b/components/browser_sync/abstract_profile_sync_service_test.h
index 64819166..1f2f33d2 100644
--- a/components/browser_sync/abstract_profile_sync_service_test.h
+++ b/components/browser_sync/abstract_profile_sync_service_test.h
@@ -5,10 +5,7 @@
 #ifndef COMPONENTS_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
 #define COMPONENTS_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
 
-#include <stdint.h>
-
 #include <memory>
-#include <string>
 
 #include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
@@ -23,22 +20,6 @@
 
 class TestProfileSyncService;
 
-class ProfileSyncServiceTestHelper {
- public:
-  static syncer::ImmutableChangeRecordList MakeSingletonChangeRecordList(
-      int64_t node_id,
-      syncer::ChangeRecord::Action action);
-
-  // Deletions must provide an EntitySpecifics for the deleted data.
-  static syncer::ImmutableChangeRecordList
-  MakeSingletonDeletionChangeRecordList(
-      int64_t node_id,
-      const sync_pb::EntitySpecifics& specifics);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceTestHelper);
-};
-
 class AbstractProfileSyncServiceTest : public testing::Test {
  public:
   AbstractProfileSyncServiceTest();
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 06bd315..eeb1d3b 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -281,7 +281,10 @@
                   ->GetControllerDelegate()
                   .get()));
 
-  user_settings_ = std::make_unique<SyncUserSettingsImpl>(this, &sync_prefs_);
+  user_settings_ = std::make_unique<SyncUserSettingsImpl>(
+      &crypto_, &sync_prefs_, GetRegisteredDataTypes(),
+      base::BindRepeating(&ProfileSyncService::SyncAllowedByPlatformChanged,
+                          base::Unretained(this)));
 
   sync_prefs_.AddSyncPrefObserver(this);
 
@@ -448,12 +451,7 @@
 }
 
 bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
-  if (encryption_pending())
-    return true;
-  const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes();
-  const syncer::ModelTypeSet encrypted_types = GetEncryptedDataTypes();
-  DCHECK(encrypted_types.Has(syncer::PASSWORDS));
-  return !Intersection(preferred_types, encrypted_types).Empty();
+  return user_settings_->IsEncryptedDatatypeEnabled();
 }
 
 void ProfileSyncService::OnProtocolEvent(const syncer::ProtocolEvent& event) {
@@ -1008,8 +1006,9 @@
   NotifyObservers();
 
   // Nobody will call us to start if no sign in is going to happen.
-  if (IsLocalSyncEnabled())
-    RequestStart();
+  if (IsLocalSyncEnabled()) {
+    startup_controller_->TryStart(/*force_immediate=*/true);
+  }
 }
 
 void ProfileSyncService::OnSyncCycleCompleted(
@@ -1235,7 +1234,7 @@
 
   // This must be done before we start syncing with the server to avoid
   // sending unencrypted data up on a first time sync.
-  if (crypto_.encryption_pending())
+  if (user_settings_->IsEncryptionPending())
     engine_->EnableEncryptEverything();
   NotifyObservers();
 
@@ -1331,8 +1330,7 @@
 
 bool ProfileSyncService::IsPassphraseRequired() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.passphrase_required_reason() !=
-         syncer::REASON_PASSPHRASE_NOT_REQUIRED;
+  return user_settings_->IsPassphraseRequired();
 }
 
 bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
@@ -1414,24 +1412,24 @@
 
 syncer::ModelTypeSet ProfileSyncService::GetActiveDataTypes() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!data_type_manager_)
+  if (!data_type_manager_ || GetAuthError().IsPersistentError())
     return syncer::ModelTypeSet();
   return data_type_manager_->GetActiveDataTypes();
 }
 
 bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.IsUsingSecondaryPassphrase();
+  return user_settings_->IsUsingSecondaryPassphrase();
 }
 
 syncer::PassphraseType ProfileSyncService::GetPassphraseType() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.GetPassphraseType();
+  return user_settings_->GetPassphraseType();
 }
 
 base::Time ProfileSyncService::GetExplicitPassphraseTime() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.GetExplicitPassphraseTime();
+  return user_settings_->GetExplicitPassphraseTime();
 }
 
 void ProfileSyncService::SyncAllowedByPlatformChanged(bool allowed) {
@@ -1441,7 +1439,7 @@
     StopImpl(KEEP_DATA);
     // TODO(crbug.com/856179): Evaluate whether we can get away without a full
     // restart (i.e. just reconfigure plus whatever cleanup is necessary). See
-    // also similar comment in RequestStop.
+    // also similar comment in OnSyncRequestedPrefChange().
     if (IsStandaloneTransportEnabled()) {
       startup_controller_->TryStart(/*force_immediate=*/false);
     }
@@ -1653,58 +1651,45 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(data_type_manager_);
   DCHECK(data_type_manager_->IsNigoriEnabled());
-  crypto_.SetEncryptionPassphrase(passphrase);
+  user_settings_->SetEncryptionPassphrase(passphrase);
 }
 
 bool ProfileSyncService::SetDecryptionPassphrase(
     const std::string& passphrase) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (IsPassphraseRequired()) {
-    DCHECK(data_type_manager_);
-    DCHECK(data_type_manager_->IsNigoriEnabled());
-
-    DVLOG(1) << "Setting passphrase for decryption.";
-
-    bool result = crypto_.SetDecryptionPassphrase(passphrase);
-    UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
-    return result;
-  }
-  NOTREACHED() << "SetDecryptionPassphrase must not be called when "
-                  "IsPassphraseRequired() is false.";
-  return false;
+  return user_settings_->SetDecryptionPassphrase(passphrase);
 }
 
 bool ProfileSyncService::IsEncryptEverythingAllowed() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.IsEncryptEverythingAllowed();
+  return user_settings_->IsEncryptEverythingAllowed();
 }
 
 void ProfileSyncService::SetEncryptEverythingAllowed(bool allowed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  crypto_.SetEncryptEverythingAllowed(allowed);
+  user_settings_->SetEncryptEverythingAllowed(allowed);
 }
 
 void ProfileSyncService::EnableEncryptEverything() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  crypto_.EnableEncryptEverything();
+  user_settings_->EnableEncryptEverything();
 }
 
 bool ProfileSyncService::encryption_pending() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // We may be called during the setup process before we're
-  // initialized (via IsEncryptedDatatypeEnabled and
-  // IsPassphraseRequiredForDecryption).
-  return crypto_.encryption_pending();
+  // We may be called during the setup process before we're initialized (via
+  // IsEncryptedDatatypeEnabled and IsPassphraseRequiredForDecryption).
+  return user_settings_->IsEncryptionPending();
 }
 
 bool ProfileSyncService::IsEncryptEverythingEnabled() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.IsEncryptEverythingEnabled();
+  return user_settings_->IsEncryptEverythingEnabled();
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return crypto_.GetEncryptedDataTypes();
+  return user_settings_->GetEncryptedDataTypes();
 }
 
 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
@@ -1725,6 +1710,33 @@
   }
 }
 
+void ProfileSyncService::OnSyncRequestedPrefChange(bool is_sync_requested) {
+  if (is_sync_requested) {
+    NotifyObservers();
+
+    // If the Sync engine was already initialized (probably running in transport
+    // mode), just reconfigure.
+    if (IsStandaloneTransportEnabled() && engine_initialized_) {
+      ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
+    } else {
+      // Otherwise try to start up. Note that there might still be other disable
+      // reasons remaining, in which case this will effectively do nothing.
+      startup_controller_->TryStart(/*force_immediate=*/true);
+    }
+  } else {
+    // This will notify the observers.
+    StopImpl(KEEP_DATA);
+
+    // TODO(crbug.com/856179): Evaluate whether we can get away without a full
+    // restart (i.e. just reconfigure plus whatever cleanup is necessary).
+    // Especially in the CLEAR_DATA case, StopImpl does a lot of cleanup that
+    // might still be required.
+    if (IsStandaloneTransportEnabled()) {
+      startup_controller_->TryStart(/*force_immediate=*/false);
+    }
+  }
+}
+
 void ProfileSyncService::OnGaiaAccountsInCookieUpdated(
     const std::vector<gaia::ListedAccount>& accounts,
     const std::vector<gaia::ListedAccount>& signed_out_accounts,
@@ -1962,38 +1974,9 @@
 }
 
 void ProfileSyncService::StopAndClear() {
-  RequestStop(CLEAR_DATA);
-}
-
-void ProfileSyncService::RequestStop(SyncStopDataFate data_fate) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  sync_prefs_.SetSyncRequested(false);
-  StopImpl(data_fate);
-
-  // TODO(crbug.com/856179): Evaluate whether we can get away without a full
-  // restart (i.e. just reconfigure plus whatever cleanup is necessary).
-  // Especially in the CLEAR_DATA case, StopImpl does a lot of cleanup that
-  // might still be required.
-  if (IsStandaloneTransportEnabled()) {
-    startup_controller_->TryStart(/*force_immediate=*/false);
-  }
-}
-
-void ProfileSyncService::RequestStart() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!sync_prefs_.IsSyncRequested()) {
-    sync_prefs_.SetSyncRequested(true);
-    NotifyObservers();
-  }
-  // If the Sync engine was already initialized (probably running in transport
-  // mode), just reconfigure.
-  if (IsStandaloneTransportEnabled() && engine_initialized_) {
-    ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
-  } else {
-    // Otherwise try to start up. Note that there might still be other disable
-    // reasons remaining, in which case this will effectively do nothing.
-    startup_controller_->TryStart(/*force_immediate=*/true);
-  }
+  StopImpl(CLEAR_DATA);
+  user_settings_->SetSyncRequested(false);
 }
 
 void ProfileSyncService::ReconfigureDatatypeManager(
@@ -2067,7 +2050,7 @@
   network_resources_ = std::move(network_resources);
 
   if (restart) {
-    RequestStart();
+    startup_controller_->TryStart(/*force_immediate=*/true);
     DCHECK(engine_);
   }
 }
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 4e09c45..27e977d7 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -162,14 +162,6 @@
     MANUAL_START,
   };
 
-  // Passed as an argument to RequestStop to control whether or not the sync
-  // engine should clear its data directory when it shuts down. See
-  // RequestStop for more information.
-  enum SyncStopDataFate {
-    KEEP_DATA,
-    CLEAR_DATA,
-  };
-
   // Bundles the arguments for ProfileSyncService construction. This is a
   // movable struct. Because of the non-POD data members, it needs out-of-line
   // constructors, so in particular the move constructor needs to be
@@ -213,8 +205,6 @@
   void TriggerRefresh(const syncer::ModelTypeSet& types) override;
   void OnDataTypeRequestsSyncStartup(syncer::ModelType type) override;
   void StopAndClear() override;
-  void RequestStop(SyncStopDataFate data_fate);
-  void RequestStart();
   void AddObserver(syncer::SyncServiceObserver* observer) override;
   void RemoveObserver(syncer::SyncServiceObserver* observer) override;
   bool HasObserver(const syncer::SyncServiceObserver* observer) const override;
@@ -366,6 +356,7 @@
   // SyncPrefObserver implementation.
   void OnSyncManagedPrefChange(bool is_sync_managed) override;
   void OnFirstSetupCompletePrefChange(bool is_first_setup_complete) override;
+  void OnSyncRequestedPrefChange(bool is_sync_requested) override;
   void OnPreferredDataTypesPrefChange(
       bool sync_everything,
       syncer::ModelTypeSet preferred_types) override;
@@ -414,6 +405,14 @@
   syncer::SyncClient* GetSyncClientForTest();
 
  private:
+  // Passed as an argument to StopImpl to control whether or not the sync
+  // engine should clear its data directory when it shuts down. See StopImpl
+  // for more information.
+  enum SyncStopDataFate {
+    KEEP_DATA,
+    CLEAR_DATA,
+  };
+
   // Shorthand for user_settings_.IsFirstSetupComplete().
   bool IsFirstSetupComplete() const;
 
diff --git a/components/browser_sync/sync_user_settings_impl.cc b/components/browser_sync/sync_user_settings_impl.cc
index cc1feb9..e01353a 100644
--- a/components/browser_sync/sync_user_settings_impl.cc
+++ b/components/browser_sync/sync_user_settings_impl.cc
@@ -4,18 +4,22 @@
 
 #include "components/browser_sync/sync_user_settings_impl.h"
 
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/sync/base/model_type.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/sync_service_crypto.h"
 
 namespace browser_sync {
 
-SyncUserSettingsImpl::SyncUserSettingsImpl(ProfileSyncService* service,
-                                           syncer::SyncPrefs* prefs)
-    : service_(service),
+SyncUserSettingsImpl::SyncUserSettingsImpl(
+    syncer::SyncServiceCrypto* crypto,
+    syncer::SyncPrefs* prefs,
+    syncer::ModelTypeSet registered_types,
+    const base::RepeatingCallback<void(bool)>& sync_allowed_by_platform_changed)
+    : crypto_(crypto),
       prefs_(prefs),
-      registered_types_(service_->GetRegisteredDataTypes()) {
-  DCHECK(service_);
+      registered_types_(registered_types),
+      sync_allowed_by_platform_changed_cb_(sync_allowed_by_platform_changed) {
+  DCHECK(crypto_);
   DCHECK(prefs_);
 }
 
@@ -26,12 +30,7 @@
 }
 
 void SyncUserSettingsImpl::SetSyncRequested(bool requested) {
-  // TODO(crbug.com/884159): Write to prefs directly.
-  if (requested) {
-    service_->RequestStart();
-  } else {
-    service_->RequestStop(ProfileSyncService::KEEP_DATA);
-  }
+  prefs_->SetSyncRequested(requested);
 }
 
 bool SyncUserSettingsImpl::IsSyncAllowedByPlatform() const {
@@ -45,7 +44,7 @@
 
   sync_allowed_by_platform_ = allowed;
 
-  service_->SyncAllowedByPlatformChanged(sync_allowed_by_platform_);
+  sync_allowed_by_platform_changed_cb_.Run(sync_allowed_by_platform_);
 }
 
 bool SyncUserSettingsImpl::IsFirstSetupComplete() const {
@@ -74,63 +73,61 @@
 }
 
 bool SyncUserSettingsImpl::IsEncryptEverythingAllowed() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  // Note: This requires *Profile*SyncService.
-  return service_->IsEncryptEverythingAllowed();
+  return crypto_->IsEncryptEverythingAllowed();
 }
 
 void SyncUserSettingsImpl::SetEncryptEverythingAllowed(bool allowed) {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  // Note: This requires *Profile*SyncService.
-  service_->SetEncryptEverythingAllowed(allowed);
+  crypto_->SetEncryptEverythingAllowed(allowed);
 }
 
 bool SyncUserSettingsImpl::IsEncryptEverythingEnabled() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->IsEncryptEverythingEnabled();
+  return crypto_->IsEncryptEverythingEnabled();
 }
 
 void SyncUserSettingsImpl::EnableEncryptEverything() {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  service_->EnableEncryptEverything();
+  crypto_->EnableEncryptEverything();
 }
 
 bool SyncUserSettingsImpl::IsPassphraseRequired() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->IsPassphraseRequired();
+  return crypto_->passphrase_required_reason() !=
+         syncer::REASON_PASSPHRASE_NOT_REQUIRED;
 }
 
 bool SyncUserSettingsImpl::IsPassphraseRequiredForDecryption() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->IsPassphraseRequiredForDecryption();
+  // If there is an encrypted datatype enabled and we don't have the proper
+  // passphrase, we must prompt the user for a passphrase. The only way for the
+  // user to avoid entering their passphrase is to disable the encrypted types.
+  return IsEncryptedDatatypeEnabled() && IsPassphraseRequired();
 }
 
 bool SyncUserSettingsImpl::IsUsingSecondaryPassphrase() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->IsUsingSecondaryPassphrase();
+  return crypto_->IsUsingSecondaryPassphrase();
 }
 
 base::Time SyncUserSettingsImpl::GetExplicitPassphraseTime() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->GetExplicitPassphraseTime();
+  return crypto_->GetExplicitPassphraseTime();
 }
 
 syncer::PassphraseType SyncUserSettingsImpl::GetPassphraseType() const {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  // Note: This requires *Profile*SyncService.
-  return service_->GetPassphraseType();
+  return crypto_->GetPassphraseType();
 }
 
 void SyncUserSettingsImpl::SetEncryptionPassphrase(
     const std::string& passphrase) {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  service_->SetEncryptionPassphrase(passphrase);
+  crypto_->SetEncryptionPassphrase(passphrase);
 }
 
 bool SyncUserSettingsImpl::SetDecryptionPassphrase(
     const std::string& passphrase) {
-  // TODO(crbug.com/884159): Use SyncServiceCrypto.
-  return service_->SetDecryptionPassphrase(passphrase);
+  DCHECK(IsPassphraseRequired())
+      << "SetDecryptionPassphrase must not be called when "
+         "IsPassphraseRequired() is false.";
+
+  DVLOG(1) << "Setting passphrase for decryption.";
+
+  bool result = crypto_->SetDecryptionPassphrase(passphrase);
+  UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
+  return result;
 }
 
 syncer::ModelTypeSet SyncUserSettingsImpl::GetPreferredDataTypes() const {
@@ -144,4 +141,21 @@
   return types;
 }
 
+syncer::ModelTypeSet SyncUserSettingsImpl::GetEncryptedDataTypes() const {
+  return crypto_->GetEncryptedDataTypes();
+}
+
+bool SyncUserSettingsImpl::IsEncryptedDatatypeEnabled() const {
+  if (IsEncryptionPending())
+    return true;
+  const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes();
+  const syncer::ModelTypeSet encrypted_types = GetEncryptedDataTypes();
+  DCHECK(encrypted_types.Has(syncer::PASSWORDS));
+  return !Intersection(preferred_types, encrypted_types).Empty();
+}
+
+bool SyncUserSettingsImpl::IsEncryptionPending() const {
+  return crypto_->encryption_pending();
+}
+
 }  // namespace browser_sync
diff --git a/components/browser_sync/sync_user_settings_impl.h b/components/browser_sync/sync_user_settings_impl.h
index 096fab112..5ef1ea4e 100644
--- a/components/browser_sync/sync_user_settings_impl.h
+++ b/components/browser_sync/sync_user_settings_impl.h
@@ -9,18 +9,24 @@
 
 #include "components/sync/driver/sync_user_settings.h"
 
+#include "base/callback.h"
+#include "components/sync/base/model_type.h"
+
 namespace syncer {
 class SyncPrefs;
+class SyncServiceCrypto;
 }
 
 namespace browser_sync {
 
-class ProfileSyncService;
-
 class SyncUserSettingsImpl : public syncer::SyncUserSettings {
  public:
-  // Both |service| and |prefs| must not be null, and must outlive this object.
-  SyncUserSettingsImpl(ProfileSyncService* service, syncer::SyncPrefs* prefs);
+  // Both |crypto| and |prefs| must not be null, and must outlive this object.
+  SyncUserSettingsImpl(syncer::SyncServiceCrypto* crypto,
+                       syncer::SyncPrefs* prefs,
+                       syncer::ModelTypeSet registered_types,
+                       const base::RepeatingCallback<void(bool)>&
+                           sync_allowed_by_platform_changed);
   ~SyncUserSettingsImpl() override;
 
   bool IsSyncRequested() const override;
@@ -52,11 +58,15 @@
   bool SetDecryptionPassphrase(const std::string& passphrase) override;
 
   syncer::ModelTypeSet GetPreferredDataTypes() const;
+  syncer::ModelTypeSet GetEncryptedDataTypes() const;
+  bool IsEncryptedDatatypeEnabled() const;
+  bool IsEncryptionPending() const;
 
  private:
-  ProfileSyncService* const service_;
+  syncer::SyncServiceCrypto* const crypto_;
   syncer::SyncPrefs* const prefs_;
   const syncer::ModelTypeSet registered_types_;
+  base::RepeatingCallback<void(bool)> sync_allowed_by_platform_changed_cb_;
 
   // Whether sync is currently allowed on this platform.
   bool sync_allowed_by_platform_ = true;
diff --git a/components/cronet/native/cronet.idl b/components/cronet/native/cronet.idl
index 413e3c63e..0b2d42a6 100644
--- a/components/cronet/native/cronet.idl
+++ b/components/cronet/native/cronet.idl
@@ -133,7 +133,7 @@
 
   /**
    * Cronet internal error code. This may provide more specific error
-   * diagnosis than |error_code|, but the constant values  may change over time.
+   * diagnosis than |error_code|, but the constant values may change over time.
    * See
    * <a href=https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h>
    * here</a> for the lastest list of values.
@@ -382,6 +382,30 @@
    */
   [Sync]
   GetDefaultUserAgent() => (string default_user_agent);
+
+  /**
+   * Registers a listener that gets called after the end of each request after
+   * OnSucceeded(), OnFailed(), or OnCanceled() is called with the {@code
+   * RequestFinishedInfo}.
+   *
+   * The listener is called on {@code executor} -- the same executor may be
+   * used with several listeners.
+   *
+   * Ownership **not** taken for {@code listener} or {@code executor} -- these
+   * may be safely destroyed after a call to RemoveRequestFinishedListener(),
+   * or if the engine has shutdown.
+   *
+   * @param listener the listener for finished requests.
+   * @param executor the executor upon which to run the listener.
+   */
+  AddRequestFinishedListener(RequestFinishedInfoListener listener,
+                             Executor executor);
+
+  /**
+   * Unregisters a RequestFinishedInfoListener, including its association with
+   * its registered Executor.
+   */
+  RemoveRequestFinishedListener(RequestFinishedInfoListener listener);
 };
 
 /**
@@ -1128,12 +1152,185 @@
    * Passed through to a RequestFinishedInfoListener.
    */
   array<handle> annotations;
+
+  /**
+   * A listener that gets invoked after {@link
+   * UrlRequestCallback.OnCanceled()}, {@link UrlRequestCallback.OnFailed()} or
+   * {@link UrlRequestCallback.OnSucceeded()} return.
+   *
+   * The listener is invoked with the request finished info on {@code
+   * request_finished_executor}, which must be set.
+   *
+   * Ownership **not** taken for {@code request_finished_listener} or {@code
+   * request_finished_executor} -- these may be safely destroyed after a call
+   * to RemoveRequestFinishedListener(), or if the engine has shutdown.
+   */
+  RequestFinishedInfoListener? request_finished_listener;
+
+  /**
+   * The Executor used to run the {@code request_finished_listener}.
+   */
+  Executor? request_finished_executor;
 };
 
 /**
- * This value indicates an invalid time.
+ * Represents a date and time expressed as the number of milliseconds since the
+ * UNIX epoch.
  */
-const int64 kInvalidTime = -1;
+struct DateTime {
+  /**
+   * Number of milliseconds since the UNIX epoch.
+   */
+  int64 value;
+};
+
+/*
+ * Represents metrics collected for a single request. Most of these metrics are
+ * timestamps for events during the lifetime of the request, which can be used
+ * to build a detailed timeline for investigating performance.
+ *
+ * Events happen in this order:
+ * <ol>
+ * <li>{@link #request_start request start}</li>
+ * <li>{@link #dns_start DNS start}</li>
+ * <li>{@link #dns_end DNS end}</li>
+ * <li>{@link #connect_start connect start}</li>
+ * <li>{@link #ssl_start SSL start}</li>
+ * <li>{@link #ssl_end SSL end}</li>
+ * <li>{@link #connect_end connect end}</li>
+ * <li>{@link #sending_start sending start}</li>
+ * <li>{@link #sending_end sending end}</li>
+ * <li>{@link #response_start response start}</li>
+ * <li>{@link #request_end request end}</li>
+ * </ol>
+ *
+ * Start times are reported as the time when a request started blocking on the
+ * event, not when the event actually occurred, with the exception of push
+ * start and end. If a metric is not meaningful or not available, including
+ * cases when a request finished before reaching that stage, start and end
+ * times will be null. If no time was spent blocking on an event, start and end
+ * will be the same time.
+ *
+ * Timestamps are recorded using a clock that is guaranteed not to run
+ * backwards. All timestamps are correct relative to the system clock at the
+ * time of request start, and taking the difference between two timestamps will
+ * give the correct difference between the events. In order to preserve this
+ * property, timestamps for events other than request start are not guaranteed
+ * to match the system clock at the times they represent.
+ *
+ * Most timing metrics are taken from
+ * <a
+ * href="https://cs.chromium.org/chromium/src/net/base/load_timing_info.h">LoadTimingInfo</a>,
+ * which holds the information for <a href="http://w3c.github.io/navigation-timing/"></a> and
+ * <a href="https://www.w3.org/TR/resource-timing/"></a>.
+ */
+struct Metrics {
+  /**
+   * Time when the request started, which corresponds to calling
+   * Cronet_UrlRequest_Start(). This timestamp will match the system clock at
+   * the time it represents.
+   */
+  DateTime request_start;
+
+  /**
+   * Time when DNS lookup started. This and {@link #dns_end} will be set to
+   * non-null regardless of whether the result came from a DNS server or the
+   * local cache. Will equal null if the socket was reused (see {@link
+   * #socket_reused}).
+   */
+  DateTime? dns_start;
+
+  /**
+   * Time when DNS lookup finished. This and {@link dns_start} will return
+   * non-null regardless of whether the result came from a DNS server or the
+   * local cache. Will equal null if the socket was reused (see {@link
+   * #socket_reused}).
+   */
+  DateTime? dns_end;
+
+  /**
+   * Time when connection establishment started, typically when DNS resolution
+   * finishes. Will equal null if the socket was reused (see {@link
+   * #socket_reused}).
+   */
+  DateTime? connect_start;
+
+  /**
+   * Time when connection establishment finished, after TCP connection is
+   * established and, if using HTTPS, SSL handshake is completed. For QUIC
+   * 0-RTT, this represents the time of handshake confirmation and might happen
+   * later than {@link #sending_start}. Will equal null if the socket was
+   * reused (see {@link #socket_reused}).
+   */
+  DateTime? connect_end;
+
+  /**
+   * Time when SSL handshake started. For QUIC, this will be the same time as
+   * {@link #connect_start}. Will equal null if SSL is not used or if the
+   * socket was reused (see {@link #socket_reused}).
+   */
+  DateTime? ssl_start;
+
+  /**
+   * Time when SSL handshake finished. For QUIC, this will be the same time as
+   * {@link #connect_end}. Will equal null if SSL is not used or if the socket
+   * was reused (see {@link #socket_reused}).
+   */
+  DateTime? ssl_end;
+
+  /**
+   * Time when sending HTTP request headers started.
+   */
+  DateTime sending_start;
+
+  /**
+   * Time when sending HTTP request body finished. (Sending request body
+   * happens after sending request headers.)
+   */
+  DateTime sending_end;
+
+  /**
+   * Time when first byte of HTTP/2 server push was received.  Will equal
+   * null if server push is not used.
+   */
+  DateTime? push_start;
+
+  /**
+   * Time when last byte of HTTP/2 server push was received.  Will equal
+   * null if server push is not used.
+   */
+  DateTime? push_end;
+
+  /**
+   * Time when the end of the response headers was received.
+   */
+  DateTime response_start;
+
+  /**
+   * Time when the request finished.
+   */
+  DateTime request_end;
+
+  /**
+   * True if the socket was reused from a previous request, false otherwise.
+   * In HTTP/2 or QUIC, if streams are multiplexed in a single connection, this
+   * will be {@code true} for all streams after the first.  When {@code true},
+   * DNS, connection, and SSL times will be null.
+   */
+  bool socket_reused = false;
+
+  /**
+   * Returns total bytes sent over the network transport layer, or -1 if not
+   * collected.
+   */
+  int64 sent_byte_count = -1;
+
+  /**
+   * Total bytes received over the network transport layer, or -1 if not
+   * collected. Number of bytes does not include any previous redirects.
+   */
+  int64 received_byte_count = -1;
+};
 
 /**
  * Information about a finished request.
@@ -1157,159 +1354,24 @@
     CANCELED = 2,
   };
 
-  // TODO(caraitto): Move metrics fields to their own class once C generator
-  // supports struct within a struct.
+  /**
+   * Metrics collected for this request.
+   */
+  Metrics metrics;
 
-  /*
-   * The below fields represent metrics collected for a single request. Most of
-   * these metrics are timestamps for events during the lifetime of the request,
-   * which can be used to build a detailed timeline for investigating
-   * performance.
+  /**
+   * The objects that the caller has supplied when initiating the request,
+   * using {@link UrlRequestParams.annotations}.
    *
-   * Events happen in this order:
-   * <ol>
-   * <li>{@link #request_start request start}</li>
-   * <li>{@link #dns_start DNS start}</li>
-   * <li>{@link #dns_end DNS end}</li>
-   * <li>{@link #connect_start connect start}</li>
-   * <li>{@link #ssl_start SSL start}</li>
-   * <li>{@link #ssl_end SSL end}</li>
-   * <li>{@link #connect_end connect end}</li>
-   * <li>{@link #sending_start sending start}</li>
-   * <li>{@link #sending_end sending end}</li>
-   * <li>{@link #response_start response start}</li>
-   * <li>{@link #request_end request end}</li>
-   * </ol>
-   *
-   * Start times are reported as the time when a request started blocking on
-   * the event, not when the event actually occurred, with the exception of
-   * push start and end. If a metric is not meaningful or not available,
-   * including cases when a request finished before reaching that stage, start
-   * and end times will be kInvalidTime. If no time was spent blocking on an
-   * event, start and end will be the same time.
-   *
-   * Timestamps are recorded using a clock that is guaranteed not to run
-   * backwards. All timestamps are correct relative to the system clock at the
-   * time of request start, and taking the difference between two timestamps
-   * will give the correct difference between the events. In order to preserve
-   * this property, timestamps for events other than request start are not
-   * guaranteed to match the system clock at the times they represent.
-   *
-   * Most timing metrics are taken from
-   * <a
-   * href="https://cs.chromium.org/chromium/src/net/base/load_timing_info.h">LoadTimingInfo</a>,
-   * which holds the information for <a href="http://w3c.github.io/navigation-timing/"></a> and
-   * <a href="https://www.w3.org/TR/resource-timing/"></a>.
-   *
-   * All time fields are expressed as the number of milleseconds since the UNIX
-   * epoch, or kInvalidTime.
+   * Annotations can be used to associate a {@link RequestFinishedInfo} with
+   * the original request or type of request.
    */
+  array<handle> annotations;
 
   /**
-   * Time when the request started, which corresponds to calling
-   * Cronet_UrlRequest_Start(). This timestamp will match the system clock at
-   * the time it represents.
+   * Returns the reason why the request finished.
    */
-  int64 request_start = kInvalidTime;
-
-  /**
-   * Time when DNS lookup started. This and {@link #dns_end} will be set to
-   * non-kInvalidTime regardless of whether the result came from a DNS server
-   * or the local cache. Will equal kInvalidTime if the socket was reused (see
-   * {@link #socket_reused}).
-   */
-  int64 dns_start = kInvalidTime;
-
-  /**
-   * Time when DNS lookup finished. This and {@link dns_start} will return
-   * non-kInvalidTime regardless of whether the result came from a DNS server
-   * or the local cache. Will equal kInvalidTime if the socket was reused (see
-   * {@link #socket_reused}).
-   */
-  int64 dns_end = kInvalidTime;
-
-  /**
-   * Time when connection establishment started, typically when DNS resolution
-   * finishes. Will equal kInvalidTime if the socket was reused (see {@link
-   * #socket_reused}).
-   */
-  int64 connect_start = kInvalidTime;
-
-  /**
-   * Time when connection establishment finished, after TCP connection is
-   * established and, if using HTTPS, SSL handshake is completed. For QUIC
-   * 0-RTT, this represents the time of handshake confirmation and might happen
-   * later than {@link #sending_start}. Will equal kInvalidTime if the socket
-   * was reused (see {@link #socket_reused}).
-   */
-  int64 connect_end = kInvalidTime;
-
-  /**
-   * Time when SSL handshake started. For QUIC, this will be the same time as
-   * {@link #connect_start}. Will equal kInvalidTime if SSL is not used or if
-   * the socket was reused (see {@link #socket_reused}).
-   */
-  int64 ssl_start = kInvalidTime;
-
-  /**
-   * Time when SSL handshake finished. For QUIC, this will be the same time as
-   * {@link #connect_end}. Will equal kInvalidTime if SSL is not used or if the
-   * socket was reused (see {@link #socket_reused}).
-   */
-  int64 ssl_end = kInvalidTime;
-
-  /**
-   * Time when sending HTTP request headers started.
-   */
-  int64 sending_start = kInvalidTime;
-
-  /**
-   * Time when sending HTTP request body finished. (Sending request body
-   * happens after sending request headers.)
-   */
-  int64 sending_end = kInvalidTime;
-
-  /**
-   * Time when first byte of HTTP/2 server push was received.  Will equal
-   * kInvalidTime if server push is not used.
-   */
-  int64 push_start = kInvalidTime;
-
-  /**
-   * Time when last byte of HTTP/2 server push was received.  Will equal
-   * kInvalidTime if server push is not used.
-   */
-  int64 push_end = kInvalidTime;
-
-  /**
-   * Time when the end of the response headers was received.
-   */
-  int64 response_start = kInvalidTime;
-
-  /**
-   * Time when the request finished.
-   */
-  int64 request_end = kInvalidTime;
-
-  /**
-   * True if the socket was reused from a previous request, false otherwise.
-   * In HTTP/2 or QUIC, if streams are multiplexed in a single connection,
-   * this will be {@code true} for all streams after the first.  When {@code
-   * true}, DNS, connection, and SSL times will be kInvalidTime.
-   */
-  bool socket_reused = false;
-
-  /**
-   * Returns total bytes sent over the network transport layer, or -1 if not
-   * collected.
-   */
-  int64 sent_byte_count = -1;
-
-  /**
-   * Total bytes received over the network transport layer, or -1 if not
-   * collected. Number of bytes does not include any previous redirects.
-   */
-  int64 received_byte_count = -1;
+  FINISHED_REASON finished_reason = SUCCEEDED;
 
   /**
    * {@link UrlResponseInfo} for the request, if its response had started.
diff --git a/components/cronet/native/engine.cc b/components/cronet/native/engine.cc
index e4c3d10..584cb74 100644
--- a/components/cronet/native/engine.cc
+++ b/components/cronet/native/engine.cc
@@ -258,6 +258,17 @@
   return CheckResult(Cronet_RESULT_SUCCESS);
 }
 
+void Cronet_EngineImpl::AddRequestFinishedListener(
+    Cronet_RequestFinishedInfoListenerPtr listener,
+    Cronet_ExecutorPtr executor) {
+  NOTIMPLEMENTED();
+}
+
+void Cronet_EngineImpl::RemoveRequestFinishedListener(
+    Cronet_RequestFinishedInfoListenerPtr listener) {
+  NOTIMPLEMENTED();
+}
+
 Cronet_RESULT Cronet_EngineImpl::CheckResult(Cronet_RESULT result) {
   if (enable_check_result_)
     CHECK_EQ(Cronet_RESULT_SUCCESS, result);
diff --git a/components/cronet/native/engine.h b/components/cronet/native/engine.h
index 6e104402..689cff8 100644
--- a/components/cronet/native/engine.h
+++ b/components/cronet/native/engine.h
@@ -35,6 +35,11 @@
   Cronet_String GetVersionString() override;
   Cronet_String GetDefaultUserAgent() override;
   Cronet_RESULT Shutdown() override;
+  void AddRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener,
+      Cronet_ExecutorPtr executor) override;
+  void RemoveRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener) override;
 
   // Check |result| and aborts if result is not SUCCESS and enableCheckResult
   // is true.
diff --git a/components/cronet/native/generated/cronet.idl_c.h b/components/cronet/native/generated/cronet.idl_c.h
index fb362c8..b4c3ca8 100644
--- a/components/cronet/native/generated/cronet.idl_c.h
+++ b/components/cronet/native/generated/cronet.idl_c.h
@@ -60,6 +60,10 @@
 typedef struct Cronet_UrlResponseInfo* Cronet_UrlResponseInfoPtr;
 typedef struct Cronet_UrlRequestParams Cronet_UrlRequestParams;
 typedef struct Cronet_UrlRequestParams* Cronet_UrlRequestParamsPtr;
+typedef struct Cronet_DateTime Cronet_DateTime;
+typedef struct Cronet_DateTime* Cronet_DateTimePtr;
+typedef struct Cronet_Metrics Cronet_Metrics;
+typedef struct Cronet_Metrics* Cronet_MetricsPtr;
 typedef struct Cronet_RequestFinishedInfo Cronet_RequestFinishedInfo;
 typedef struct Cronet_RequestFinishedInfo* Cronet_RequestFinishedInfoPtr;
 
@@ -153,7 +157,6 @@
 } Cronet_UrlRequestStatusListener_Status;
 
 // Declare constants
-const int64_t Cronet_kInvalidTime = -1;
 
 ///////////////////////
 // Concrete interface Cronet_Buffer.
@@ -313,6 +316,15 @@
 Cronet_String Cronet_Engine_GetVersionString(Cronet_EnginePtr self);
 CRONET_EXPORT
 Cronet_String Cronet_Engine_GetDefaultUserAgent(Cronet_EnginePtr self);
+CRONET_EXPORT
+void Cronet_Engine_AddRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener,
+    Cronet_ExecutorPtr executor);
+CRONET_EXPORT
+void Cronet_Engine_RemoveRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener);
 // Concrete interface Cronet_Engine is implemented by Cronet.
 // The app can implement these for testing / mocking.
 typedef Cronet_RESULT (*Cronet_Engine_StartWithParamsFunc)(
@@ -327,6 +339,13 @@
     Cronet_EnginePtr self);
 typedef Cronet_String (*Cronet_Engine_GetDefaultUserAgentFunc)(
     Cronet_EnginePtr self);
+typedef void (*Cronet_Engine_AddRequestFinishedListenerFunc)(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener,
+    Cronet_ExecutorPtr executor);
+typedef void (*Cronet_Engine_RemoveRequestFinishedListenerFunc)(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener);
 // Concrete interface Cronet_Engine is implemented by Cronet.
 // The app can use this for testing / mocking.
 CRONET_EXPORT Cronet_EnginePtr Cronet_Engine_CreateWith(
@@ -335,7 +354,10 @@
     Cronet_Engine_StopNetLogFunc StopNetLogFunc,
     Cronet_Engine_ShutdownFunc ShutdownFunc,
     Cronet_Engine_GetVersionStringFunc GetVersionStringFunc,
-    Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc);
+    Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc,
+    Cronet_Engine_AddRequestFinishedListenerFunc AddRequestFinishedListenerFunc,
+    Cronet_Engine_RemoveRequestFinishedListenerFunc
+        RemoveRequestFinishedListenerFunc);
 
 ///////////////////////
 // Abstract interface Cronet_UrlRequestStatusListener is implemented by the app.
@@ -968,6 +990,14 @@
 CRONET_EXPORT
 void Cronet_UrlRequestParams_annotations_add(Cronet_UrlRequestParamsPtr self,
                                              Cronet_RawDataPtr element);
+CRONET_EXPORT
+void Cronet_UrlRequestParams_request_finished_listener_set(
+    Cronet_UrlRequestParamsPtr self,
+    Cronet_RequestFinishedInfoListenerPtr request_finished_listener);
+CRONET_EXPORT
+void Cronet_UrlRequestParams_request_finished_executor_set(
+    Cronet_UrlRequestParamsPtr self,
+    Cronet_ExecutorPtr request_finished_executor);
 // Cronet_UrlRequestParams getters.
 CRONET_EXPORT
 Cronet_String Cronet_UrlRequestParams_http_method_get(
@@ -1005,6 +1035,163 @@
     uint32_t index);
 CRONET_EXPORT
 void Cronet_UrlRequestParams_annotations_clear(Cronet_UrlRequestParamsPtr self);
+CRONET_EXPORT
+Cronet_RequestFinishedInfoListenerPtr
+Cronet_UrlRequestParams_request_finished_listener_get(
+    Cronet_UrlRequestParamsPtr self);
+CRONET_EXPORT
+Cronet_ExecutorPtr Cronet_UrlRequestParams_request_finished_executor_get(
+    Cronet_UrlRequestParamsPtr self);
+
+///////////////////////
+// Struct Cronet_DateTime.
+CRONET_EXPORT Cronet_DateTimePtr Cronet_DateTime_Create();
+CRONET_EXPORT void Cronet_DateTime_Destroy(Cronet_DateTimePtr self);
+// Cronet_DateTime setters.
+CRONET_EXPORT
+void Cronet_DateTime_value_set(Cronet_DateTimePtr self, int64_t value);
+// Cronet_DateTime getters.
+CRONET_EXPORT
+int64_t Cronet_DateTime_value_get(Cronet_DateTimePtr self);
+
+///////////////////////
+// Struct Cronet_Metrics.
+CRONET_EXPORT Cronet_MetricsPtr Cronet_Metrics_Create();
+CRONET_EXPORT void Cronet_Metrics_Destroy(Cronet_MetricsPtr self);
+// Cronet_Metrics setters.
+CRONET_EXPORT
+void Cronet_Metrics_request_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr request_start);
+// Move data from |request_start|. The caller retains ownership of
+// |request_start| and must destroy it.
+void Cronet_Metrics_request_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr request_start);
+CRONET_EXPORT
+void Cronet_Metrics_dns_start_set(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr dns_start);
+// Move data from |dns_start|. The caller retains ownership of |dns_start| and
+// must destroy it.
+void Cronet_Metrics_dns_start_move(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr dns_start);
+CRONET_EXPORT
+void Cronet_Metrics_dns_end_set(Cronet_MetricsPtr self,
+                                Cronet_DateTimePtr dns_end);
+// Move data from |dns_end|. The caller retains ownership of |dns_end| and must
+// destroy it.
+void Cronet_Metrics_dns_end_move(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr dns_end);
+CRONET_EXPORT
+void Cronet_Metrics_connect_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr connect_start);
+// Move data from |connect_start|. The caller retains ownership of
+// |connect_start| and must destroy it.
+void Cronet_Metrics_connect_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr connect_start);
+CRONET_EXPORT
+void Cronet_Metrics_connect_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr connect_end);
+// Move data from |connect_end|. The caller retains ownership of |connect_end|
+// and must destroy it.
+void Cronet_Metrics_connect_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr connect_end);
+CRONET_EXPORT
+void Cronet_Metrics_ssl_start_set(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr ssl_start);
+// Move data from |ssl_start|. The caller retains ownership of |ssl_start| and
+// must destroy it.
+void Cronet_Metrics_ssl_start_move(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr ssl_start);
+CRONET_EXPORT
+void Cronet_Metrics_ssl_end_set(Cronet_MetricsPtr self,
+                                Cronet_DateTimePtr ssl_end);
+// Move data from |ssl_end|. The caller retains ownership of |ssl_end| and must
+// destroy it.
+void Cronet_Metrics_ssl_end_move(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr ssl_end);
+CRONET_EXPORT
+void Cronet_Metrics_sending_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr sending_start);
+// Move data from |sending_start|. The caller retains ownership of
+// |sending_start| and must destroy it.
+void Cronet_Metrics_sending_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr sending_start);
+CRONET_EXPORT
+void Cronet_Metrics_sending_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr sending_end);
+// Move data from |sending_end|. The caller retains ownership of |sending_end|
+// and must destroy it.
+void Cronet_Metrics_sending_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr sending_end);
+CRONET_EXPORT
+void Cronet_Metrics_push_start_set(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr push_start);
+// Move data from |push_start|. The caller retains ownership of |push_start| and
+// must destroy it.
+void Cronet_Metrics_push_start_move(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr push_start);
+CRONET_EXPORT
+void Cronet_Metrics_push_end_set(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr push_end);
+// Move data from |push_end|. The caller retains ownership of |push_end| and
+// must destroy it.
+void Cronet_Metrics_push_end_move(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr push_end);
+CRONET_EXPORT
+void Cronet_Metrics_response_start_set(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr response_start);
+// Move data from |response_start|. The caller retains ownership of
+// |response_start| and must destroy it.
+void Cronet_Metrics_response_start_move(Cronet_MetricsPtr self,
+                                        Cronet_DateTimePtr response_start);
+CRONET_EXPORT
+void Cronet_Metrics_request_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr request_end);
+// Move data from |request_end|. The caller retains ownership of |request_end|
+// and must destroy it.
+void Cronet_Metrics_request_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr request_end);
+CRONET_EXPORT
+void Cronet_Metrics_socket_reused_set(Cronet_MetricsPtr self,
+                                      bool socket_reused);
+CRONET_EXPORT
+void Cronet_Metrics_sent_byte_count_set(Cronet_MetricsPtr self,
+                                        int64_t sent_byte_count);
+CRONET_EXPORT
+void Cronet_Metrics_received_byte_count_set(Cronet_MetricsPtr self,
+                                            int64_t received_byte_count);
+// Cronet_Metrics getters.
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_request_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_dns_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_dns_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_connect_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_connect_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_ssl_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_ssl_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_sending_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_sending_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_push_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_push_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_response_start_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+Cronet_DateTimePtr Cronet_Metrics_request_end_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+bool Cronet_Metrics_socket_reused_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+int64_t Cronet_Metrics_sent_byte_count_get(Cronet_MetricsPtr self);
+CRONET_EXPORT
+int64_t Cronet_Metrics_received_byte_count_get(Cronet_MetricsPtr self);
 
 ///////////////////////
 // Struct Cronet_RequestFinishedInfo.
@@ -1013,66 +1200,20 @@
     Cronet_RequestFinishedInfoPtr self);
 // Cronet_RequestFinishedInfo setters.
 CRONET_EXPORT
-void Cronet_RequestFinishedInfo_request_start_set(
+void Cronet_RequestFinishedInfo_metrics_set(Cronet_RequestFinishedInfoPtr self,
+                                            Cronet_MetricsPtr metrics);
+// Move data from |metrics|. The caller retains ownership of |metrics| and must
+// destroy it.
+void Cronet_RequestFinishedInfo_metrics_move(Cronet_RequestFinishedInfoPtr self,
+                                             Cronet_MetricsPtr metrics);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_annotations_add(
     Cronet_RequestFinishedInfoPtr self,
-    int64_t request_start);
+    Cronet_RawDataPtr element);
 CRONET_EXPORT
-void Cronet_RequestFinishedInfo_dns_start_set(
+void Cronet_RequestFinishedInfo_finished_reason_set(
     Cronet_RequestFinishedInfoPtr self,
-    int64_t dns_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_dns_end_set(Cronet_RequestFinishedInfoPtr self,
-                                            int64_t dns_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_connect_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t connect_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_connect_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t connect_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_ssl_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t ssl_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_ssl_end_set(Cronet_RequestFinishedInfoPtr self,
-                                            int64_t ssl_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_sending_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sending_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_sending_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sending_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_push_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t push_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_push_end_set(Cronet_RequestFinishedInfoPtr self,
-                                             int64_t push_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_response_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t response_start);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_request_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t request_end);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_socket_reused_set(
-    Cronet_RequestFinishedInfoPtr self,
-    bool socket_reused);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_sent_byte_count_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sent_byte_count);
-CRONET_EXPORT
-void Cronet_RequestFinishedInfo_received_byte_count_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t received_byte_count);
+    Cronet_RequestFinishedInfo_FINISHED_REASON finished_reason);
 CRONET_EXPORT
 void Cronet_RequestFinishedInfo_response_info_set(
     Cronet_RequestFinishedInfoPtr self,
@@ -1091,52 +1232,21 @@
                                            Cronet_ErrorPtr error);
 // Cronet_RequestFinishedInfo getters.
 CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_request_start_get(
+Cronet_MetricsPtr Cronet_RequestFinishedInfo_metrics_get(
     Cronet_RequestFinishedInfoPtr self);
 CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_dns_start_get(
+uint32_t Cronet_RequestFinishedInfo_annotations_size(
     Cronet_RequestFinishedInfoPtr self);
 CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_dns_end_get(
+Cronet_RawDataPtr Cronet_RequestFinishedInfo_annotations_at(
+    Cronet_RequestFinishedInfoPtr self,
+    uint32_t index);
+CRONET_EXPORT
+void Cronet_RequestFinishedInfo_annotations_clear(
     Cronet_RequestFinishedInfoPtr self);
 CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_connect_start_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_connect_end_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_ssl_start_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_ssl_end_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_sending_start_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_sending_end_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_push_start_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_push_end_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_response_start_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_request_end_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-bool Cronet_RequestFinishedInfo_socket_reused_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_sent_byte_count_get(
-    Cronet_RequestFinishedInfoPtr self);
-CRONET_EXPORT
-int64_t Cronet_RequestFinishedInfo_received_byte_count_get(
+Cronet_RequestFinishedInfo_FINISHED_REASON
+Cronet_RequestFinishedInfo_finished_reason_get(
     Cronet_RequestFinishedInfoPtr self);
 CRONET_EXPORT
 Cronet_UrlResponseInfoPtr Cronet_RequestFinishedInfo_response_info_get(
diff --git a/components/cronet/native/generated/cronet.idl_impl_interface.cc b/components/cronet/native/generated/cronet.idl_impl_interface.cc
index ecdea48..d6dcc5b 100644
--- a/components/cronet/native/generated/cronet.idl_impl_interface.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_interface.cc
@@ -289,6 +289,21 @@
   return self->GetDefaultUserAgent();
 }
 
+void Cronet_Engine_AddRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener,
+    Cronet_ExecutorPtr executor) {
+  DCHECK(self);
+  self->AddRequestFinishedListener(listener, executor);
+}
+
+void Cronet_Engine_RemoveRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener) {
+  DCHECK(self);
+  self->RemoveRequestFinishedListener(listener);
+}
+
 // Implementation of Cronet_Engine that forwards calls to C functions
 // implemented by the app.
 class Cronet_EngineStub : public Cronet_Engine {
@@ -299,13 +314,19 @@
       Cronet_Engine_StopNetLogFunc StopNetLogFunc,
       Cronet_Engine_ShutdownFunc ShutdownFunc,
       Cronet_Engine_GetVersionStringFunc GetVersionStringFunc,
-      Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc)
+      Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc,
+      Cronet_Engine_AddRequestFinishedListenerFunc
+          AddRequestFinishedListenerFunc,
+      Cronet_Engine_RemoveRequestFinishedListenerFunc
+          RemoveRequestFinishedListenerFunc)
       : StartWithParamsFunc_(StartWithParamsFunc),
         StartNetLogToFileFunc_(StartNetLogToFileFunc),
         StopNetLogFunc_(StopNetLogFunc),
         ShutdownFunc_(ShutdownFunc),
         GetVersionStringFunc_(GetVersionStringFunc),
-        GetDefaultUserAgentFunc_(GetDefaultUserAgentFunc) {}
+        GetDefaultUserAgentFunc_(GetDefaultUserAgentFunc),
+        AddRequestFinishedListenerFunc_(AddRequestFinishedListenerFunc),
+        RemoveRequestFinishedListenerFunc_(RemoveRequestFinishedListenerFunc) {}
 
   ~Cronet_EngineStub() override {}
 
@@ -330,6 +351,17 @@
     return GetDefaultUserAgentFunc_(this);
   }
 
+  void AddRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener,
+      Cronet_ExecutorPtr executor) override {
+    AddRequestFinishedListenerFunc_(this, listener, executor);
+  }
+
+  void RemoveRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener) override {
+    RemoveRequestFinishedListenerFunc_(this, listener);
+  }
+
  private:
   const Cronet_Engine_StartWithParamsFunc StartWithParamsFunc_;
   const Cronet_Engine_StartNetLogToFileFunc StartNetLogToFileFunc_;
@@ -337,6 +369,10 @@
   const Cronet_Engine_ShutdownFunc ShutdownFunc_;
   const Cronet_Engine_GetVersionStringFunc GetVersionStringFunc_;
   const Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc_;
+  const Cronet_Engine_AddRequestFinishedListenerFunc
+      AddRequestFinishedListenerFunc_;
+  const Cronet_Engine_RemoveRequestFinishedListenerFunc
+      RemoveRequestFinishedListenerFunc_;
 
   DISALLOW_COPY_AND_ASSIGN(Cronet_EngineStub);
 };
@@ -347,10 +383,14 @@
     Cronet_Engine_StopNetLogFunc StopNetLogFunc,
     Cronet_Engine_ShutdownFunc ShutdownFunc,
     Cronet_Engine_GetVersionStringFunc GetVersionStringFunc,
-    Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc) {
-  return new Cronet_EngineStub(StartWithParamsFunc, StartNetLogToFileFunc,
-                               StopNetLogFunc, ShutdownFunc,
-                               GetVersionStringFunc, GetDefaultUserAgentFunc);
+    Cronet_Engine_GetDefaultUserAgentFunc GetDefaultUserAgentFunc,
+    Cronet_Engine_AddRequestFinishedListenerFunc AddRequestFinishedListenerFunc,
+    Cronet_Engine_RemoveRequestFinishedListenerFunc
+        RemoveRequestFinishedListenerFunc) {
+  return new Cronet_EngineStub(
+      StartWithParamsFunc, StartNetLogToFileFunc, StopNetLogFunc, ShutdownFunc,
+      GetVersionStringFunc, GetDefaultUserAgentFunc,
+      AddRequestFinishedListenerFunc, RemoveRequestFinishedListenerFunc);
 }
 
 // C functions of Cronet_UrlRequestStatusListener that forward calls to C++
diff --git a/components/cronet/native/generated/cronet.idl_impl_interface.h b/components/cronet/native/generated/cronet.idl_impl_interface.h
index 38d7f901..6a0c562 100644
--- a/components/cronet/native/generated/cronet.idl_impl_interface.h
+++ b/components/cronet/native/generated/cronet.idl_impl_interface.h
@@ -101,6 +101,11 @@
   virtual Cronet_RESULT Shutdown() = 0;
   virtual Cronet_String GetVersionString() = 0;
   virtual Cronet_String GetDefaultUserAgent() = 0;
+  virtual void AddRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener,
+      Cronet_ExecutorPtr executor) = 0;
+  virtual void RemoveRequestFinishedListener(
+      Cronet_RequestFinishedInfoListenerPtr listener) = 0;
 
  private:
   Cronet_ClientContext client_context_ = nullptr;
diff --git a/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc b/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
index 7c96c97e..4950a45 100644
--- a/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_interface_unittest.cc
@@ -225,6 +225,8 @@
   bool Shutdown_called_ = false;
   bool GetVersionString_called_ = false;
   bool GetDefaultUserAgent_called_ = false;
+  bool AddRequestFinishedListener_called_ = false;
+  bool RemoveRequestFinishedListener_called_ = false;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Cronet_EngineTest);
@@ -287,6 +289,25 @@
 
   return static_cast<Cronet_String>(0);
 }
+void TestCronet_Engine_AddRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener,
+    Cronet_ExecutorPtr executor) {
+  CHECK(self);
+  Cronet_ClientContext client_context = Cronet_Engine_GetClientContext(self);
+  auto* test = static_cast<Cronet_EngineTest*>(client_context);
+  CHECK(test);
+  test->AddRequestFinishedListener_called_ = true;
+}
+void TestCronet_Engine_RemoveRequestFinishedListener(
+    Cronet_EnginePtr self,
+    Cronet_RequestFinishedInfoListenerPtr listener) {
+  CHECK(self);
+  Cronet_ClientContext client_context = Cronet_Engine_GetClientContext(self);
+  auto* test = static_cast<Cronet_EngineTest*>(client_context);
+  CHECK(test);
+  test->RemoveRequestFinishedListener_called_ = true;
+}
 }  // namespace
 
 // Test that Cronet_Engine stub forwards function calls as expected.
@@ -294,8 +315,9 @@
   Cronet_EnginePtr test = Cronet_Engine_CreateWith(
       TestCronet_Engine_StartWithParams, TestCronet_Engine_StartNetLogToFile,
       TestCronet_Engine_StopNetLog, TestCronet_Engine_Shutdown,
-      TestCronet_Engine_GetVersionString,
-      TestCronet_Engine_GetDefaultUserAgent);
+      TestCronet_Engine_GetVersionString, TestCronet_Engine_GetDefaultUserAgent,
+      TestCronet_Engine_AddRequestFinishedListener,
+      TestCronet_Engine_RemoveRequestFinishedListener);
   CHECK(test);
   Cronet_Engine_SetClientContext(test, this);
   CHECK(!StartWithParams_called_);
@@ -308,6 +330,8 @@
   CHECK(GetVersionString_called_);
   Cronet_Engine_GetDefaultUserAgent(test);
   CHECK(GetDefaultUserAgent_called_);
+  CHECK(!AddRequestFinishedListener_called_);
+  CHECK(!RemoveRequestFinishedListener_called_);
 
   Cronet_Engine_Destroy(test);
 }
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.cc b/components/cronet/native/generated/cronet.idl_impl_struct.cc
index 4aed426..4ef6b46 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_struct.cc
@@ -682,6 +682,20 @@
   self->annotations.push_back(element);
 }
 
+void Cronet_UrlRequestParams_request_finished_listener_set(
+    Cronet_UrlRequestParamsPtr self,
+    Cronet_RequestFinishedInfoListenerPtr request_finished_listener) {
+  DCHECK(self);
+  self->request_finished_listener = request_finished_listener;
+}
+
+void Cronet_UrlRequestParams_request_finished_executor_set(
+    Cronet_UrlRequestParamsPtr self,
+    Cronet_ExecutorPtr request_finished_executor) {
+  DCHECK(self);
+  self->request_finished_executor = request_finished_executor;
+}
+
 // Struct Cronet_UrlRequestParams getters.
 Cronet_String Cronet_UrlRequestParams_http_method_get(
     Cronet_UrlRequestParamsPtr self) {
@@ -755,6 +769,386 @@
   self->annotations.clear();
 }
 
+Cronet_RequestFinishedInfoListenerPtr
+Cronet_UrlRequestParams_request_finished_listener_get(
+    Cronet_UrlRequestParamsPtr self) {
+  DCHECK(self);
+  return self->request_finished_listener;
+}
+
+Cronet_ExecutorPtr Cronet_UrlRequestParams_request_finished_executor_get(
+    Cronet_UrlRequestParamsPtr self) {
+  DCHECK(self);
+  return self->request_finished_executor;
+}
+
+// Struct Cronet_DateTime.
+Cronet_DateTime::Cronet_DateTime() = default;
+
+Cronet_DateTime::Cronet_DateTime(const Cronet_DateTime& from) = default;
+
+Cronet_DateTime::Cronet_DateTime(Cronet_DateTime&& from) = default;
+
+Cronet_DateTime::~Cronet_DateTime() = default;
+
+Cronet_DateTimePtr Cronet_DateTime_Create() {
+  return new Cronet_DateTime();
+}
+
+void Cronet_DateTime_Destroy(Cronet_DateTimePtr self) {
+  delete self;
+}
+
+// Struct Cronet_DateTime setters.
+void Cronet_DateTime_value_set(Cronet_DateTimePtr self, int64_t value) {
+  DCHECK(self);
+  self->value = value;
+}
+
+// Struct Cronet_DateTime getters.
+int64_t Cronet_DateTime_value_get(Cronet_DateTimePtr self) {
+  DCHECK(self);
+  return self->value;
+}
+
+// Struct Cronet_Metrics.
+Cronet_Metrics::Cronet_Metrics() = default;
+
+Cronet_Metrics::Cronet_Metrics(const Cronet_Metrics& from) = default;
+
+Cronet_Metrics::Cronet_Metrics(Cronet_Metrics&& from) = default;
+
+Cronet_Metrics::~Cronet_Metrics() = default;
+
+Cronet_MetricsPtr Cronet_Metrics_Create() {
+  return new Cronet_Metrics();
+}
+
+void Cronet_Metrics_Destroy(Cronet_MetricsPtr self) {
+  delete self;
+}
+
+// Struct Cronet_Metrics setters.
+void Cronet_Metrics_request_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr request_start) {
+  DCHECK(self);
+  self->request_start.reset();
+  if (request_start != nullptr)
+    self->request_start.emplace(*request_start);
+}
+void Cronet_Metrics_request_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr request_start) {
+  DCHECK(self);
+  self->request_start.reset();
+  if (request_start != nullptr)
+    self->request_start.emplace(std::move(*request_start));
+}
+
+void Cronet_Metrics_dns_start_set(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr dns_start) {
+  DCHECK(self);
+  self->dns_start.reset();
+  if (dns_start != nullptr)
+    self->dns_start.emplace(*dns_start);
+}
+void Cronet_Metrics_dns_start_move(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr dns_start) {
+  DCHECK(self);
+  self->dns_start.reset();
+  if (dns_start != nullptr)
+    self->dns_start.emplace(std::move(*dns_start));
+}
+
+void Cronet_Metrics_dns_end_set(Cronet_MetricsPtr self,
+                                Cronet_DateTimePtr dns_end) {
+  DCHECK(self);
+  self->dns_end.reset();
+  if (dns_end != nullptr)
+    self->dns_end.emplace(*dns_end);
+}
+void Cronet_Metrics_dns_end_move(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr dns_end) {
+  DCHECK(self);
+  self->dns_end.reset();
+  if (dns_end != nullptr)
+    self->dns_end.emplace(std::move(*dns_end));
+}
+
+void Cronet_Metrics_connect_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr connect_start) {
+  DCHECK(self);
+  self->connect_start.reset();
+  if (connect_start != nullptr)
+    self->connect_start.emplace(*connect_start);
+}
+void Cronet_Metrics_connect_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr connect_start) {
+  DCHECK(self);
+  self->connect_start.reset();
+  if (connect_start != nullptr)
+    self->connect_start.emplace(std::move(*connect_start));
+}
+
+void Cronet_Metrics_connect_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr connect_end) {
+  DCHECK(self);
+  self->connect_end.reset();
+  if (connect_end != nullptr)
+    self->connect_end.emplace(*connect_end);
+}
+void Cronet_Metrics_connect_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr connect_end) {
+  DCHECK(self);
+  self->connect_end.reset();
+  if (connect_end != nullptr)
+    self->connect_end.emplace(std::move(*connect_end));
+}
+
+void Cronet_Metrics_ssl_start_set(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr ssl_start) {
+  DCHECK(self);
+  self->ssl_start.reset();
+  if (ssl_start != nullptr)
+    self->ssl_start.emplace(*ssl_start);
+}
+void Cronet_Metrics_ssl_start_move(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr ssl_start) {
+  DCHECK(self);
+  self->ssl_start.reset();
+  if (ssl_start != nullptr)
+    self->ssl_start.emplace(std::move(*ssl_start));
+}
+
+void Cronet_Metrics_ssl_end_set(Cronet_MetricsPtr self,
+                                Cronet_DateTimePtr ssl_end) {
+  DCHECK(self);
+  self->ssl_end.reset();
+  if (ssl_end != nullptr)
+    self->ssl_end.emplace(*ssl_end);
+}
+void Cronet_Metrics_ssl_end_move(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr ssl_end) {
+  DCHECK(self);
+  self->ssl_end.reset();
+  if (ssl_end != nullptr)
+    self->ssl_end.emplace(std::move(*ssl_end));
+}
+
+void Cronet_Metrics_sending_start_set(Cronet_MetricsPtr self,
+                                      Cronet_DateTimePtr sending_start) {
+  DCHECK(self);
+  self->sending_start.reset();
+  if (sending_start != nullptr)
+    self->sending_start.emplace(*sending_start);
+}
+void Cronet_Metrics_sending_start_move(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr sending_start) {
+  DCHECK(self);
+  self->sending_start.reset();
+  if (sending_start != nullptr)
+    self->sending_start.emplace(std::move(*sending_start));
+}
+
+void Cronet_Metrics_sending_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr sending_end) {
+  DCHECK(self);
+  self->sending_end.reset();
+  if (sending_end != nullptr)
+    self->sending_end.emplace(*sending_end);
+}
+void Cronet_Metrics_sending_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr sending_end) {
+  DCHECK(self);
+  self->sending_end.reset();
+  if (sending_end != nullptr)
+    self->sending_end.emplace(std::move(*sending_end));
+}
+
+void Cronet_Metrics_push_start_set(Cronet_MetricsPtr self,
+                                   Cronet_DateTimePtr push_start) {
+  DCHECK(self);
+  self->push_start.reset();
+  if (push_start != nullptr)
+    self->push_start.emplace(*push_start);
+}
+void Cronet_Metrics_push_start_move(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr push_start) {
+  DCHECK(self);
+  self->push_start.reset();
+  if (push_start != nullptr)
+    self->push_start.emplace(std::move(*push_start));
+}
+
+void Cronet_Metrics_push_end_set(Cronet_MetricsPtr self,
+                                 Cronet_DateTimePtr push_end) {
+  DCHECK(self);
+  self->push_end.reset();
+  if (push_end != nullptr)
+    self->push_end.emplace(*push_end);
+}
+void Cronet_Metrics_push_end_move(Cronet_MetricsPtr self,
+                                  Cronet_DateTimePtr push_end) {
+  DCHECK(self);
+  self->push_end.reset();
+  if (push_end != nullptr)
+    self->push_end.emplace(std::move(*push_end));
+}
+
+void Cronet_Metrics_response_start_set(Cronet_MetricsPtr self,
+                                       Cronet_DateTimePtr response_start) {
+  DCHECK(self);
+  self->response_start.reset();
+  if (response_start != nullptr)
+    self->response_start.emplace(*response_start);
+}
+void Cronet_Metrics_response_start_move(Cronet_MetricsPtr self,
+                                        Cronet_DateTimePtr response_start) {
+  DCHECK(self);
+  self->response_start.reset();
+  if (response_start != nullptr)
+    self->response_start.emplace(std::move(*response_start));
+}
+
+void Cronet_Metrics_request_end_set(Cronet_MetricsPtr self,
+                                    Cronet_DateTimePtr request_end) {
+  DCHECK(self);
+  self->request_end.reset();
+  if (request_end != nullptr)
+    self->request_end.emplace(*request_end);
+}
+void Cronet_Metrics_request_end_move(Cronet_MetricsPtr self,
+                                     Cronet_DateTimePtr request_end) {
+  DCHECK(self);
+  self->request_end.reset();
+  if (request_end != nullptr)
+    self->request_end.emplace(std::move(*request_end));
+}
+
+void Cronet_Metrics_socket_reused_set(Cronet_MetricsPtr self,
+                                      bool socket_reused) {
+  DCHECK(self);
+  self->socket_reused = socket_reused;
+}
+
+void Cronet_Metrics_sent_byte_count_set(Cronet_MetricsPtr self,
+                                        int64_t sent_byte_count) {
+  DCHECK(self);
+  self->sent_byte_count = sent_byte_count;
+}
+
+void Cronet_Metrics_received_byte_count_set(Cronet_MetricsPtr self,
+                                            int64_t received_byte_count) {
+  DCHECK(self);
+  self->received_byte_count = received_byte_count;
+}
+
+// Struct Cronet_Metrics getters.
+Cronet_DateTimePtr Cronet_Metrics_request_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->request_start == base::nullopt)
+    return nullptr;
+  return &self->request_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_dns_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->dns_start == base::nullopt)
+    return nullptr;
+  return &self->dns_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_dns_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->dns_end == base::nullopt)
+    return nullptr;
+  return &self->dns_end.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_connect_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->connect_start == base::nullopt)
+    return nullptr;
+  return &self->connect_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_connect_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->connect_end == base::nullopt)
+    return nullptr;
+  return &self->connect_end.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_ssl_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->ssl_start == base::nullopt)
+    return nullptr;
+  return &self->ssl_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_ssl_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->ssl_end == base::nullopt)
+    return nullptr;
+  return &self->ssl_end.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_sending_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->sending_start == base::nullopt)
+    return nullptr;
+  return &self->sending_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_sending_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->sending_end == base::nullopt)
+    return nullptr;
+  return &self->sending_end.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_push_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->push_start == base::nullopt)
+    return nullptr;
+  return &self->push_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_push_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->push_end == base::nullopt)
+    return nullptr;
+  return &self->push_end.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_response_start_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->response_start == base::nullopt)
+    return nullptr;
+  return &self->response_start.value();
+}
+
+Cronet_DateTimePtr Cronet_Metrics_request_end_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  if (self->request_end == base::nullopt)
+    return nullptr;
+  return &self->request_end.value();
+}
+
+bool Cronet_Metrics_socket_reused_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  return self->socket_reused;
+}
+
+int64_t Cronet_Metrics_sent_byte_count_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  return self->sent_byte_count;
+}
+
+int64_t Cronet_Metrics_received_byte_count_get(Cronet_MetricsPtr self) {
+  DCHECK(self);
+  return self->received_byte_count;
+}
+
 // Struct Cronet_RequestFinishedInfo.
 Cronet_RequestFinishedInfo::Cronet_RequestFinishedInfo() = default;
 
@@ -775,113 +1169,33 @@
 }
 
 // Struct Cronet_RequestFinishedInfo setters.
-void Cronet_RequestFinishedInfo_request_start_set(
+void Cronet_RequestFinishedInfo_metrics_set(Cronet_RequestFinishedInfoPtr self,
+                                            Cronet_MetricsPtr metrics) {
+  DCHECK(self);
+  self->metrics.reset();
+  if (metrics != nullptr)
+    self->metrics.emplace(*metrics);
+}
+void Cronet_RequestFinishedInfo_metrics_move(Cronet_RequestFinishedInfoPtr self,
+                                             Cronet_MetricsPtr metrics) {
+  DCHECK(self);
+  self->metrics.reset();
+  if (metrics != nullptr)
+    self->metrics.emplace(std::move(*metrics));
+}
+
+void Cronet_RequestFinishedInfo_annotations_add(
     Cronet_RequestFinishedInfoPtr self,
-    int64_t request_start) {
+    Cronet_RawDataPtr element) {
   DCHECK(self);
-  self->request_start = request_start;
+  self->annotations.push_back(element);
 }
 
-void Cronet_RequestFinishedInfo_dns_start_set(
+void Cronet_RequestFinishedInfo_finished_reason_set(
     Cronet_RequestFinishedInfoPtr self,
-    int64_t dns_start) {
+    Cronet_RequestFinishedInfo_FINISHED_REASON finished_reason) {
   DCHECK(self);
-  self->dns_start = dns_start;
-}
-
-void Cronet_RequestFinishedInfo_dns_end_set(Cronet_RequestFinishedInfoPtr self,
-                                            int64_t dns_end) {
-  DCHECK(self);
-  self->dns_end = dns_end;
-}
-
-void Cronet_RequestFinishedInfo_connect_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t connect_start) {
-  DCHECK(self);
-  self->connect_start = connect_start;
-}
-
-void Cronet_RequestFinishedInfo_connect_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t connect_end) {
-  DCHECK(self);
-  self->connect_end = connect_end;
-}
-
-void Cronet_RequestFinishedInfo_ssl_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t ssl_start) {
-  DCHECK(self);
-  self->ssl_start = ssl_start;
-}
-
-void Cronet_RequestFinishedInfo_ssl_end_set(Cronet_RequestFinishedInfoPtr self,
-                                            int64_t ssl_end) {
-  DCHECK(self);
-  self->ssl_end = ssl_end;
-}
-
-void Cronet_RequestFinishedInfo_sending_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sending_start) {
-  DCHECK(self);
-  self->sending_start = sending_start;
-}
-
-void Cronet_RequestFinishedInfo_sending_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sending_end) {
-  DCHECK(self);
-  self->sending_end = sending_end;
-}
-
-void Cronet_RequestFinishedInfo_push_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t push_start) {
-  DCHECK(self);
-  self->push_start = push_start;
-}
-
-void Cronet_RequestFinishedInfo_push_end_set(Cronet_RequestFinishedInfoPtr self,
-                                             int64_t push_end) {
-  DCHECK(self);
-  self->push_end = push_end;
-}
-
-void Cronet_RequestFinishedInfo_response_start_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t response_start) {
-  DCHECK(self);
-  self->response_start = response_start;
-}
-
-void Cronet_RequestFinishedInfo_request_end_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t request_end) {
-  DCHECK(self);
-  self->request_end = request_end;
-}
-
-void Cronet_RequestFinishedInfo_socket_reused_set(
-    Cronet_RequestFinishedInfoPtr self,
-    bool socket_reused) {
-  DCHECK(self);
-  self->socket_reused = socket_reused;
-}
-
-void Cronet_RequestFinishedInfo_sent_byte_count_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t sent_byte_count) {
-  DCHECK(self);
-  self->sent_byte_count = sent_byte_count;
-}
-
-void Cronet_RequestFinishedInfo_received_byte_count_set(
-    Cronet_RequestFinishedInfoPtr self,
-    int64_t received_byte_count) {
-  DCHECK(self);
-  self->received_byte_count = received_byte_count;
+  self->finished_reason = finished_reason;
 }
 
 void Cronet_RequestFinishedInfo_response_info_set(
@@ -917,100 +1231,37 @@
 }
 
 // Struct Cronet_RequestFinishedInfo getters.
-int64_t Cronet_RequestFinishedInfo_request_start_get(
+Cronet_MetricsPtr Cronet_RequestFinishedInfo_metrics_get(
     Cronet_RequestFinishedInfoPtr self) {
   DCHECK(self);
-  return self->request_start;
+  if (self->metrics == base::nullopt)
+    return nullptr;
+  return &self->metrics.value();
 }
 
-int64_t Cronet_RequestFinishedInfo_dns_start_get(
+uint32_t Cronet_RequestFinishedInfo_annotations_size(
     Cronet_RequestFinishedInfoPtr self) {
   DCHECK(self);
-  return self->dns_start;
+  return self->annotations.size();
+}
+Cronet_RawDataPtr Cronet_RequestFinishedInfo_annotations_at(
+    Cronet_RequestFinishedInfoPtr self,
+    uint32_t index) {
+  DCHECK(self);
+  DCHECK(index < self->annotations.size());
+  return self->annotations[index];
+}
+void Cronet_RequestFinishedInfo_annotations_clear(
+    Cronet_RequestFinishedInfoPtr self) {
+  DCHECK(self);
+  self->annotations.clear();
 }
 
-int64_t Cronet_RequestFinishedInfo_dns_end_get(
+Cronet_RequestFinishedInfo_FINISHED_REASON
+Cronet_RequestFinishedInfo_finished_reason_get(
     Cronet_RequestFinishedInfoPtr self) {
   DCHECK(self);
-  return self->dns_end;
-}
-
-int64_t Cronet_RequestFinishedInfo_connect_start_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->connect_start;
-}
-
-int64_t Cronet_RequestFinishedInfo_connect_end_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->connect_end;
-}
-
-int64_t Cronet_RequestFinishedInfo_ssl_start_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->ssl_start;
-}
-
-int64_t Cronet_RequestFinishedInfo_ssl_end_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->ssl_end;
-}
-
-int64_t Cronet_RequestFinishedInfo_sending_start_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->sending_start;
-}
-
-int64_t Cronet_RequestFinishedInfo_sending_end_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->sending_end;
-}
-
-int64_t Cronet_RequestFinishedInfo_push_start_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->push_start;
-}
-
-int64_t Cronet_RequestFinishedInfo_push_end_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->push_end;
-}
-
-int64_t Cronet_RequestFinishedInfo_response_start_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->response_start;
-}
-
-int64_t Cronet_RequestFinishedInfo_request_end_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->request_end;
-}
-
-bool Cronet_RequestFinishedInfo_socket_reused_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->socket_reused;
-}
-
-int64_t Cronet_RequestFinishedInfo_sent_byte_count_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->sent_byte_count;
-}
-
-int64_t Cronet_RequestFinishedInfo_received_byte_count_get(
-    Cronet_RequestFinishedInfoPtr self) {
-  DCHECK(self);
-  return self->received_byte_count;
+  return self->finished_reason;
 }
 
 Cronet_UrlResponseInfoPtr Cronet_RequestFinishedInfo_response_info_get(
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.h b/components/cronet/native/generated/cronet.idl_impl_struct.h
index b1968d7..42e3a2ec34 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct.h
+++ b/components/cronet/native/generated/cronet.idl_impl_struct.h
@@ -149,11 +149,56 @@
   Cronet_ExecutorPtr upload_data_provider_executor = nullptr;
   bool allow_direct_executor = false;
   std::vector<Cronet_RawDataPtr> annotations;
+  Cronet_RequestFinishedInfoListenerPtr request_finished_listener = nullptr;
+  Cronet_ExecutorPtr request_finished_executor = nullptr;
 
  private:
   DISALLOW_ASSIGN(Cronet_UrlRequestParams);
 };
 
+// Struct Cronet_DateTime.
+struct Cronet_DateTime {
+ public:
+  Cronet_DateTime();
+  explicit Cronet_DateTime(const Cronet_DateTime& from);
+  explicit Cronet_DateTime(Cronet_DateTime&& from);
+  ~Cronet_DateTime();
+
+  int64_t value;
+
+ private:
+  DISALLOW_ASSIGN(Cronet_DateTime);
+};
+
+// Struct Cronet_Metrics.
+struct Cronet_Metrics {
+ public:
+  Cronet_Metrics();
+  explicit Cronet_Metrics(const Cronet_Metrics& from);
+  explicit Cronet_Metrics(Cronet_Metrics&& from);
+  ~Cronet_Metrics();
+
+  base::Optional<Cronet_DateTime> request_start;
+  base::Optional<Cronet_DateTime> dns_start;
+  base::Optional<Cronet_DateTime> dns_end;
+  base::Optional<Cronet_DateTime> connect_start;
+  base::Optional<Cronet_DateTime> connect_end;
+  base::Optional<Cronet_DateTime> ssl_start;
+  base::Optional<Cronet_DateTime> ssl_end;
+  base::Optional<Cronet_DateTime> sending_start;
+  base::Optional<Cronet_DateTime> sending_end;
+  base::Optional<Cronet_DateTime> push_start;
+  base::Optional<Cronet_DateTime> push_end;
+  base::Optional<Cronet_DateTime> response_start;
+  base::Optional<Cronet_DateTime> request_end;
+  bool socket_reused = false;
+  int64_t sent_byte_count = -1;
+  int64_t received_byte_count = -1;
+
+ private:
+  DISALLOW_ASSIGN(Cronet_Metrics);
+};
+
 // Struct Cronet_RequestFinishedInfo.
 struct Cronet_RequestFinishedInfo {
  public:
@@ -162,22 +207,10 @@
   explicit Cronet_RequestFinishedInfo(Cronet_RequestFinishedInfo&& from);
   ~Cronet_RequestFinishedInfo();
 
-  int64_t request_start = Cronet_kInvalidTime;
-  int64_t dns_start = Cronet_kInvalidTime;
-  int64_t dns_end = Cronet_kInvalidTime;
-  int64_t connect_start = Cronet_kInvalidTime;
-  int64_t connect_end = Cronet_kInvalidTime;
-  int64_t ssl_start = Cronet_kInvalidTime;
-  int64_t ssl_end = Cronet_kInvalidTime;
-  int64_t sending_start = Cronet_kInvalidTime;
-  int64_t sending_end = Cronet_kInvalidTime;
-  int64_t push_start = Cronet_kInvalidTime;
-  int64_t push_end = Cronet_kInvalidTime;
-  int64_t response_start = Cronet_kInvalidTime;
-  int64_t request_end = Cronet_kInvalidTime;
-  bool socket_reused = false;
-  int64_t sent_byte_count = -1;
-  int64_t received_byte_count = -1;
+  base::Optional<Cronet_Metrics> metrics;
+  std::vector<Cronet_RawDataPtr> annotations;
+  Cronet_RequestFinishedInfo_FINISHED_REASON finished_reason =
+      Cronet_RequestFinishedInfo_FINISHED_REASON_SUCCEEDED;
   base::Optional<Cronet_UrlResponseInfo> response_info;
   base::Optional<Cronet_Error> error;
 
diff --git a/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc b/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
index a077a28..f7e0270 100644
--- a/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
+++ b/components/cronet/native/generated/cronet.idl_impl_struct_unittest.cc
@@ -249,80 +249,266 @@
   EXPECT_EQ(Cronet_UrlRequestParams_allow_direct_executor_get(first),
             Cronet_UrlRequestParams_allow_direct_executor_get(second));
   // TODO(mef): Test array |annotations|.
+  Cronet_UrlRequestParams_request_finished_listener_set(
+      second, Cronet_UrlRequestParams_request_finished_listener_get(first));
+  EXPECT_EQ(Cronet_UrlRequestParams_request_finished_listener_get(first),
+            Cronet_UrlRequestParams_request_finished_listener_get(second));
+  Cronet_UrlRequestParams_request_finished_executor_set(
+      second, Cronet_UrlRequestParams_request_finished_executor_get(first));
+  EXPECT_EQ(Cronet_UrlRequestParams_request_finished_executor_get(first),
+            Cronet_UrlRequestParams_request_finished_executor_get(second));
   Cronet_UrlRequestParams_Destroy(first);
   Cronet_UrlRequestParams_Destroy(second);
 }
 
+// Fails under MSAN: crbug.com/922842
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_TestCronet_DateTime DISABLED_TestCronet_DateTime
+#else
+#define MAYBE_TestCronet_DateTime TestCronet_DateTime
+#endif
+// Test Struct Cronet_DateTime setters and getters.
+TEST_F(CronetStructTest, MAYBE_TestCronet_DateTime) {
+  Cronet_DateTimePtr first = Cronet_DateTime_Create();
+  Cronet_DateTimePtr second = Cronet_DateTime_Create();
+
+  // Copy values from |first| to |second|.
+  Cronet_DateTime_value_set(second, Cronet_DateTime_value_get(first));
+  EXPECT_EQ(Cronet_DateTime_value_get(first),
+            Cronet_DateTime_value_get(second));
+  Cronet_DateTime_Destroy(first);
+  Cronet_DateTime_Destroy(second);
+}
+
+// Test Struct Cronet_Metrics setters and getters.
+TEST_F(CronetStructTest, TestCronet_Metrics) {
+  Cronet_MetricsPtr first = Cronet_Metrics_Create();
+  Cronet_MetricsPtr second = Cronet_Metrics_Create();
+
+  // Copy values from |first| to |second|.
+  Cronet_DateTimePtr test_request_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_request_start_get(first), nullptr);
+
+  Cronet_Metrics_request_start_set(first, test_request_start);
+  EXPECT_NE(Cronet_Metrics_request_start_get(first), nullptr);
+  Cronet_Metrics_request_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_request_start_get(first), nullptr);
+
+  Cronet_Metrics_request_start_move(first, test_request_start);
+  EXPECT_NE(Cronet_Metrics_request_start_get(first), nullptr);
+  Cronet_Metrics_request_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_request_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_request_start);
+  Cronet_DateTimePtr test_dns_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_dns_start_get(first), nullptr);
+
+  Cronet_Metrics_dns_start_set(first, test_dns_start);
+  EXPECT_NE(Cronet_Metrics_dns_start_get(first), nullptr);
+  Cronet_Metrics_dns_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_dns_start_get(first), nullptr);
+
+  Cronet_Metrics_dns_start_move(first, test_dns_start);
+  EXPECT_NE(Cronet_Metrics_dns_start_get(first), nullptr);
+  Cronet_Metrics_dns_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_dns_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_dns_start);
+  Cronet_DateTimePtr test_dns_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_dns_end_get(first), nullptr);
+
+  Cronet_Metrics_dns_end_set(first, test_dns_end);
+  EXPECT_NE(Cronet_Metrics_dns_end_get(first), nullptr);
+  Cronet_Metrics_dns_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_dns_end_get(first), nullptr);
+
+  Cronet_Metrics_dns_end_move(first, test_dns_end);
+  EXPECT_NE(Cronet_Metrics_dns_end_get(first), nullptr);
+  Cronet_Metrics_dns_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_dns_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_dns_end);
+  Cronet_DateTimePtr test_connect_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_connect_start_get(first), nullptr);
+
+  Cronet_Metrics_connect_start_set(first, test_connect_start);
+  EXPECT_NE(Cronet_Metrics_connect_start_get(first), nullptr);
+  Cronet_Metrics_connect_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_connect_start_get(first), nullptr);
+
+  Cronet_Metrics_connect_start_move(first, test_connect_start);
+  EXPECT_NE(Cronet_Metrics_connect_start_get(first), nullptr);
+  Cronet_Metrics_connect_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_connect_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_connect_start);
+  Cronet_DateTimePtr test_connect_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_connect_end_get(first), nullptr);
+
+  Cronet_Metrics_connect_end_set(first, test_connect_end);
+  EXPECT_NE(Cronet_Metrics_connect_end_get(first), nullptr);
+  Cronet_Metrics_connect_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_connect_end_get(first), nullptr);
+
+  Cronet_Metrics_connect_end_move(first, test_connect_end);
+  EXPECT_NE(Cronet_Metrics_connect_end_get(first), nullptr);
+  Cronet_Metrics_connect_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_connect_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_connect_end);
+  Cronet_DateTimePtr test_ssl_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_ssl_start_get(first), nullptr);
+
+  Cronet_Metrics_ssl_start_set(first, test_ssl_start);
+  EXPECT_NE(Cronet_Metrics_ssl_start_get(first), nullptr);
+  Cronet_Metrics_ssl_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_ssl_start_get(first), nullptr);
+
+  Cronet_Metrics_ssl_start_move(first, test_ssl_start);
+  EXPECT_NE(Cronet_Metrics_ssl_start_get(first), nullptr);
+  Cronet_Metrics_ssl_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_ssl_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_ssl_start);
+  Cronet_DateTimePtr test_ssl_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_ssl_end_get(first), nullptr);
+
+  Cronet_Metrics_ssl_end_set(first, test_ssl_end);
+  EXPECT_NE(Cronet_Metrics_ssl_end_get(first), nullptr);
+  Cronet_Metrics_ssl_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_ssl_end_get(first), nullptr);
+
+  Cronet_Metrics_ssl_end_move(first, test_ssl_end);
+  EXPECT_NE(Cronet_Metrics_ssl_end_get(first), nullptr);
+  Cronet_Metrics_ssl_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_ssl_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_ssl_end);
+  Cronet_DateTimePtr test_sending_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_sending_start_get(first), nullptr);
+
+  Cronet_Metrics_sending_start_set(first, test_sending_start);
+  EXPECT_NE(Cronet_Metrics_sending_start_get(first), nullptr);
+  Cronet_Metrics_sending_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_sending_start_get(first), nullptr);
+
+  Cronet_Metrics_sending_start_move(first, test_sending_start);
+  EXPECT_NE(Cronet_Metrics_sending_start_get(first), nullptr);
+  Cronet_Metrics_sending_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_sending_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_sending_start);
+  Cronet_DateTimePtr test_sending_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_sending_end_get(first), nullptr);
+
+  Cronet_Metrics_sending_end_set(first, test_sending_end);
+  EXPECT_NE(Cronet_Metrics_sending_end_get(first), nullptr);
+  Cronet_Metrics_sending_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_sending_end_get(first), nullptr);
+
+  Cronet_Metrics_sending_end_move(first, test_sending_end);
+  EXPECT_NE(Cronet_Metrics_sending_end_get(first), nullptr);
+  Cronet_Metrics_sending_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_sending_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_sending_end);
+  Cronet_DateTimePtr test_push_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_push_start_get(first), nullptr);
+
+  Cronet_Metrics_push_start_set(first, test_push_start);
+  EXPECT_NE(Cronet_Metrics_push_start_get(first), nullptr);
+  Cronet_Metrics_push_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_push_start_get(first), nullptr);
+
+  Cronet_Metrics_push_start_move(first, test_push_start);
+  EXPECT_NE(Cronet_Metrics_push_start_get(first), nullptr);
+  Cronet_Metrics_push_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_push_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_push_start);
+  Cronet_DateTimePtr test_push_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_push_end_get(first), nullptr);
+
+  Cronet_Metrics_push_end_set(first, test_push_end);
+  EXPECT_NE(Cronet_Metrics_push_end_get(first), nullptr);
+  Cronet_Metrics_push_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_push_end_get(first), nullptr);
+
+  Cronet_Metrics_push_end_move(first, test_push_end);
+  EXPECT_NE(Cronet_Metrics_push_end_get(first), nullptr);
+  Cronet_Metrics_push_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_push_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_push_end);
+  Cronet_DateTimePtr test_response_start = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_response_start_get(first), nullptr);
+
+  Cronet_Metrics_response_start_set(first, test_response_start);
+  EXPECT_NE(Cronet_Metrics_response_start_get(first), nullptr);
+  Cronet_Metrics_response_start_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_response_start_get(first), nullptr);
+
+  Cronet_Metrics_response_start_move(first, test_response_start);
+  EXPECT_NE(Cronet_Metrics_response_start_get(first), nullptr);
+  Cronet_Metrics_response_start_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_response_start_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_response_start);
+  Cronet_DateTimePtr test_request_end = Cronet_DateTime_Create();
+  EXPECT_EQ(Cronet_Metrics_request_end_get(first), nullptr);
+
+  Cronet_Metrics_request_end_set(first, test_request_end);
+  EXPECT_NE(Cronet_Metrics_request_end_get(first), nullptr);
+  Cronet_Metrics_request_end_set(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_request_end_get(first), nullptr);
+
+  Cronet_Metrics_request_end_move(first, test_request_end);
+  EXPECT_NE(Cronet_Metrics_request_end_get(first), nullptr);
+  Cronet_Metrics_request_end_move(first, nullptr);
+  EXPECT_EQ(Cronet_Metrics_request_end_get(first), nullptr);
+
+  Cronet_DateTime_Destroy(test_request_end);
+  Cronet_Metrics_socket_reused_set(second,
+                                   Cronet_Metrics_socket_reused_get(first));
+  EXPECT_EQ(Cronet_Metrics_socket_reused_get(first),
+            Cronet_Metrics_socket_reused_get(second));
+  Cronet_Metrics_sent_byte_count_set(second,
+                                     Cronet_Metrics_sent_byte_count_get(first));
+  EXPECT_EQ(Cronet_Metrics_sent_byte_count_get(first),
+            Cronet_Metrics_sent_byte_count_get(second));
+  Cronet_Metrics_received_byte_count_set(
+      second, Cronet_Metrics_received_byte_count_get(first));
+  EXPECT_EQ(Cronet_Metrics_received_byte_count_get(first),
+            Cronet_Metrics_received_byte_count_get(second));
+  Cronet_Metrics_Destroy(first);
+  Cronet_Metrics_Destroy(second);
+}
+
 // Test Struct Cronet_RequestFinishedInfo setters and getters.
 TEST_F(CronetStructTest, TestCronet_RequestFinishedInfo) {
   Cronet_RequestFinishedInfoPtr first = Cronet_RequestFinishedInfo_Create();
   Cronet_RequestFinishedInfoPtr second = Cronet_RequestFinishedInfo_Create();
 
   // Copy values from |first| to |second|.
-  Cronet_RequestFinishedInfo_request_start_set(
-      second, Cronet_RequestFinishedInfo_request_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_request_start_get(first),
-            Cronet_RequestFinishedInfo_request_start_get(second));
-  Cronet_RequestFinishedInfo_dns_start_set(
-      second, Cronet_RequestFinishedInfo_dns_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_dns_start_get(first),
-            Cronet_RequestFinishedInfo_dns_start_get(second));
-  Cronet_RequestFinishedInfo_dns_end_set(
-      second, Cronet_RequestFinishedInfo_dns_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_dns_end_get(first),
-            Cronet_RequestFinishedInfo_dns_end_get(second));
-  Cronet_RequestFinishedInfo_connect_start_set(
-      second, Cronet_RequestFinishedInfo_connect_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_connect_start_get(first),
-            Cronet_RequestFinishedInfo_connect_start_get(second));
-  Cronet_RequestFinishedInfo_connect_end_set(
-      second, Cronet_RequestFinishedInfo_connect_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_connect_end_get(first),
-            Cronet_RequestFinishedInfo_connect_end_get(second));
-  Cronet_RequestFinishedInfo_ssl_start_set(
-      second, Cronet_RequestFinishedInfo_ssl_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_ssl_start_get(first),
-            Cronet_RequestFinishedInfo_ssl_start_get(second));
-  Cronet_RequestFinishedInfo_ssl_end_set(
-      second, Cronet_RequestFinishedInfo_ssl_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_ssl_end_get(first),
-            Cronet_RequestFinishedInfo_ssl_end_get(second));
-  Cronet_RequestFinishedInfo_sending_start_set(
-      second, Cronet_RequestFinishedInfo_sending_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_sending_start_get(first),
-            Cronet_RequestFinishedInfo_sending_start_get(second));
-  Cronet_RequestFinishedInfo_sending_end_set(
-      second, Cronet_RequestFinishedInfo_sending_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_sending_end_get(first),
-            Cronet_RequestFinishedInfo_sending_end_get(second));
-  Cronet_RequestFinishedInfo_push_start_set(
-      second, Cronet_RequestFinishedInfo_push_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_push_start_get(first),
-            Cronet_RequestFinishedInfo_push_start_get(second));
-  Cronet_RequestFinishedInfo_push_end_set(
-      second, Cronet_RequestFinishedInfo_push_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_push_end_get(first),
-            Cronet_RequestFinishedInfo_push_end_get(second));
-  Cronet_RequestFinishedInfo_response_start_set(
-      second, Cronet_RequestFinishedInfo_response_start_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_response_start_get(first),
-            Cronet_RequestFinishedInfo_response_start_get(second));
-  Cronet_RequestFinishedInfo_request_end_set(
-      second, Cronet_RequestFinishedInfo_request_end_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_request_end_get(first),
-            Cronet_RequestFinishedInfo_request_end_get(second));
-  Cronet_RequestFinishedInfo_socket_reused_set(
-      second, Cronet_RequestFinishedInfo_socket_reused_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_socket_reused_get(first),
-            Cronet_RequestFinishedInfo_socket_reused_get(second));
-  Cronet_RequestFinishedInfo_sent_byte_count_set(
-      second, Cronet_RequestFinishedInfo_sent_byte_count_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_sent_byte_count_get(first),
-            Cronet_RequestFinishedInfo_sent_byte_count_get(second));
-  Cronet_RequestFinishedInfo_received_byte_count_set(
-      second, Cronet_RequestFinishedInfo_received_byte_count_get(first));
-  EXPECT_EQ(Cronet_RequestFinishedInfo_received_byte_count_get(first),
-            Cronet_RequestFinishedInfo_received_byte_count_get(second));
+  Cronet_MetricsPtr test_metrics = Cronet_Metrics_Create();
+  EXPECT_EQ(Cronet_RequestFinishedInfo_metrics_get(first), nullptr);
+
+  Cronet_RequestFinishedInfo_metrics_set(first, test_metrics);
+  EXPECT_NE(Cronet_RequestFinishedInfo_metrics_get(first), nullptr);
+  Cronet_RequestFinishedInfo_metrics_set(first, nullptr);
+  EXPECT_EQ(Cronet_RequestFinishedInfo_metrics_get(first), nullptr);
+
+  Cronet_RequestFinishedInfo_metrics_move(first, test_metrics);
+  EXPECT_NE(Cronet_RequestFinishedInfo_metrics_get(first), nullptr);
+  Cronet_RequestFinishedInfo_metrics_move(first, nullptr);
+  EXPECT_EQ(Cronet_RequestFinishedInfo_metrics_get(first), nullptr);
+
+  Cronet_Metrics_Destroy(test_metrics);
+  // TODO(mef): Test array |annotations|.
+  Cronet_RequestFinishedInfo_finished_reason_set(
+      second, Cronet_RequestFinishedInfo_finished_reason_get(first));
+  EXPECT_EQ(Cronet_RequestFinishedInfo_finished_reason_get(first),
+            Cronet_RequestFinishedInfo_finished_reason_get(second));
   Cronet_UrlResponseInfoPtr test_response_info =
       Cronet_UrlResponseInfo_Create();
   EXPECT_EQ(Cronet_RequestFinishedInfo_response_info_get(first), nullptr);
diff --git a/components/dom_distiller/core/dom_distiller_store.h b/components/dom_distiller/core/dom_distiller_store.h
index e868200..d98e538 100644
--- a/components/dom_distiller/core/dom_distiller_store.h
+++ b/components/dom_distiller/core/dom_distiller_store.h
@@ -14,7 +14,7 @@
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/dom_distiller_model.h"
 #include "components/dom_distiller/core/dom_distiller_observer.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "components/sync/model/sync_change.h"
 #include "components/sync/model/sync_data.h"
 #include "components/sync/model/sync_error.h"
diff --git a/components/dom_distiller/standalone/content_extractor_browsertest.cc b/components/dom_distiller/standalone/content_extractor_browsertest.cc
index aa71a04b..152ed147 100644
--- a/components/dom_distiller/standalone/content_extractor_browsertest.cc
+++ b/components/dom_distiller/standalone/content_extractor_browsertest.cc
@@ -29,8 +29,8 @@
 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
 #include "components/dom_distiller/core/task_tracker.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -131,9 +131,8 @@
 
   // TODO(cjhopman): use an in-memory database instead of an on-disk one with
   // temporary directory.
-  std::unique_ptr<leveldb_proto::ProtoDatabaseImpl<ArticleEntry>> db(
-      new leveldb_proto::ProtoDatabaseImpl<ArticleEntry>(
-          background_task_runner));
+  auto db = leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<ArticleEntry>(
+      background_task_runner);
   std::unique_ptr<DomDistillerStore> dom_distiller_store(
       new DomDistillerStore(std::move(db), db_path));
 
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory.cc
index d691b40..295b79fb 100644
--- a/components/download/content/factory/download_service_factory.cc
+++ b/components/download/content/factory/download_service_factory.cc
@@ -22,7 +22,7 @@
 #include "components/download/internal/background_service/proto/entry.pb.h"
 #include "components/download/internal/background_service/scheduler/scheduler_impl.h"
 #include "components/download/public/task/empty_task_scheduler.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "content/public/browser/storage_partition.h"
 
 #if defined(OS_ANDROID)
@@ -107,7 +107,7 @@
 
   auto entry_db_storage_dir = storage_dir.Append(kEntryDBStorageDir);
   auto entry_db =
-      std::make_unique<leveldb_proto::ProtoDatabaseImpl<protodb::Entry>>(
+      leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<protodb::Entry>(
           background_task_runner);
   auto store = std::make_unique<DownloadStore>(entry_db_storage_dir,
                                                std::move(entry_db));
diff --git a/components/download/database/download_db_impl.cc b/components/download/database/download_db_impl.cc
index 50e34413..06f7477 100644
--- a/components/download/database/download_db_impl.cc
+++ b/components/download/database/download_db_impl.cc
@@ -12,7 +12,7 @@
 #include "components/download/database/download_db_conversions.h"
 #include "components/download/database/download_db_entry.h"
 #include "components/download/database/proto/download_entry.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace download {
 
@@ -51,8 +51,8 @@
     : DownloadDBImpl(
           download_namespace,
           database_dir,
-          std::make_unique<
-              leveldb_proto::ProtoDatabaseImpl<download_pb::DownloadDBEntry>>(
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              download_pb::DownloadDBEntry>(
               base::CreateSequencedTaskRunnerWithTraits(
                   {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
diff --git a/components/download/database/download_db_impl.h b/components/download/database/download_db_impl.h
index 39e6eea..9dac9f5 100644
--- a/components/download/database/download_db_impl.h
+++ b/components/download/database/download_db_impl.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/download/database/download_db.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace download_pb {
 class DownloadDBEntry;
diff --git a/components/download/internal/background_service/download_store.cc b/components/download/internal/background_service/download_store.cc
index c6961284..a03702c 100644
--- a/components/download/internal/background_service/download_store.cc
+++ b/components/download/internal/background_service/download_store.cc
@@ -11,7 +11,7 @@
 #include "components/download/internal/background_service/entry.h"
 #include "components/download/internal/background_service/proto/entry.pb.h"
 #include "components/download/internal/background_service/proto_conversions.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace download {
 
diff --git a/components/download/internal/background_service/download_store.h b/components/download/internal/background_service/download_store.h
index 953b298..3e580f2 100644
--- a/components/download/internal/background_service/download_store.h
+++ b/components/download/internal/background_service/download_store.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/download/internal/background_service/store.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace protodb {
 class Entry;
diff --git a/components/download/public/common/auto_resumption_handler.cc b/components/download/public/common/auto_resumption_handler.cc
index 3e01c85..3f1c6f6 100644
--- a/components/download/public/common/auto_resumption_handler.cc
+++ b/components/download/public/common/auto_resumption_handler.cc
@@ -170,10 +170,11 @@
 
   if (item->GetState() == download::DownloadItem::INTERRUPTED &&
       IsAutoResumableDownload(item) && SatisfiesNetworkRequirements(item)) {
+    downloads_to_retry_.insert(item);
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
-        base::BindOnce(&AutoResumptionHandler::ResumeDownload,
-                       weak_factory_.GetWeakPtr(), item),
+        base::BindOnce(&AutoResumptionHandler::ResumeDownloadImmediately,
+                       weak_factory_.GetWeakPtr()),
         kDownloadImmediateRetryDelay);
     return;
   }
@@ -185,11 +186,18 @@
   RecomputeTaskParams();
 }
 
-void AutoResumptionHandler::ResumeDownload(download::DownloadItem* download) {
-  if (SatisfiesNetworkRequirements(download))
-    download->Resume(false);
-  else
-    RecomputeTaskParams();
+void AutoResumptionHandler::OnDownloadDestroyed(download::DownloadItem* item) {
+  downloads_to_retry_.erase(item);
+}
+
+void AutoResumptionHandler::ResumeDownloadImmediately() {
+  for (auto* download : std::move(downloads_to_retry_)) {
+    if (SatisfiesNetworkRequirements(download))
+      download->Resume(false);
+    else
+      RecomputeTaskParams();
+  }
+  downloads_to_retry_.clear();
 }
 
 void AutoResumptionHandler::OnStartScheduledTask(
diff --git a/components/download/public/common/auto_resumption_handler.h b/components/download/public/common/auto_resumption_handler.h
index 0f36597..2feac61 100644
--- a/components/download/public/common/auto_resumption_handler.h
+++ b/components/download/public/common/auto_resumption_handler.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <memory>
+#include <set>
 #include <vector>
 
 #include "base/macros.h"
@@ -58,6 +59,7 @@
   // DownloadItem::Observer overrides.
   void OnDownloadUpdated(download::DownloadItem* item) override;
   void OnDownloadRemoved(download::DownloadItem* item) override;
+  void OnDownloadDestroyed(download::DownloadItem* item) override;
 
  private:
   // NetworkStatusListener::Observer implementation.
@@ -66,7 +68,7 @@
   void ResumePendingDownloads();
   void RecomputeTaskParams();
   void RescheduleTaskIfNecessary();
-  void ResumeDownload(download::DownloadItem* download);
+  void ResumeDownloadImmediately();
   bool SatisfiesNetworkRequirements(download::DownloadItem* download);
   bool IsAutoResumableDownload(download::DownloadItem* item);
 
@@ -76,8 +78,13 @@
 
   std::unique_ptr<Config> config_;
 
+  // List of downloads that are auto-resumable. These will be resumed as soon as
+  // network conditions becomes favorable.
   std::map<std::string, download::DownloadItem*> resumable_downloads_;
 
+  // A temporary list of downloads which are being retried immediately.
+  std::set<download::DownloadItem*> downloads_to_retry_;
+
   bool recompute_task_params_scheduled_ = false;
 
   base::WeakPtrFactory<AutoResumptionHandler> weak_factory_;
diff --git a/components/exo/display.cc b/components/exo/display.cc
index 6c5243a..d8065f4 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -45,19 +45,20 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Display, public:
 
-Display::Display() : file_helper_(std::unique_ptr<FileHelper>()) {}
+Display::Display()
+#if defined(USE_OZONE)
+    : client_native_pixmap_factory_(
+          gfx::CreateClientNativePixmapFactoryDmabuf())
+#endif
+{
+}
 
 #if defined(OS_CHROMEOS)
 Display::Display(NotificationSurfaceManager* notification_surface_manager,
                  InputMethodSurfaceManager* input_method_surface_manager,
                  std::unique_ptr<FileHelper> file_helper)
-    : file_helper_(std::move(file_helper))
-#if defined(USE_OZONE)
-      ,
-      client_native_pixmap_factory_(
-          gfx::CreateClientNativePixmapFactoryDmabuf())
-#endif  // defined(USE_OZONE)
-{
+    : Display() {
+  file_helper_ = std::move(file_helper);
   notification_surface_manager_ = notification_surface_manager;
   input_method_surface_manager_ = input_method_surface_manager;
 }
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index 12ee4e8..a6d9891 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -79,6 +79,8 @@
       "zcr_stylus_tools.h",
       "zwp_input_timestamps_manager.cc",
       "zwp_input_timestamps_manager.h",
+      "zwp_linux_explicit_synchronization.cc",
+      "zwp_linux_explicit_synchronization.h",
       "zwp_pointer_gestures.cc",
       "zwp_pointer_gestures.h",
       "zwp_text_input_manager.cc",
@@ -102,6 +104,7 @@
     "//third_party/wayland-protocols:input_timestamps_protocol",
     "//third_party/wayland-protocols:keyboard_configuration_protocol",
     "//third_party/wayland-protocols:keyboard_extension_protocol",
+    "//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
     "//third_party/wayland-protocols:notification_shell_protocol",
     "//third_party/wayland-protocols:pointer_gestures_protocol",
     "//third_party/wayland-protocols:presentation_time_protocol",
@@ -168,6 +171,14 @@
     "//testing/gtest",
     "//third_party/wayland:wayland_client",
   ]
+
+  if (is_chromeos) {
+    sources += [ "server_util_unittest.cc" ]
+    deps += [
+      "//ui/display",
+      "//ui/gfx",
+    ]
+  }
 }
 
 config("client_support_config") {
@@ -200,6 +211,7 @@
     "//third_party/wayland-protocols:fullscreen_shell_protocol",
     "//third_party/wayland-protocols:input_timestamps_protocol",
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
+    "//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
     "//third_party/wayland-protocols:presentation_time_protocol",
   ]
 
@@ -452,6 +464,25 @@
     configs += [ "//ui/gl:gl_config" ]
     deps += [ "//third_party/minigbm" ]
   }
+
+  executable("wayland_explicit_synchronization_client") {
+    sources = [
+      "clients/explicit_synchronization.cc",
+    ]
+
+    deps = [
+      ":client_support",
+      "//base",
+      "//build/config/linux/libdrm",
+      "//skia",
+      "//third_party/wayland:wayland_client",
+      "//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
+      "//ui/gl",
+    ]
+
+    configs += [ "//ui/gl:gl_config" ]
+  }
+
   if (enable_vulkan_wayland_client) {
     executable("wayland_vulkan_client") {
       sources = [
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 3e692f9..8679f43 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -8,6 +8,7 @@
 #include <fcntl.h>
 #include <fullscreen-shell-unstable-v1-client-protocol.h>
 #include <linux-dmabuf-unstable-v1-client-protocol.h>
+#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
 #include <presentation-time-client-protocol.h>
 #include <wayland-client-core.h>
 #include <wayland-client-protocol.h>
@@ -129,6 +130,11 @@
   } else if (strcmp(interface, "wl_output") == 0) {
     globals->output.reset(static_cast<wl_output*>(
         wl_registry_bind(registry, id, &wl_output_interface, 1)));
+  } else if (strcmp(interface, "zwp_linux_explicit_synchronization_v1") == 0) {
+    globals->linux_explicit_synchronization.reset(
+        static_cast<zwp_linux_explicit_synchronization_v1*>(wl_registry_bind(
+            registry, id, &zwp_linux_explicit_synchronization_v1_interface,
+            1)));
   }
 }
 
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h
index 6799ad1..0513d411 100644
--- a/components/exo/wayland/clients/client_base.h
+++ b/components/exo/wayland/clients/client_base.h
@@ -76,6 +76,8 @@
     std::unique_ptr<zaura_shell> aura_shell;
     std::unique_ptr<zwp_fullscreen_shell_v1> fullscreen_shell;
     std::unique_ptr<zwp_input_timestamps_manager_v1> input_timestamps_manager;
+    std::unique_ptr<zwp_linux_explicit_synchronization_v1>
+        linux_explicit_synchronization;
   };
 
   struct Buffer {
diff --git a/components/exo/wayland/clients/client_helper.cc b/components/exo/wayland/clients/client_helper.cc
index 41dcab3..29531c72 100644
--- a/components/exo/wayland/clients/client_helper.cc
+++ b/components/exo/wayland/clients/client_helper.cc
@@ -6,6 +6,7 @@
 
 #include <input-timestamps-unstable-v1-client-protocol.h>
 #include <linux-dmabuf-unstable-v1-client-protocol.h>
+#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
 #include <presentation-time-client-protocol.h>
 #include <wayland-client-core.h>
 #include <wayland-client-protocol.h>
@@ -49,12 +50,18 @@
 DEFAULT_DELETER(zaura_shell, zaura_shell_destroy)
 DEFAULT_DELETER(zaura_surface, zaura_surface_destroy)
 DEFAULT_DELETER(zaura_output, zaura_output_destroy)
+DEFAULT_DELETER(zwp_linux_buffer_release_v1,
+                zwp_linux_buffer_release_v1_destroy)
 DEFAULT_DELETER(zwp_fullscreen_shell_v1, zwp_fullscreen_shell_v1_destroy)
 DEFAULT_DELETER(zwp_input_timestamps_manager_v1,
                 zwp_input_timestamps_manager_v1_destroy);
 DEFAULT_DELETER(zwp_input_timestamps_v1, zwp_input_timestamps_v1_destroy)
 DEFAULT_DELETER(zwp_linux_buffer_params_v1, zwp_linux_buffer_params_v1_destroy)
 DEFAULT_DELETER(zwp_linux_dmabuf_v1, zwp_linux_dmabuf_v1_destroy)
+DEFAULT_DELETER(zwp_linux_explicit_synchronization_v1,
+                zwp_linux_explicit_synchronization_v1_destroy)
+DEFAULT_DELETER(zwp_linux_surface_synchronization_v1,
+                zwp_linux_surface_synchronization_v1_destroy)
 
 #if defined(USE_GBM)
 DEFAULT_DELETER(gbm_bo, gbm_bo_destroy)
diff --git a/components/exo/wayland/clients/client_helper.h b/components/exo/wayland/clients/client_helper.h
index 56c8db52..9b068da9 100644
--- a/components/exo/wayland/clients/client_helper.h
+++ b/components/exo/wayland/clients/client_helper.h
@@ -9,6 +9,7 @@
 #include <fullscreen-shell-unstable-v1-client-protocol.h>
 #include <input-timestamps-unstable-v1-client-protocol.h>
 #include <linux-dmabuf-unstable-v1-client-protocol.h>
+#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
 #include <presentation-time-client-protocol.h>
 #include <wayland-client-core.h>
 #include <wayland-client-protocol.h>
@@ -55,11 +56,14 @@
 DEFAULT_DELETER_FDECL(zaura_shell)
 DEFAULT_DELETER_FDECL(zaura_surface)
 DEFAULT_DELETER_FDECL(zaura_output)
+DEFAULT_DELETER_FDECL(zwp_linux_buffer_release_v1)
 DEFAULT_DELETER_FDECL(zwp_fullscreen_shell_v1)
 DEFAULT_DELETER_FDECL(zwp_input_timestamps_manager_v1)
 DEFAULT_DELETER_FDECL(zwp_input_timestamps_v1)
 DEFAULT_DELETER_FDECL(zwp_linux_buffer_params_v1)
 DEFAULT_DELETER_FDECL(zwp_linux_dmabuf_v1)
+DEFAULT_DELETER_FDECL(zwp_linux_explicit_synchronization_v1)
+DEFAULT_DELETER_FDECL(zwp_linux_surface_synchronization_v1)
 
 #if defined(USE_GBM)
 DEFAULT_DELETER_FDECL(gbm_bo)
diff --git a/components/exo/wayland/clients/explicit_synchronization.cc b/components/exo/wayland/clients/explicit_synchronization.cc
new file mode 100644
index 0000000..6a68619
--- /dev/null
+++ b/components/exo/wayland/clients/explicit_synchronization.cc
@@ -0,0 +1,138 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/clients/client_base.h"
+
+#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "components/exo/wayland/clients/client_helper.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "ui/gl/gl_bindings.h"
+
+namespace exo {
+namespace wayland {
+namespace clients {
+namespace {
+
+void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
+  bool* frame_callback_pending = static_cast<bool*>(data);
+  *frame_callback_pending = false;
+}
+
+}  // namespace
+
+// This client exercises the zwp_linux_explicit_synchronization_unstable_v1
+// wayland protocol, which enables buffer acquire and release synchronization
+// between client and server using dma-fences.
+class ExplicitSynchronizationClient : public ClientBase {
+ public:
+  ExplicitSynchronizationClient() = default;
+
+  // Initialize and run client main loop.
+  void Run();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExplicitSynchronizationClient);
+};
+
+void ExplicitSynchronizationClient::Run() {
+  wl_callback_listener frame_listener = {FrameCallback};
+
+  /* With a 60Hz redraw rate this completes a half-oscillation in 3 seconds */
+  static const int kMaxDrawStep = 180;
+  int draw_step = 0;
+  int draw_step_dir = 1;
+
+  // A valid GL context with support for fd fences is required for this client.
+  CHECK(gr_context_)
+      << "A valid GL context is required. Try running with --use-drm.";
+  CHECK_EQ(egl_sync_type_, static_cast<unsigned>(EGL_SYNC_NATIVE_FENCE_ANDROID))
+      << "EGL doesn't support the EGL_ANDROID_native_fence_sync extension.";
+
+  // The server needs to support the linux_explicit_synchronization protocol.
+  CHECK(globals_.linux_explicit_synchronization)
+      << "Server doesn't support zwp_linux_explicit_synchronization_v1.";
+  std::unique_ptr<zwp_linux_surface_synchronization_v1> surface_synchronization(
+      zwp_linux_explicit_synchronization_v1_get_synchronization(
+          globals_.linux_explicit_synchronization.get(), surface_.get()));
+  DCHECK(surface_synchronization);
+
+  std::unique_ptr<wl_callback> frame_callback;
+  bool frame_callback_pending = false;
+
+  do {
+    if (frame_callback_pending)
+      continue;
+
+    Buffer* buffer = DequeueBuffer();
+    if (!buffer)
+      continue;
+
+    /* Oscillate between 0 and kMaxDrawStep */
+    draw_step += draw_step_dir;
+    if (draw_step == 0 || draw_step == kMaxDrawStep)
+      draw_step_dir = -draw_step_dir;
+
+    SkCanvas* canvas = buffer->sk_surface->getCanvas();
+    float draw_step_percent = static_cast<float>(draw_step) / kMaxDrawStep;
+    canvas->clear(
+        SkColor4f{0.0, draw_step_percent, 1.0 - draw_step_percent, 1.0}
+            .toSkColor());
+
+    // Create an EGLSyncKHR object to signal when rendering is done.
+    gr_context_->flush();
+    buffer->egl_sync.reset(new ScopedEglSync(
+        eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type_, nullptr)));
+    DCHECK(buffer->egl_sync->is_valid());
+    glFlush();
+
+    wl_surface_set_buffer_scale(surface_.get(), scale_);
+    wl_surface_set_buffer_transform(surface_.get(), transform_);
+    wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
+                      surface_size_.height());
+    wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
+
+    // TODO(afrantzis): Acquire a dma-fence fd and use
+    // zwp_surface_synchronization_v1.set_acquire_fence to synchronize once
+    // the server supports it.
+    eglClientWaitSyncKHR(eglGetCurrentDisplay(), buffer->egl_sync->get(),
+                         EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+
+    // Set up the frame callback.
+    frame_callback_pending = true;
+    frame_callback.reset(wl_surface_frame(surface_.get()));
+    wl_callback_add_listener(frame_callback.get(), &frame_listener,
+                             &frame_callback_pending);
+
+    wl_surface_commit(surface_.get());
+    wl_display_flush(display_.get());
+  } while (wl_display_dispatch(display_.get()) != -1);
+}
+
+}  // namespace clients
+}  // namespace wayland
+}  // namespace exo
+
+int main(int argc, char* argv[]) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  exo::wayland::clients::ClientBase::InitParams params;
+  if (!params.FromCommandLine(*command_line))
+    return 1;
+
+  base::MessageLoopForUI message_loop;
+  exo::wayland::clients::ExplicitSynchronizationClient client;
+  if (!client.Init(params))
+    return 1;
+
+  client.Run();
+
+  return 0;
+}
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index fe3da637f..9a572a8 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -13,6 +13,7 @@
 #include <input-timestamps-unstable-v1-server-protocol.h>
 #include <keyboard-configuration-unstable-v1-server-protocol.h>
 #include <keyboard-extension-unstable-v1-server-protocol.h>
+#include <linux-explicit-synchronization-unstable-v1-server-protocol.h>
 #include <linux/input.h>
 #include <notification-shell-unstable-v1-server-protocol.h>
 #include <pointer-gestures-unstable-v1-server-protocol.h>
@@ -111,6 +112,7 @@
 #include "components/exo/wayland/zcr_remote_shell.h"
 #include "components/exo/wayland/zcr_stylus_tools.h"
 #include "components/exo/wayland/zwp_input_timestamps_manager.h"
+#include "components/exo/wayland/zwp_linux_explicit_synchronization.h"
 #include "components/exo/wayland/zwp_pointer_gestures.h"
 #include "components/exo/wayland/zwp_text_input_manager.h"
 #include "components/exo/wayland/zxdg_shell.h"
@@ -1865,6 +1867,9 @@
                    display_, bind_text_input_manager);
   wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, display_,
                    bind_xdg_shell_v6);
+  wl_global_create(wl_display_.get(),
+                   &zwp_linux_explicit_synchronization_v1_interface, 1,
+                   display_, bind_linux_explicit_synchronization);
 #endif
 
 #if defined(USE_FULLSCREEN_SHELL)
diff --git a/components/exo/wayland/server_util.cc b/components/exo/wayland/server_util.cc
index 3202c1c..42ef5d7 100644
--- a/components/exo/wayland/server_util.cc
+++ b/components/exo/wayland/server_util.cc
@@ -97,11 +97,11 @@
     float default_dsf,
     const gfx::Size& size_in_client_pixel,
     const gfx::Rect& work_area_in_dp) {
-  gfx::Rect work_area_in_p = display.work_area();
-  work_area_in_p.Offset(-display.bounds().x(), -display.bounds().y());
+  gfx::Rect work_area_in_display = display.work_area();
+  work_area_in_display.Offset(-display.bounds().x(), -display.bounds().y());
   gfx::Rect work_area_in_client_pixel = ScaleBoundsToPixelSnappedToParent(
       size_in_client_pixel, display.bounds().size(), default_dsf,
-      work_area_in_dp);
+      work_area_in_display);
   gfx::Insets insets_in_client_pixel =
       gfx::Rect(size_in_client_pixel).InsetsFrom(work_area_in_client_pixel);
 
diff --git a/components/exo/wayland/server_util_unittest.cc b/components/exo/wayland/server_util_unittest.cc
new file mode 100644
index 0000000..3bd9802
--- /dev/null
+++ b/components/exo/wayland/server_util_unittest.cc
@@ -0,0 +1,33 @@
+// 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 "components/exo/wayland/server_util.h"
+#include "components/exo/test/exo_test_base.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/insets.h"
+
+namespace exo {
+
+using ServerUtilTest = test::ExoTestBase;
+
+TEST_F(ServerUtilTest, GetWorkAreaInsetsInClientPixel) {
+  UpdateDisplay("3000x2000*2.25,1920x1080");
+  auto display = display::Screen::GetScreen()->GetPrimaryDisplay();
+
+  gfx::Insets insets = wayland::GetWorkAreaInsetsInClientPixel(
+      display, 2.25f, gfx::Size(3000, 2000), display.work_area());
+  EXPECT_EQ(gfx::Insets(0, 0, 128, 0), insets);
+
+  auto secondary_display = GetSecondaryDisplay();
+  gfx::Size secondary_size(1920, 1080);
+  gfx::Size secondary_size_in_client_pixel =
+      gfx::ScaleToRoundedSize(secondary_size, 2.25f);
+  gfx::Insets secondary_insets = wayland::GetWorkAreaInsetsInClientPixel(
+      secondary_display, 2.25f, secondary_size_in_client_pixel,
+      secondary_display.work_area());
+  EXPECT_EQ(gfx::Insets(0, 0, 126, 0), secondary_insets);
+}
+
+}  // namespace exo
diff --git a/components/exo/wayland/zwp_linux_explicit_synchronization.cc b/components/exo/wayland/zwp_linux_explicit_synchronization.cc
new file mode 100644
index 0000000..27a16ff14
--- /dev/null
+++ b/components/exo/wayland/zwp_linux_explicit_synchronization.cc
@@ -0,0 +1,131 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/zwp_linux_explicit_synchronization.h"
+
+#include <linux-explicit-synchronization-unstable-v1-server-protocol.h>
+
+#include "components/exo/surface.h"
+#include "components/exo/surface_observer.h"
+#include "components/exo/wayland/server_util.h"
+
+namespace exo {
+namespace wayland {
+
+namespace {
+
+// A property key containing a pointer to the surface_synchronization resource
+// associated with the surface object.
+DEFINE_UI_CLASS_PROPERTY_KEY(wl_resource*,
+                             kSurfaceSynchronizationResource,
+                             nullptr);
+
+////////////////////////////////////////////////////////////////////////////////
+// linux_surface_synchronization_v1 interface:
+
+// Implements the surface synchronization interface, providing explicit
+// synchronization for surface buffers using dma-fences.
+class LinuxSurfaceSynchronization : public SurfaceObserver {
+ public:
+  explicit LinuxSurfaceSynchronization(wl_resource* resource, Surface* surface)
+      : surface_(surface) {
+    surface_->AddSurfaceObserver(this);
+    surface_->SetProperty(kSurfaceSynchronizationResource, resource);
+  }
+  ~LinuxSurfaceSynchronization() override {
+    if (surface_) {
+      surface_->RemoveSurfaceObserver(this);
+      surface_->SetProperty(kSurfaceSynchronizationResource,
+                            static_cast<wl_resource*>(nullptr));
+    }
+  }
+
+  // Overridden from SurfaceObserver:
+  void OnSurfaceDestroying(Surface* surface) override {
+    surface->RemoveSurfaceObserver(this);
+    surface_ = nullptr;
+  }
+
+ private:
+  Surface* surface_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinuxSurfaceSynchronization);
+};
+
+void linux_surface_synchronization_destroy(struct wl_client* client,
+                                           struct wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void linux_surface_synchronization_set_acquire_fence(wl_client* client,
+                                                     wl_resource* resource,
+                                                     int32_t fd) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void linux_surface_synchronization_get_release(wl_client* client,
+                                               wl_resource* resource,
+                                               uint32_t id) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+const struct zwp_linux_surface_synchronization_v1_interface
+    linux_surface_synchronization_implementation = {
+        linux_surface_synchronization_destroy,
+        linux_surface_synchronization_set_acquire_fence,
+        linux_surface_synchronization_get_release,
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// linux_explicit_synchronization_v1 interface:
+
+void linux_explicit_synchronization_destroy(struct wl_client* client,
+                                            struct wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void linux_explicit_synchronization_get_synchronization(
+    wl_client* client,
+    wl_resource* resource,
+    uint32_t id,
+    wl_resource* surface_resource) {
+  Surface* surface = GetUserDataAs<Surface>(surface_resource);
+  if (surface->GetProperty(kSurfaceSynchronizationResource) != nullptr) {
+    wl_resource_post_error(
+        resource,
+        ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS,
+        "a synchronization object for the surface already exists");
+    return;
+  }
+
+  wl_resource* linux_surface_synchronization_resource = wl_resource_create(
+      client, &zwp_linux_surface_synchronization_v1_interface, 1, id);
+
+  SetImplementation(linux_surface_synchronization_resource,
+                    &linux_surface_synchronization_implementation,
+                    std::make_unique<LinuxSurfaceSynchronization>(
+                        linux_surface_synchronization_resource, surface));
+}
+
+const struct zwp_linux_explicit_synchronization_v1_interface
+    linux_explicit_synchronization_implementation = {
+        linux_explicit_synchronization_destroy,
+        linux_explicit_synchronization_get_synchronization};
+
+}  // namespace
+
+void bind_linux_explicit_synchronization(wl_client* client,
+                                         void* data,
+                                         uint32_t version,
+                                         uint32_t id) {
+  wl_resource* resource = wl_resource_create(
+      client, &zwp_linux_explicit_synchronization_v1_interface, 1, id);
+
+  wl_resource_set_implementation(resource,
+                                 &linux_explicit_synchronization_implementation,
+                                 nullptr, nullptr);
+}
+
+}  // namespace wayland
+}  // namespace exo
diff --git a/components/exo/wayland/zwp_linux_explicit_synchronization.h b/components/exo/wayland/zwp_linux_explicit_synchronization.h
new file mode 100644
index 0000000..b73ed63
--- /dev/null
+++ b/components/exo/wayland/zwp_linux_explicit_synchronization.h
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_WAYLAND_ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_H_
+#define COMPONENTS_EXO_WAYLAND_ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_H_
+
+#include <stdint.h>
+
+struct wl_client;
+
+namespace exo {
+namespace wayland {
+
+void bind_linux_explicit_synchronization(wl_client* client,
+                                         void* data,
+                                         uint32_t version,
+                                         uint32_t id);
+
+}  // namespace wayland
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_WAYLAND_ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_H_
diff --git a/components/feature_engagement/internal/persistent_availability_store.cc b/components/feature_engagement/internal/persistent_availability_store.cc
index 54152c1c..46f6269 100644
--- a/components/feature_engagement/internal/persistent_availability_store.cc
+++ b/components/feature_engagement/internal/persistent_availability_store.cc
@@ -15,7 +15,7 @@
 #include "components/feature_engagement/internal/proto/availability.pb.h"
 #include "components/feature_engagement/internal/stats.h"
 #include "components/feature_engagement/public/feature_list.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace feature_engagement {
 
diff --git a/components/feature_engagement/internal/persistent_availability_store.h b/components/feature_engagement/internal/persistent_availability_store.h
index 808d0cfb3..5e0373e 100644
--- a/components/feature_engagement/internal/persistent_availability_store.h
+++ b/components/feature_engagement/internal/persistent_availability_store.h
@@ -14,7 +14,7 @@
 #include "base/macros.h"
 #include "components/feature_engagement/internal/proto/availability.pb.h"
 #include "components/feature_engagement/public/feature_list.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace base {
 class FilePath;
diff --git a/components/feature_engagement/internal/persistent_availability_store_unittest.cc b/components/feature_engagement/internal/persistent_availability_store_unittest.cc
index 0e55bfe..595478bc8 100644
--- a/components/feature_engagement/internal/persistent_availability_store_unittest.cc
+++ b/components/feature_engagement/internal/persistent_availability_store_unittest.cc
@@ -19,7 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "components/feature_engagement/internal/proto/availability.pb.h"
 #include "components/feature_engagement/public/feature_list.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/feature_engagement/internal/persistent_event_store.cc b/components/feature_engagement/internal/persistent_event_store.cc
index d6a7bed..1f3d03f 100644
--- a/components/feature_engagement/internal/persistent_event_store.cc
+++ b/components/feature_engagement/internal/persistent_event_store.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "components/feature_engagement/internal/stats.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace feature_engagement {
 namespace {
diff --git a/components/feature_engagement/internal/persistent_event_store.h b/components/feature_engagement/internal/persistent_event_store.h
index 42cc8795..1bf5a7e 100644
--- a/components/feature_engagement/internal/persistent_event_store.h
+++ b/components/feature_engagement/internal/persistent_event_store.h
@@ -13,7 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/feature_engagement/internal/event_store.h"
 #include "components/feature_engagement/internal/proto/feature_event.pb.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace feature_engagement {
 
diff --git a/components/feature_engagement/internal/persistent_event_store_unittest.cc b/components/feature_engagement/internal/persistent_event_store_unittest.cc
index 39ff9aa1..8297481 100644
--- a/components/feature_engagement/internal/persistent_event_store_unittest.cc
+++ b/components/feature_engagement/internal/persistent_event_store_unittest.cc
@@ -12,7 +12,7 @@
 #include "components/feature_engagement/internal/proto/feature_event.pb.h"
 #include "components/feature_engagement/internal/stats.h"
 #include "components/feature_engagement/internal/test/event_util.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/feature_engagement/internal/tracker_impl.cc b/components/feature_engagement/internal/tracker_impl.cc
index 83717f30..3e8ce5f 100644
--- a/components/feature_engagement/internal/tracker_impl.cc
+++ b/components/feature_engagement/internal/tracker_impl.cc
@@ -33,8 +33,7 @@
 #include "components/feature_engagement/internal/system_time_provider.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/feature_list.h"
-#include "components/leveldb_proto/proto_database_impl.h"
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace feature_engagement {
 
diff --git a/components/feed/core/feed_content_database.cc b/components/feed/core/feed_content_database.cc
index defec8a1..7e06660 100644
--- a/components/feed/core/feed_content_database.cc
+++ b/components/feed/core/feed_content_database.cc
@@ -16,7 +16,7 @@
 #include "components/feed/core/feed_content_mutation.h"
 #include "components/feed/core/feed_content_operation.h"
 #include "components/feed/core/proto/content_storage.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace feed {
 
@@ -56,11 +56,10 @@
 FeedContentDatabase::FeedContentDatabase(const base::FilePath& database_folder)
     : FeedContentDatabase(
           database_folder,
-          std::make_unique<
-              leveldb_proto::ProtoDatabaseImpl<ContentStorageProto>>(
-              base::CreateSequencedTaskRunnerWithTraits(
-                  {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              ContentStorageProto>(base::CreateSequencedTaskRunnerWithTraits(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
 
 FeedContentDatabase::FeedContentDatabase(
     const base::FilePath& database_folder,
diff --git a/components/feed/core/feed_content_database.h b/components/feed/core/feed_content_database.h
index 5345212..209f7e94 100644
--- a/components/feed/core/feed_content_database.h
+++ b/components/feed/core/feed_content_database.h
@@ -12,7 +12,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace feed {
 
diff --git a/components/feed/core/feed_image_database.cc b/components/feed/core/feed_image_database.cc
index 3176a3c..57601876 100644
--- a/components/feed/core/feed_image_database.cc
+++ b/components/feed/core/feed_image_database.cc
@@ -16,7 +16,7 @@
 #include "base/time/time.h"
 #include "components/feed/core/proto/cached_image.pb.h"
 #include "components/feed/core/time_serialization.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace feed {
 
@@ -36,10 +36,10 @@
 FeedImageDatabase::FeedImageDatabase(const base::FilePath& database_dir)
     : FeedImageDatabase(
           database_dir,
-          std::make_unique<leveldb_proto::ProtoDatabaseImpl<CachedImageProto>>(
-              base::CreateSequencedTaskRunnerWithTraits(
-                  {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              CachedImageProto>(base::CreateSequencedTaskRunnerWithTraits(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
           base::DefaultClock::GetInstance()) {}
 
 FeedImageDatabase::FeedImageDatabase(
diff --git a/components/feed/core/feed_image_database.h b/components/feed/core/feed_image_database.h
index c319ff6e..90c8a39 100644
--- a/components/feed/core/feed_image_database.h
+++ b/components/feed/core/feed_image_database.h
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace base {
 class Clock;
diff --git a/components/feed/core/feed_image_manager_unittest.cc b/components/feed/core/feed_image_manager_unittest.cc
index 0be4e3b..81ea896 100644
--- a/components/feed/core/feed_image_manager_unittest.cc
+++ b/components/feed/core/feed_image_manager_unittest.cc
@@ -81,7 +81,7 @@
   ~FeedImageManagerTest() override {
     feed_image_manager_.reset();
     // We need to run until idle after deleting the database, because
-    // ProtoDatabaseImpl deletes the actual LevelDB asynchronously.
+    // ProtoDatabase deletes the actual LevelDB asynchronously.
     RunUntilIdle();
   }
 
diff --git a/components/feed/core/feed_journal_database.cc b/components/feed/core/feed_journal_database.cc
index 858cff3..412ee24 100644
--- a/components/feed/core/feed_journal_database.cc
+++ b/components/feed/core/feed_journal_database.cc
@@ -13,7 +13,7 @@
 #include "components/feed/core/feed_journal_mutation.h"
 #include "components/feed/core/feed_journal_operation.h"
 #include "components/feed/core/proto/journal_storage.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace feed {
 
@@ -46,11 +46,10 @@
 FeedJournalDatabase::FeedJournalDatabase(const base::FilePath& database_folder)
     : FeedJournalDatabase(
           database_folder,
-          std::make_unique<
-              leveldb_proto::ProtoDatabaseImpl<JournalStorageProto>>(
-              base::CreateSequencedTaskRunnerWithTraits(
-                  {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              JournalStorageProto>(base::CreateSequencedTaskRunnerWithTraits(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
 
 FeedJournalDatabase::FeedJournalDatabase(
     const base::FilePath& database_folder,
diff --git a/components/feed/core/feed_journal_database.h b/components/feed/core/feed_journal_database.h
index d8093f78..9ca9d41 100644
--- a/components/feed/core/feed_journal_database.h
+++ b/components/feed/core/feed_journal_database.h
@@ -14,7 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace feed {
 
diff --git a/components/feed/core/feed_logging_metrics.cc b/components/feed/core/feed_logging_metrics.cc
index 908a6f2b..9b83b0a 100644
--- a/components/feed/core/feed_logging_metrics.cc
+++ b/components/feed/core/feed_logging_metrics.cc
@@ -222,8 +222,9 @@
 }
 
 void FeedLoggingMetrics::OnSpinnerShown(base::TimeDelta shown_time) {
-  base::UmaHistogramLongTimes(
-      "ContentSuggestions.FetchPendingSpinner.VisibleDuration", shown_time);
+  base::UmaHistogramTimes(
+      "ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration",
+      shown_time);
 }
 
 void FeedLoggingMetrics::ReportScrolledAfterOpen() {
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
index b0cd81b..cbb39cb 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -70,7 +70,7 @@
   void TearDown() override {
     encryption_provider_.reset();
 
-    // |encryption_provider_| owns a ProtoDatabaseImpl whose destructor deletes
+    // |encryption_provider_| owns a ProtoDatabase whose destructor deletes
     // the underlying LevelDB database on the task runner.
     base::RunLoop().RunUntilIdle();
   }
diff --git a/components/gcm_driver/crypto/gcm_key_store.cc b/components/gcm_driver/crypto/gcm_key_store.cc
index d8be596..09cb0e6 100644
--- a/components/gcm_driver/crypto/gcm_key_store.cc
+++ b/components/gcm_driver/crypto/gcm_key_store.cc
@@ -16,7 +16,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
 #include "components/gcm_driver/crypto/p256_key_util.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "crypto/random.h"
 
 namespace gcm {
@@ -294,8 +294,9 @@
 
   state_ = State::INITIALIZING;
 
-  database_.reset(new leveldb_proto::ProtoDatabaseImpl<EncryptionData>(
-      blocking_task_runner_));
+  database_ =
+      leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<EncryptionData>(
+          blocking_task_runner_);
 
   database_->Init(
       kDatabaseUMAClientName, key_store_path_,
diff --git a/components/gcm_driver/crypto/gcm_key_store_unittest.cc b/components/gcm_driver/crypto/gcm_key_store_unittest.cc
index 50086ec01..a30f26cc 100644
--- a/components/gcm_driver/crypto/gcm_key_store_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_key_store_unittest.cc
@@ -18,7 +18,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/gcm_driver/crypto/p256_key_util.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "crypto/random.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -67,7 +67,7 @@
   void TearDown() override {
     gcm_key_store_.reset();
 
-    // |gcm_key_store_| owns a ProtoDatabaseImpl whose destructor deletes the
+    // |gcm_key_store_| owns a ProtoDatabase whose destructor deletes the
     // underlying LevelDB database on the task runner.
     base::RunLoop().RunUntilIdle();
   }
diff --git a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
index 052ead602..8e77f01 100644
--- a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
+++ b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
@@ -125,7 +125,7 @@
 void InstanceIDDriverTest::TearDown() {
   driver_.reset();
   gcm_driver_.reset();
-  // |gcm_driver_| owns a GCMKeyStore that owns a ProtoDatabaseImpl whose
+  // |gcm_driver_| owns a GCMKeyStore that owns a ProtoDatabase whose
   // destructor deletes the underlying LevelDB on the task runner.
   base::RunLoop().RunUntilIdle();
 }
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
index 8c4fdd2..db0d293 100644
--- a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
+++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
@@ -13,7 +13,7 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/image_fetcher/core/cache/proto/cached_image_metadata.pb.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 using image_fetcher::CachedImageMetadataProto;
 
@@ -61,9 +61,8 @@
     base::Clock* clock)
     : ImageMetadataStoreLevelDB(
           database_dir,
-          std::make_unique<
-              leveldb_proto::ProtoDatabaseImpl<CachedImageMetadataProto>>(
-              task_runner),
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              CachedImageMetadataProto>(task_runner),
           clock) {}
 
 ImageMetadataStoreLevelDB::ImageMetadataStoreLevelDB(
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb.h b/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
index 8ed864f..0a751bc 100644
--- a/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
+++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb.h
@@ -13,7 +13,7 @@
 #include "base/time/time.h"
 #include "components/image_fetcher/core/cache/image_metadata_store.h"
 #include "components/image_fetcher/core/cache/image_store_types.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace base {
 class Clock;
diff --git a/components/image_fetcher/core/cached_image_fetcher_unittest.cc b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
index 1e46e64..fec79d6 100644
--- a/components/image_fetcher/core/cached_image_fetcher_unittest.cc
+++ b/components/image_fetcher/core/cached_image_fetcher_unittest.cc
@@ -67,7 +67,7 @@
   ~ComponentizedCachedImageFetcherTest() override {
     cached_image_fetcher_.reset();
     // We need to run until idle after deleting the database, because
-    // ProtoDatabaseImpl deletes the actual LevelDB asynchronously.
+    // ProtoDatabase deletes the actual LevelDB asynchronously.
     RunUntilIdle();
   }
 
diff --git a/components/invalidation/impl/fcm_invalidation_listener.cc b/components/invalidation/impl/fcm_invalidation_listener.cc
index da07ce6..41dc2a4 100644
--- a/components/invalidation/impl/fcm_invalidation_listener.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener.cc
@@ -241,16 +241,12 @@
 
 std::unique_ptr<base::DictionaryValue>
 FCMInvalidationListener::CollectDebugData() const {
-  std::unique_ptr<base::DictionaryValue> return_value(
-      new base::DictionaryValue());
-  // For each topic record if the subscription was successful.
-  TopicSet active_topics =
-      per_user_topic_registration_manager_->GetRegisteredIds();
+  std::unique_ptr<base::DictionaryValue> return_value =
+      per_user_topic_registration_manager_->CollectDebugData();
   for (const Topic& topic : registered_topics_) {
-    std::string status = "Registered";
-    if (active_topics.count(topic) == 0)
-      status = "Unregistered";
-    return_value->SetString(topic, status);
+    if (!return_value->HasKey(topic)) {
+      return_value->SetString(topic, "Unregistered");
+    }
   }
   return return_value;
 }
diff --git a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
index 76810e3..7e4d582 100644
--- a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
@@ -184,7 +184,8 @@
             nullptr /* identity_provider */,
             nullptr /* pref_service */,
             nullptr /* loader_factory */,
-            base::BindRepeating(&syncer::JsonUnsafeParser::Parse)) {}
+            base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
+            "fake_sender_id") {}
   ~MockRegistrationManager() override {}
   MOCK_METHOD2(UpdateRegisteredTopics,
                void(const TopicSet& topics, const std::string& token));
diff --git a/components/invalidation/impl/fcm_invalidation_service.cc b/components/invalidation/impl/fcm_invalidation_service.cc
index b922d6d..f411b5a 100644
--- a/components/invalidation/impl/fcm_invalidation_service.cc
+++ b/components/invalidation/impl/fcm_invalidation_service.cc
@@ -19,6 +19,8 @@
 
 namespace {
 const char kApplicationName[] = "com.google.chrome.fcm.invalidations";
+// Sender ID coming from the Firebase console.
+const char kInvalidationGCMSenderId[] = "8181035976";
 }
 
 namespace invalidation {
@@ -183,7 +185,8 @@
   DCHECK(IsReadyToStart());
 
   auto network = std::make_unique<syncer::FCMNetworkHandler>(
-      gcm_driver_, instance_id_driver_);
+      gcm_driver_, instance_id_driver_, kInvalidationGCMSenderId,
+      kApplicationName);
   // The order of calls is important. Do not change.
   // We should start listening before requesting the id, because
   // valid id is only generated, once there is an app handler
@@ -193,7 +196,7 @@
 
   invalidator_ = std::make_unique<syncer::FCMInvalidator>(
       std::move(network), identity_provider_, pref_service_, loader_factory_,
-      parse_json_);
+      parse_json_, kInvalidationGCMSenderId);
   invalidator_->RegisterHandler(this);
   DoUpdateRegisteredIdsIfNeeded();
 }
diff --git a/components/invalidation/impl/fcm_invalidator.cc b/components/invalidation/impl/fcm_invalidator.cc
index 3c0f7e4..055ceef 100644
--- a/components/invalidation/impl/fcm_invalidator.cc
+++ b/components/invalidation/impl/fcm_invalidator.cc
@@ -19,10 +19,11 @@
     invalidation::IdentityProvider* identity_provider,
     PrefService* pref_service,
     network::mojom::URLLoaderFactory* loader_factory,
-    const ParseJSONCallback& parse_json)
+    const ParseJSONCallback& parse_json,
+    const std::string& project_id)
     : invalidation_listener_(std::move(network_channel)) {
   auto registration_manager = std::make_unique<PerUserTopicRegistrationManager>(
-      identity_provider, pref_service, loader_factory, parse_json);
+      identity_provider, pref_service, loader_factory, parse_json, project_id);
   invalidation_listener_.Start(
       base::BindOnce(&CreatePerUserTopicInvalidationClient), this,
       std::move(registration_manager));
diff --git a/components/invalidation/impl/fcm_invalidator.h b/components/invalidation/impl/fcm_invalidator.h
index 92ca158..5ac250da 100644
--- a/components/invalidation/impl/fcm_invalidator.h
+++ b/components/invalidation/impl/fcm_invalidator.h
@@ -32,7 +32,8 @@
                  invalidation::IdentityProvider* identity_provider,
                  PrefService* pref_service,
                  network::mojom::URLLoaderFactory* loader_factory,
-                 const ParseJSONCallback& parse_json);
+                 const ParseJSONCallback& parse_json,
+                 const std::string& project_id);
 
   ~FCMInvalidator() override;
 
diff --git a/components/invalidation/impl/fcm_invalidator_unittest.cc b/components/invalidation/impl/fcm_invalidator_unittest.cc
index ee18ab0..e819807 100644
--- a/components/invalidation/impl/fcm_invalidator_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidator_unittest.cc
@@ -47,7 +47,8 @@
     invalidator_.reset(new FCMInvalidator(
         std::move(network_channel), identity_provider_.get(), &pref_service_,
         &url_loader_factory_,
-        base::BindRepeating(&syncer::JsonUnsafeParser::Parse)));
+        base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
+        "fake_sender_id"));
   }
 
   Invalidator* GetInvalidator() { return invalidator_.get(); }
diff --git a/components/invalidation/impl/fcm_network_handler.cc b/components/invalidation/impl/fcm_network_handler.cc
index a321791..410742c 100644
--- a/components/invalidation/impl/fcm_network_handler.cc
+++ b/components/invalidation/impl/fcm_network_handler.cc
@@ -23,8 +23,6 @@
 
 namespace {
 
-const char kInvalidationsAppId[] = "com.google.chrome.fcm.invalidations";
-const char kInvalidationGCMSenderId[] = "8181035976";
 const char kPayloadKey[] = "payload";
 const char kPublicTopic[] = "external_name";
 const char kVersionKey[] = "version";
@@ -75,10 +73,14 @@
 
 FCMNetworkHandler::FCMNetworkHandler(
     gcm::GCMDriver* gcm_driver,
-    instance_id::InstanceIDDriver* instance_id_driver)
+    instance_id::InstanceIDDriver* instance_id_driver,
+    const std::string& sender_id,
+    const std::string& app_id)
     : gcm_driver_(gcm_driver),
       instance_id_driver_(instance_id_driver),
       token_validation_timer_(std::make_unique<base::OneShotTimer>()),
+      sender_id_(sender_id),
+      app_id_(app_id),
       weak_ptr_factory_(this) {}
 
 FCMNetworkHandler::~FCMNetworkHandler() {
@@ -88,23 +90,23 @@
 void FCMNetworkHandler::StartListening() {
   // Adding ourselves as Handler means start listening.
   // Being the listener is pre-requirement for token operations.
-  gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
+  gcm_driver_->AddAppHandler(app_id_, this);
 
-  instance_id_driver_->GetInstanceID(kInvalidationsAppId)
-      ->GetToken(kInvalidationGCMSenderId, kGCMScope,
-                 /*options=*/std::map<std::string, std::string>(),
-                 /*is_lazy=*/true,
-                 base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken,
-                                     weak_ptr_factory_.GetWeakPtr()));
+  instance_id_driver_->GetInstanceID(app_id_)->GetToken(
+      sender_id_, kGCMScope,
+      /*options=*/std::map<std::string, std::string>(),
+      /*is_lazy=*/true,
+      base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken,
+                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 void FCMNetworkHandler::StopListening() {
   if (IsListening())
-    gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
+    gcm_driver_->RemoveAppHandler(app_id_);
 }
 
 bool FCMNetworkHandler::IsListening() const {
-  return gcm_driver_->GetAppHandler(kInvalidationsAppId);
+  return gcm_driver_->GetAppHandler(app_id_);
 }
 
 void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token,
@@ -144,12 +146,11 @@
 void FCMNetworkHandler::StartTokenValidation() {
   DCHECK(IsListening());
 
-  instance_id_driver_->GetInstanceID(kInvalidationsAppId)
-      ->GetToken(kInvalidationGCMSenderId, kGCMScope,
-                 std::map<std::string, std::string>(),
-                 /*is_lazy=*/true,
-                 base::Bind(&FCMNetworkHandler::DidReceiveTokenForValidation,
-                            weak_ptr_factory_.GetWeakPtr()));
+  instance_id_driver_->GetInstanceID(app_id_)->GetToken(
+      sender_id_, kGCMScope, std::map<std::string, std::string>(),
+      /*is_lazy=*/true,
+      base::Bind(&FCMNetworkHandler::DidReceiveTokenForValidation,
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void FCMNetworkHandler::DidReceiveTokenForValidation(
@@ -185,7 +186,7 @@
 
 void FCMNetworkHandler::OnMessage(const std::string& app_id,
                                   const gcm::IncomingMessage& message) {
-  DCHECK_EQ(app_id, kInvalidationsAppId);
+  DCHECK_EQ(app_id, app_id_);
   std::string payload;
   std::string private_topic;
   std::string public_topic;
diff --git a/components/invalidation/impl/fcm_network_handler.h b/components/invalidation/impl/fcm_network_handler.h
index ff2149f2..4c0a300 100644
--- a/components/invalidation/impl/fcm_network_handler.h
+++ b/components/invalidation/impl/fcm_network_handler.h
@@ -36,7 +36,9 @@
                           public FCMSyncNetworkChannel {
  public:
   FCMNetworkHandler(gcm::GCMDriver* gcm_driver,
-                    instance_id::InstanceIDDriver* instance_id_driver);
+                    instance_id::InstanceIDDriver* instance_id_driver,
+                    const std::string& sender_id,
+                    const std::string& app_id);
 
   ~FCMNetworkHandler() override;
 
@@ -75,6 +77,10 @@
   std::string token_;
 
   std::unique_ptr<base::OneShotTimer> token_validation_timer_;
+
+  const std::string sender_id_;
+  const std::string app_id_;
+
   base::WeakPtrFactory<FCMNetworkHandler> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(FCMNetworkHandler);
diff --git a/components/invalidation/impl/fcm_network_handler_unittests.cc b/components/invalidation/impl/fcm_network_handler_unittests.cc
index 5edaafc3..a61bc7b 100644
--- a/components/invalidation/impl/fcm_network_handler_unittests.cc
+++ b/components/invalidation/impl/fcm_network_handler_unittests.cc
@@ -223,8 +223,9 @@
   }
 
   std::unique_ptr<FCMNetworkHandler> MakeHandler() {
-    return std::make_unique<FCMNetworkHandler>(mock_gcm_driver_.get(),
-                                               mock_instance_id_driver_.get());
+    return std::make_unique<FCMNetworkHandler>(
+        mock_gcm_driver_.get(), mock_instance_id_driver_.get(),
+        "fake_sender_id", kInvalidationsAppId);
   }
 
   std::unique_ptr<FCMNetworkHandler> MakeHandlerReadyForMessage(
diff --git a/components/invalidation/impl/per_user_topic_registration_manager.cc b/components/invalidation/impl/per_user_topic_registration_manager.cc
index e16650e2..249a88d 100644
--- a/components/invalidation/impl/per_user_topic_registration_manager.cc
+++ b/components/invalidation/impl/per_user_topic_registration_manager.cc
@@ -38,8 +38,6 @@
 const char kInvalidationRegistrationScope[] =
     "https://firebaseperusertopics-pa.googleapis.com";
 
-const char kProjectId[] = "8181035976";
-
 const char kFCMOAuthScope[] =
     "https://www.googleapis.com/auth/firebase.messaging";
 
@@ -134,12 +132,14 @@
     invalidation::IdentityProvider* identity_provider,
     PrefService* local_state,
     network::mojom::URLLoaderFactory* url_loader_factory,
-    const ParseJSONCallback& parse_json)
+    const ParseJSONCallback& parse_json,
+    const std::string& project_id)
     : local_state_(local_state),
       identity_provider_(identity_provider),
       request_access_token_backoff_(&kBackoffPolicy),
       parse_json_(parse_json),
-      url_loader_factory_(url_loader_factory) {}
+      url_loader_factory_(url_loader_factory),
+      project_id_(project_id) {}
 
 PerUserTopicRegistrationManager::~PerUserTopicRegistrationManager() {}
 
@@ -161,8 +161,6 @@
     keys_to_remove.push_back(topic);
   }
 
-  LOG(ERROR) << "Reg. size " << topic_to_private_topic_.size();
-
   // Delete prefs, which weren't decoded successfully.
   DictionaryPrefUpdate update(local_state_, kTypeRegisteredForInvalidation);
   base::DictionaryValue* pref_update = update.Get();
@@ -238,7 +236,7 @@
                             .SetPublicTopicName(topic)
                             .SetAuthenticationHeader(base::StringPrintf(
                                 "Bearer %s", access_token_.c_str()))
-                            .SetProjectId(kProjectId)
+                            .SetProjectId(project_id_)
                             .SetType(it->second->type)
                             .Build();
   it->second->request->Start(
@@ -254,7 +252,6 @@
     std::string private_topic_name,
     PerUserTopicRegistrationRequest::RequestType type) {
   if (code.IsSuccess()) {
-    LOG(ERROR) << topic;
     auto it = registration_statuses_.find(topic);
     registration_statuses_.erase(it);
     if (type == PerUserTopicRegistrationRequest::SUBSCRIBE) {
@@ -398,4 +395,15 @@
     observer.OnSubscriptionChannelStateChanged(invalidator_state);
 }
 
+std::unique_ptr<base::DictionaryValue>
+PerUserTopicRegistrationManager::CollectDebugData() const {
+  std::unique_ptr<base::DictionaryValue> return_value(
+      new base::DictionaryValue());
+  for (const auto& topic_to_private_topic : topic_to_private_topic_) {
+    return_value->SetString(topic_to_private_topic.first,
+                            topic_to_private_topic.second);
+  }
+  return return_value;
+}
+
 }  // namespace syncer
diff --git a/components/invalidation/impl/per_user_topic_registration_manager.h b/components/invalidation/impl/per_user_topic_registration_manager.h
index fea14b2..95cd00c 100644
--- a/components/invalidation/impl/per_user_topic_registration_manager.h
+++ b/components/invalidation/impl/per_user_topic_registration_manager.h
@@ -53,7 +53,8 @@
       invalidation::IdentityProvider* identity_provider,
       PrefService* local_state,
       network::mojom::URLLoaderFactory* url_loader_factory,
-      const ParseJSONCallback& parse_json);
+      const ParseJSONCallback& parse_json,
+      const std::string& project_id);
 
   virtual ~PerUserTopicRegistrationManager();
 
@@ -70,6 +71,8 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  std::unique_ptr<base::DictionaryValue> CollectDebugData() const;
+
  private:
   struct RegistrationEntry;
 
@@ -116,6 +119,8 @@
   ParseJSONCallback parse_json_;
   network::mojom::URLLoaderFactory* url_loader_factory_;
 
+  const std::string project_id_;
+
   base::ObserverList<Observer>::Unchecked observers_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc b/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
index b353bb4..e183e95 100644
--- a/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
+++ b/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
@@ -125,7 +125,7 @@
   std::unique_ptr<PerUserTopicRegistrationManager> BuildRegistrationManager() {
     auto reg_manager = std::make_unique<PerUserTopicRegistrationManager>(
         identity_provider_.get(), &pref_service_, url_loader_factory(),
-        base::BindRepeating(&syncer::JsonUnsafeParser::Parse));
+        base::BindRepeating(&syncer::JsonUnsafeParser::Parse), kProjectId);
     reg_manager->Init();
     reg_manager->AddObserver(&state_observer_);
     return reg_manager;
diff --git a/components/leveldb_proto/BUILD.gn b/components/leveldb_proto/BUILD.gn
index a743b4d..58f080c4 100644
--- a/components/leveldb_proto/BUILD.gn
+++ b/components/leveldb_proto/BUILD.gn
@@ -6,35 +6,34 @@
 
 proto_library("proto") {
   sources = [
-    "proto/shared_db_metadata.proto",
+    "internal/proto/shared_db_metadata.proto",
   ]
 }
 
 static_library("leveldb_proto") {
   sources = [
-    "leveldb_database.cc",
-    "leveldb_database.h",
-    "migration_delegate.h",
-    "proto_database.cc",
-    "proto_database.h",
-    "proto_database_impl.h",
-    "proto_database_provider.cc",
-    "proto_database_provider.h",
-    "proto_database_wrapper.cc",
-    "proto_database_wrapper.h",
-    "proto_leveldb_wrapper.cc",
-    "proto_leveldb_wrapper.h",
-    "proto_leveldb_wrapper_metrics.cc",
-    "proto_leveldb_wrapper_metrics.h",
-    "shared_proto_database.cc",
-    "shared_proto_database.h",
-    "shared_proto_database_client.cc",
-    "shared_proto_database_client.h",
-    "shared_proto_database_client_list.cc",
-    "shared_proto_database_client_list.h",
-    "shared_proto_database_provider.cc",
-    "shared_proto_database_provider.h",
-    "unique_proto_database.h",
+    "internal/leveldb_database.cc",
+    "internal/leveldb_database.h",
+    "internal/migration_delegate.h",
+    "internal/proto_database_wrapper.cc",
+    "internal/proto_database_wrapper.h",
+    "internal/proto_leveldb_wrapper.cc",
+    "internal/proto_leveldb_wrapper.h",
+    "internal/proto_leveldb_wrapper_metrics.cc",
+    "internal/proto_leveldb_wrapper_metrics.h",
+    "internal/shared_proto_database.cc",
+    "internal/shared_proto_database.h",
+    "internal/shared_proto_database_client.cc",
+    "internal/shared_proto_database_client.h",
+    "internal/shared_proto_database_provider.cc",
+    "internal/shared_proto_database_provider.h",
+    "internal/unique_proto_database.h",
+    "public/proto_database.cc",
+    "public/proto_database.h",
+    "public/proto_database_provider.cc",
+    "public/proto_database_provider.h",
+    "public/shared_proto_database_client_list.cc",
+    "public/shared_proto_database_client_list.h",
   ]
   deps = [
     ":proto",
@@ -64,10 +63,10 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "proto_database_wrapper_unittest.cc",
-    "shared_proto_database_client_unittest.cc",
-    "shared_proto_database_unittest.cc",
-    "unique_proto_database_unittest.cc",
+    "internal/proto_database_wrapper_unittest.cc",
+    "internal/shared_proto_database_client_unittest.cc",
+    "internal/shared_proto_database_unittest.cc",
+    "internal/unique_proto_database_unittest.cc",
   ]
   deps = [
     ":test_support",
diff --git a/components/leveldb_proto/content/proto_database_provider_factory.cc b/components/leveldb_proto/content/proto_database_provider_factory.cc
index 98ab84b8..dfdf057d 100644
--- a/components/leveldb_proto/content/proto_database_provider_factory.cc
+++ b/components/leveldb_proto/content/proto_database_provider_factory.cc
@@ -7,7 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/singleton.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "content/public/browser/browser_context.h"
 
 namespace leveldb_proto {
diff --git a/components/leveldb_proto/leveldb_database.cc b/components/leveldb_proto/internal/leveldb_database.cc
similarity index 99%
rename from components/leveldb_proto/leveldb_database.cc
rename to components/leveldb_proto/internal/leveldb_database.cc
index b5ab97f..8736bde 100644
--- a/components/leveldb_proto/leveldb_database.cc
+++ b/components/leveldb_proto/internal/leveldb_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/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
 
 #include <map>
 #include <string>
diff --git a/components/leveldb_proto/leveldb_database.h b/components/leveldb_proto/internal/leveldb_database.h
similarity index 94%
rename from components/leveldb_proto/leveldb_database.h
rename to components/leveldb_proto/internal/leveldb_database.h
index 7d9deeec..d684035 100644
--- a/components/leveldb_proto/leveldb_database.h
+++ b/components/leveldb_proto/internal/leveldb_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_LEVELDB_PROTO_LEVELDB_DATABASE_H_
-#define COMPONENTS_LEVELDB_PROTO_LEVELDB_DATABASE_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_LEVELDB_DATABASE_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_LEVELDB_DATABASE_H_
 
 #include <map>
 #include <memory>
@@ -19,7 +19,6 @@
 }  // namespace base
 
 namespace leveldb {
-class Cache;
 class DB;
 class Env;
 }  // namespace leveldb
@@ -105,8 +104,6 @@
   bool GetApproximateMemoryUse(uint64_t* approx_mem);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ProtoDatabaseImplLevelDBTest, TestDBInitFail);
-
   DFAKE_MUTEX(thread_checker_);
 
   // The declaration order of these members matters: |db_| depends on |env_| and
@@ -124,4 +121,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_LEVELDB_DATABASE_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_LEVELDB_DATABASE_H_
diff --git a/components/leveldb_proto/migration_delegate.h b/components/leveldb_proto/internal/migration_delegate.h
similarity index 92%
rename from components/leveldb_proto/migration_delegate.h
rename to components/leveldb_proto/internal/migration_delegate.h
index ff134e7..abbb74e 100644
--- a/components/leveldb_proto/migration_delegate.h
+++ b/components/leveldb_proto/internal/migration_delegate.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_MIGRATION_DELEGATE_H_
-#define COMPONENTS_LEVELDB_PROTO_MIGRATION_DELEGATE_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_MIGRATION_DELEGATE_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_MIGRATION_DELEGATE_H_
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 
@@ -92,4 +92,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_MIGRATION_DELEGATE_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_MIGRATION_DELEGATE_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/internal/proto/shared_db_metadata.proto b/components/leveldb_proto/internal/proto/shared_db_metadata.proto
new file mode 100644
index 0000000..28d9950
--- /dev/null
+++ b/components/leveldb_proto/internal/proto/shared_db_metadata.proto
@@ -0,0 +1,50 @@
+// Copyright 2018 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package leveldb_proto;
+
+// A proto to for storing metadata related to a SharedProtoDatabase client.
+//
+// Next tag: 3
+message SharedDBMetadataProto {
+  // Add obsolete fields here.
+  reserved 2;
+
+  // For the global database, this is value increases every time a corruption
+  // is discovered, as a way for clients to determine whether there's been a
+  // corruption since the last time they checked.
+  // For clients, this number is set to the global value when they check their
+  // corruption status, to indicate that we don't need to tell them there was
+  // a corruption next time they check.
+  optional uint64 corruptions = 1;
+
+  // Stores the status of migration in the last session for the database.
+  enum MigrationStatus {
+    // No migration was attempted in the last session. Either unique or shared
+    // did not exist because migration was already done, or either of them
+    // failed to load.
+    MIGRATION_NOT_ATTEMPTED = 0;
+
+    // Last session used shared db and migration was successful.
+    MIGRATE_TO_SHARED_SUCCESSFUL = 1;
+
+    // Last session used shared db and migration transfer was complete, but
+    // unique db was not deleted.
+    MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED = 2;
+
+    // Last session used unique db and migration from shared to unique was
+    // successful.
+    MIGRATE_TO_UNIQUE_SUCCESSFUL = 3;
+
+    // last session used unique db and migration transfer from shared to unique
+    // was successful, but the shared db was not deleted.
+    MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED = 4;
+  };
+
+  optional MigrationStatus migration_status = 3;
+}
diff --git a/components/leveldb_proto/proto_database_perftest.cc b/components/leveldb_proto/internal/proto_database_perftest.cc
similarity index 99%
rename from components/leveldb_proto/proto_database_perftest.cc
rename to components/leveldb_proto/internal/proto_database_perftest.cc
index a1614b8..1d5721c 100644
--- a/components/leveldb_proto/proto_database_perftest.cc
+++ b/components/leveldb_proto/internal/proto_database_perftest.cc
@@ -23,9 +23,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
-#include "components/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/unique_proto_database.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
-#include "components/leveldb_proto/unique_proto_database.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
@@ -35,14 +35,14 @@
 using base::MessageLoop;
 using base::ScopedTempDir;
 using leveldb_env::Options;
+using testing::_;
 using testing::Invoke;
 using testing::MakeMatcher;
-using testing::MatchResultListener;
 using testing::Matcher;
 using testing::MatcherInterface;
+using testing::MatchResultListener;
 using testing::Return;
 using testing::UnorderedElementsAre;
-using testing::_;
 
 namespace leveldb_proto {
 
diff --git a/components/leveldb_proto/proto_database_wrapper.cc b/components/leveldb_proto/internal/proto_database_wrapper.cc
similarity index 79%
rename from components/leveldb_proto/proto_database_wrapper.cc
rename to components/leveldb_proto/internal/proto_database_wrapper.cc
index eaac505..abb7b0a6 100644
--- a/components/leveldb_proto/proto_database_wrapper.cc
+++ b/components/leveldb_proto/internal/proto_database_wrapper.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/proto_database_wrapper.h"
+#include "components/leveldb_proto/internal/proto_database_wrapper.h"
 
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace leveldb_proto {
 
diff --git a/components/leveldb_proto/proto_database_wrapper.h b/components/leveldb_proto/internal/proto_database_wrapper.h
similarity index 70%
rename from components/leveldb_proto/proto_database_wrapper.h
rename to components/leveldb_proto/internal/proto_database_wrapper.h
index 523eace..2f018a6a 100644
--- a/components/leveldb_proto/proto_database_wrapper.h
+++ b/components/leveldb_proto/internal/proto_database_wrapper.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_WRAPPER_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_WRAPPER_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_DATABASE_WRAPPER_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_DATABASE_WRAPPER_H_
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/leveldb_proto/migration_delegate.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
-#include "components/leveldb_proto/shared_proto_database.h"
-#include "components/leveldb_proto/shared_proto_database_client_list.h"
-#include "components/leveldb_proto/shared_proto_database_provider.h"
-#include "components/leveldb_proto/unique_proto_database.h"
+#include "components/leveldb_proto/internal/migration_delegate.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
+#include "components/leveldb_proto/internal/shared_proto_database_provider.h"
+#include "components/leveldb_proto/internal/unique_proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 
 namespace leveldb_proto {
 
@@ -150,16 +150,29 @@
       bool use_shared_db,
       Callbacks::InitStatusCallback callback,
       std::unique_ptr<SharedProtoDatabaseClient<T>> client);
-  void OnMigrationTransferComplete(std::unique_ptr<ProtoDatabase<T>> unique_db,
-                                   std::unique_ptr<ProtoDatabase<T>> client,
-                                   bool use_shared_db,
-                                   Callbacks::InitStatusCallback callback,
-                                   bool success);
-  void OnMigrationCleanupComplete(std::unique_ptr<ProtoDatabase<T>> unique_db,
-                                  std::unique_ptr<ProtoDatabase<T>> client,
-                                  bool use_shared_db,
-                                  Callbacks::InitStatusCallback callback,
-                                  bool success);
+  void DeleteOldDataAndMigrate(
+      std::unique_ptr<ProtoDatabase<T>> unique_db,
+      std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+      bool use_shared_db,
+      Callbacks::InitStatusCallback callback);
+  void MaybeDoMigrationOnDeletingOld(
+      std::unique_ptr<ProtoDatabase<T>> unique_db,
+      std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+      Callbacks::InitStatusCallback init_callback,
+      bool use_shared_db,
+      bool delete_success);
+  void OnMigrationTransferComplete(
+      std::unique_ptr<ProtoDatabase<T>> unique_db,
+      std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+      bool use_shared_db,
+      Callbacks::InitStatusCallback callback,
+      bool success);
+  void OnMigrationCleanupComplete(
+      std::unique_ptr<ProtoDatabase<T>> unique_db,
+      std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+      bool use_shared_db,
+      Callbacks::InitStatusCallback callback,
+      bool success);
 
   std::string client_namespace_;
   std::string type_prefix_;
@@ -325,43 +338,137 @@
     return;
   }
 
-  if (unique_db && client) {
-    // We got access to both the unique DB and a shared DB, meaning we need to
-    // attempt migration and give back the right one.
-    ProtoDatabase<T>* from = use_shared_db ? unique_db.get() : client.get();
-    ProtoDatabase<T>* to = use_shared_db ? client.get() : unique_db.get();
-    migration_delegate_->DoMigration(
-        from, to,
-        base::BindOnce(&ProtoDatabaseWrapper<T>::OnMigrationTransferComplete,
-                       weak_ptr_factory_->GetWeakPtr(), std::move(unique_db),
-                       std::move(client), use_shared_db, std::move(callback)));
+  if (!unique_db || !client) {
+    // One of two things happened:
+    // 1) We failed to get a shared DB instance, so regardless of whether we
+    // want to use the shared DB or not, we'll settle for the unique DB.
+    // 2) We failed to initialize a unique DB, but got  access to the shared DB,
+    // so we use that regardless of whether we "should be" or not.
+    db_ = client ? std::move(client) : std::move(unique_db);
+    RunCallbackOnCallingSequence(
+        base::BindOnce(std::move(callback), Enums::InitStatus::kOK));
     return;
   }
 
-  // One of two things happened:
-  // 1) We failed to get a shared DB instance, so regardless of whether we
-  // want to use the shared DB or not, we'll settle for the unique DB.
-  // 2) We failed to initialize a unique DB, but got  access to the shared DB,
-  // so we use that regardless of whether we "should be" or not.
-  db_ = client ? std::move(client) : std::move(unique_db);
-  RunCallbackOnCallingSequence(
-      base::BindOnce(std::move(callback), Enums::InitStatus::kOK));
+  ProtoDatabase<T>* from = nullptr;
+  ProtoDatabase<T>* to = nullptr;
+  if (use_shared_db) {
+    switch (client->migration_status()) {
+      case SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED:
+      case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL:
+        from = unique_db.get();
+        to = client.get();
+        break;
+      case SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL:
+        // Unique db was deleted in previous migration, so nothing to do here.
+        return OnMigrationCleanupComplete(std::move(unique_db),
+                                          std::move(client), use_shared_db,
+                                          std::move(callback), true);
+      case SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED:
+        // Migration transfer was completed, so just try deleting the unique db.
+        return OnMigrationTransferComplete(std::move(unique_db),
+                                           std::move(client), use_shared_db,
+                                           std::move(callback), true);
+      case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED:
+        // Shared db was not deleted in last migration and we want to use shared
+        // db. So, delete stale data, and attempt migration.
+        return DeleteOldDataAndMigrate(std::move(unique_db), std::move(client),
+                                       use_shared_db, std::move(callback));
+    };
+  } else {
+    switch (client->migration_status()) {
+      case SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED:
+      case SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL:
+        from = client.get();
+        to = unique_db.get();
+        break;
+      case SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED:
+        // Unique db was not deleted in last migration and we want to use unique
+        // db. So, delete stale data, and attempt migration.
+        return DeleteOldDataAndMigrate(std::move(unique_db), std::move(client),
+                                       use_shared_db, std::move(callback));
+      case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL:
+        // Shared db was deleted in previous migration, so nothing to do here.
+        return OnMigrationCleanupComplete(std::move(unique_db),
+                                          std::move(client), use_shared_db,
+                                          std::move(callback), true);
+      case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED:
+        // Migration transfer was completed, so just try deleting the shared db.
+        return OnMigrationTransferComplete(std::move(unique_db),
+                                           std::move(client), use_shared_db,
+                                           std::move(callback), true);
+    };
+  }
+
+  // We got access to both the unique DB and a shared DB, meaning we need to
+  // attempt migration and give back the right one.
+  migration_delegate_->DoMigration(
+      from, to,
+      base::BindOnce(&ProtoDatabaseWrapper<T>::OnMigrationTransferComplete,
+                     weak_ptr_factory_->GetWeakPtr(), std::move(unique_db),
+                     std::move(client), use_shared_db, std::move(callback)));
+}
+
+template <typename T>
+void ProtoDatabaseWrapper<T>::DeleteOldDataAndMigrate(
+    std::unique_ptr<ProtoDatabase<T>> unique_db,
+    std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+    bool use_shared_db,
+    Callbacks::InitStatusCallback callback) {
+  ProtoDatabase<T>* to_remove_old_data =
+      use_shared_db ? client.get() : unique_db.get();
+  auto maybe_do_migration =
+      base::BindOnce(&ProtoDatabaseWrapper<T>::MaybeDoMigrationOnDeletingOld,
+                     weak_ptr_factory_->GetWeakPtr(), std::move(unique_db),
+                     std::move(client), std::move(callback), use_shared_db);
+
+  to_remove_old_data->UpdateEntriesWithRemoveFilter(
+      std::make_unique<typename Util::Internal<T>::KeyEntryVector>(),
+      base::BindRepeating([](const std::string& key) { return true; }),
+      std::move(maybe_do_migration));
+}
+
+template <typename T>
+void ProtoDatabaseWrapper<T>::MaybeDoMigrationOnDeletingOld(
+    std::unique_ptr<ProtoDatabase<T>> unique_db,
+    std::unique_ptr<SharedProtoDatabaseClient<T>> client,
+    Callbacks::InitStatusCallback callback,
+    bool use_shared_db,
+    bool delete_success) {
+  if (!delete_success) {
+    // Old data has not been removed from the database we want to use. We also
+    // know that previous attempt of migration failed for same reason. Give up
+    // on this database and use the other.
+    // This update is not necessary since this was the old value. But update to
+    // be clear.
+    client->UpdateClientInitMetadata(
+        use_shared_db
+            ? SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED
+            : SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
+    db_ = use_shared_db ? std::move(unique_db) : std::move(client);
+    RunCallbackOnCallingSequence(
+        base::BindOnce(std::move(callback), Enums::InitStatus::kOK));
+    return;
+  }
+
+  auto from = use_shared_db ? unique_db.get() : client.get();
+  auto to = use_shared_db ? client.get() : unique_db.get();
+  migration_delegate_->DoMigration(
+      from, to,
+      base::BindOnce(&ProtoDatabaseWrapper<T>::OnMigrationTransferComplete,
+                     weak_ptr_factory_->GetWeakPtr(), std::move(unique_db),
+                     std::move(client), use_shared_db, std::move(callback)));
 }
 
 template <typename T>
 void ProtoDatabaseWrapper<T>::OnMigrationTransferComplete(
     std::unique_ptr<ProtoDatabase<T>> unique_db,
-    std::unique_ptr<ProtoDatabase<T>> client,
+    std::unique_ptr<SharedProtoDatabaseClient<T>> client,
     bool use_shared_db,
     Callbacks::InitStatusCallback callback,
     bool success) {
   if (success) {
     // Call Destroy on the DB we no longer want to use.
-    // TODO(thildebr): If this destroy fails, we should flag this as undestroyed
-    // so that we don't erroneously transfer data from the undestroyed database
-    // on next start. This might be possible by comparing the modified timestamp
-    // of the unique database directory with metadata in the shared database
-    // about last modified for each client.
     auto* db_destroy_ptr = use_shared_db ? unique_db.get() : client.get();
     db_destroy_ptr->Destroy(
         base::BindOnce(&ProtoDatabaseWrapper<T>::OnMigrationCleanupComplete,
@@ -370,6 +477,13 @@
     return;
   }
 
+  // Failing to transfer the old data means that the requested database to be
+  // used could have some bad data. So, mark them to be deleted before use in
+  // the next runs.
+  client->UpdateClientInitMetadata(
+      use_shared_db
+          ? SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED
+          : SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
   db_ = use_shared_db ? std::move(unique_db) : std::move(client);
   RunCallbackOnCallingSequence(
       base::BindOnce(std::move(callback), Enums::InitStatus::kOK));
@@ -378,7 +492,7 @@
 template <typename T>
 void ProtoDatabaseWrapper<T>::OnMigrationCleanupComplete(
     std::unique_ptr<ProtoDatabase<T>> unique_db,
-    std::unique_ptr<ProtoDatabase<T>> client,
+    std::unique_ptr<SharedProtoDatabaseClient<T>> client,
     bool use_shared_db,
     Callbacks::InitStatusCallback callback,
     bool success) {
@@ -386,10 +500,17 @@
   // far as the original caller is concerned. As long as |db_| is assigned, we
   // return true.
   if (success) {
-    db_ = use_shared_db ? std::move(client) : std::move(unique_db);
+    client->UpdateClientInitMetadata(
+        use_shared_db ? SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL
+                      : SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL);
   } else {
-    db_ = use_shared_db ? std::move(unique_db) : std::move(client);
+    client->UpdateClientInitMetadata(
+        use_shared_db
+            ? SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED
+            : SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
   }
+  // Migration transfer was complete. So, we should use the requested database.
+  db_ = use_shared_db ? std::move(client) : std::move(unique_db);
   RunCallbackOnCallingSequence(
       base::BindOnce(std::move(callback), Enums::InitStatus::kOK));
 }
@@ -603,4 +724,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_WRAPPER_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_DATABASE_WRAPPER_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/internal/proto_database_wrapper_unittest.cc b/components/leveldb_proto/internal/proto_database_wrapper_unittest.cc
new file mode 100644
index 0000000..43e61c4
--- /dev/null
+++ b/components/leveldb_proto/internal/proto_database_wrapper_unittest.cc
@@ -0,0 +1,562 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/leveldb_proto/internal/proto_database_wrapper.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "components/leveldb_proto/internal/shared_proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/leveldb_proto/testing/proto/test_db.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace leveldb_proto {
+
+namespace {
+
+const std::string kDefaultNamespace = "namespace";
+const std::string kDefaultTypePrefix = "prefix";
+const std::string kDefaultClientName = "client";
+
+}  // namespace
+
+class TestProtoDatabaseProvider : public ProtoDatabaseProvider {
+ public:
+  TestProtoDatabaseProvider(const base::FilePath& profile_dir)
+      : ProtoDatabaseProvider(profile_dir) {}
+  TestProtoDatabaseProvider(const base::FilePath& profile_dir,
+                            const scoped_refptr<SharedProtoDatabase>& shared_db)
+      : ProtoDatabaseProvider(profile_dir) {
+    shared_db_ = shared_db;
+  }
+
+  void GetSharedDBInstance(
+      ProtoDatabaseProvider::GetSharedDBInstanceCallback callback,
+      scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {
+    callback_task_runner->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), shared_db_));
+  }
+
+ private:
+  scoped_refptr<SharedProtoDatabase> shared_db_;
+};
+
+class TestSharedProtoDatabaseProvider : public SharedProtoDatabaseProvider {
+ public:
+  TestSharedProtoDatabaseProvider(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+      base::WeakPtr<ProtoDatabaseProvider> provider_weak_ptr)
+      : SharedProtoDatabaseProvider(std::move(task_runner),
+                                    std::move(provider_weak_ptr)) {}
+};
+
+class ProtoDatabaseWrapperTest : public testing::Test {
+ public:
+  void SetUp() override {
+    temp_dir_ = std::make_unique<base::ScopedTempDir>();
+    ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
+    shared_db_temp_dir_ = std::make_unique<base::ScopedTempDir>();
+    ASSERT_TRUE(shared_db_temp_dir_->CreateUniqueTempDir());
+    test_thread_ = std::make_unique<base::Thread>("test_thread");
+    ASSERT_TRUE(test_thread_->Start());
+    shared_db_ = base::WrapRefCounted(new SharedProtoDatabase(
+        kDefaultClientName, shared_db_temp_dir_->GetPath()));
+  }
+
+  void TearDown() override {
+    temp_dir_.reset();
+    shared_db_temp_dir_.reset();
+  }
+
+  std::unique_ptr<ProtoDatabaseWrapper<TestProto>> CreateWrapper(
+      const std::string& client_namespace,
+      const std::string& type_prefix,
+      const base::FilePath& db_dir,
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+      std::unique_ptr<SharedProtoDatabaseProvider> db_provider) {
+    return std::make_unique<ProtoDatabaseWrapper<TestProto>>(
+        client_namespace, type_prefix, db_dir, task_runner,
+        std::move(db_provider));
+  }
+
+  std::unique_ptr<TestProtoDatabaseProvider> CreateProviderNoSharedDB() {
+    return std::make_unique<TestProtoDatabaseProvider>(
+        shared_db_temp_dir_->GetPath());
+  }
+
+  std::unique_ptr<TestProtoDatabaseProvider> CreateProviderWithSharedDB() {
+    return std::make_unique<TestProtoDatabaseProvider>(
+        shared_db_temp_dir_->GetPath(), shared_db_);
+  }
+
+  std::unique_ptr<TestSharedProtoDatabaseProvider> CreateSharedProvider(
+      TestProtoDatabaseProvider* db_provider) {
+    return std::make_unique<TestSharedProtoDatabaseProvider>(
+        GetTestThreadTaskRunner(), db_provider->weak_factory_.GetWeakPtr());
+  }
+
+  // Uses ProtoDatabaseWrapper's 3 parameter Init to bypass the check that gets
+  // |use_shared_db|'s value.
+  void InitWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
+                   const std::string& client_name,
+                   bool use_shared_db,
+                   Callbacks::InitStatusCallback callback) {
+    wrapper->Init(client_name, use_shared_db, std::move(callback));
+  }
+
+  void InitWrapperAndWait(ProtoDatabaseWrapper<TestProto>* wrapper,
+                          const std::string& client_name,
+                          bool use_shared_db,
+                          Enums::InitStatus expect_status) {
+    base::RunLoop init_loop;
+    InitWrapper(
+        wrapper, client_name, use_shared_db,
+        base::BindOnce(
+            [](base::OnceClosure closure, Enums::InitStatus expect_status,
+               Enums::InitStatus status) {
+              ASSERT_EQ(status, expect_status);
+              std::move(closure).Run();
+            },
+            init_loop.QuitClosure(), expect_status));
+    init_loop.Run();
+  }
+
+  // Just uses each entry's key to fill out the id/data fields in TestProto as
+  // well.
+  void AddDataToWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
+                        std::vector<std::string>* entry_keys) {
+    auto data_set =
+        std::make_unique<std::vector<std::pair<std::string, TestProto>>>();
+    for (const auto& key : *entry_keys) {
+      TestProto proto;
+      proto.set_id(key);
+      proto.set_data(key);
+      data_set->emplace_back(std::make_pair(key, proto));
+    }
+
+    base::RunLoop data_loop;
+    wrapper->UpdateEntries(std::move(data_set),
+                           std::make_unique<std::vector<std::string>>(),
+                           base::BindOnce(
+                               [](base::OnceClosure closure, bool success) {
+                                 ASSERT_TRUE(success);
+                                 std::move(closure).Run();
+                               },
+                               data_loop.QuitClosure()));
+    data_loop.Run();
+  }
+
+  void VerifyDataInWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
+                           std::vector<std::string>* entry_keys) {
+    base::RunLoop load_loop;
+    wrapper->LoadKeysAndEntries(base::BindOnce(
+        [](base::OnceClosure closure, std::vector<std::string>* entry_keys,
+           bool success,
+           std::unique_ptr<std::map<std::string, TestProto>> keys_entries) {
+          ASSERT_TRUE(success);
+          ASSERT_EQ(entry_keys->size(), keys_entries->size());
+
+          for (const auto& key : *entry_keys) {
+            ASSERT_TRUE(keys_entries->find(key) != keys_entries->end());
+          }
+          std::move(closure).Run();
+        },
+        load_loop.QuitClosure(), entry_keys));
+    load_loop.Run();
+  }
+
+  void UpdateClientMetadata(
+      SharedDBMetadataProto::MigrationStatus migration_status) {
+    base::RunLoop init_wait;
+    auto client = shared_db_->GetClientForTesting<TestProto>(
+        kDefaultNamespace, kDefaultTypePrefix, /*create_if_missing=*/true,
+        base::BindOnce(
+            [](base::OnceClosure closure, Enums::InitStatus status,
+               SharedDBMetadataProto::MigrationStatus migration_status) {
+              EXPECT_EQ(Enums::kOK, status);
+              EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                        migration_status);
+              std::move(closure).Run();
+            },
+            init_wait.QuitClosure()));
+    init_wait.Run();
+
+    base::RunLoop wait_loop;
+    shared_db_->UpdateClientMetadataAsync(
+        client->client_db_id(), migration_status,
+        base::BindOnce(
+            [](base::OnceClosure closure, bool success) {
+              EXPECT_TRUE(success);
+              std::move(closure).Run();
+            },
+            wait_loop.QuitClosure()));
+    wait_loop.Run();
+  }
+
+  SharedDBMetadataProto::MigrationStatus GetClientMigrationStatus() {
+    SharedDBMetadataProto::MigrationStatus migration_status;
+    base::RunLoop init_wait;
+    auto client = shared_db_->GetClientForTesting<TestProto>(
+        kDefaultNamespace, kDefaultTypePrefix, /*create_if_missing=*/true,
+        base::BindOnce(
+            [](base::OnceClosure closure,
+               SharedDBMetadataProto::MigrationStatus* output,
+               Enums::InitStatus status,
+               SharedDBMetadataProto::MigrationStatus migration_status) {
+              EXPECT_EQ(Enums::kOK, status);
+              *output = migration_status;
+              std::move(closure).Run();
+            },
+            init_wait.QuitClosure(), &migration_status));
+    init_wait.Run();
+
+    return migration_status;
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> GetTestThreadTaskRunner() {
+    return test_thread_->task_runner();
+  }
+
+  base::FilePath temp_dir() { return temp_dir_->GetPath(); }
+
+ private:
+  std::unique_ptr<base::ScopedTempDir> temp_dir_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  // Shared database.
+  std::unique_ptr<base::Thread> test_thread_;
+  std::unique_ptr<base::Thread> shared_db_thread_;
+  scoped_refptr<SharedProtoDatabase> shared_db_;
+  std::unique_ptr<base::ScopedTempDir> shared_db_temp_dir_;
+};
+
+TEST_F(ProtoDatabaseWrapperTest, FailsBothDatabases) {
+  auto db_provider = CreateProviderNoSharedDB();
+  auto shared_db_provider = CreateSharedProvider(db_provider.get());
+  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                               temp_dir(), GetTestThreadTaskRunner(),
+                               CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kError);
+}
+
+TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_DontUseShared_NoSharedDB) {
+  auto db_provider = CreateProviderNoSharedDB();
+  auto shared_db_provider = CreateSharedProvider(db_provider.get());
+  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                               temp_dir(), GetTestThreadTaskRunner(),
+                               CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Fails_UseShared_NoSharedDB_NoUniqueDB) {
+  auto db_provider = CreateProviderNoSharedDB();
+  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                               temp_dir(), GetTestThreadTaskRunner(),
+                               CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kError);
+}
+
+TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_UseShared_NoSharedDB) {
+  // First we create a unique DB so our second pass has a unique DB available.
+  auto db_provider = CreateProviderNoSharedDB();
+  auto unique_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                                      temp_dir(), GetTestThreadTaskRunner(),
+                                      CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  // Kill the wrapper so it doesn't have a lock on the DB anymore.
+  unique_wrapper.reset();
+
+  auto shared_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                                      temp_dir(), GetTestThreadTaskRunner(),
+                                      CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+}
+
+TEST_F(ProtoDatabaseWrapperTest, SucceedsWithShared_UseShared_HasSharedDB) {
+  auto db_provider = CreateProviderWithSharedDB();
+  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                               temp_dir(), GetTestThreadTaskRunner(),
+                               CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+}
+
+TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_DontUseShared_HasSharedDB) {
+  auto db_provider = CreateProviderWithSharedDB();
+  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                               temp_dir(), GetTestThreadTaskRunner(),
+                               CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+}
+
+// Migration tests:
+TEST_F(ProtoDatabaseWrapperTest, Migration_EmptyDBs_UniqueToShared) {
+  // First we create a unique DB so our second pass has a unique DB available.
+  auto db_provider_noshared = CreateProviderNoSharedDB();
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_noshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  // Kill the wrapper so it doesn't have a lock on the DB anymore.
+  unique_wrapper.reset();
+
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_EmptyDBs_SharedToUnique) {
+  // First we create a unique DB so our second pass has a unique DB available.
+  auto db_provider = CreateProviderWithSharedDB();
+  auto shared_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                                      temp_dir(), GetTestThreadTaskRunner(),
+                                      CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            GetClientMigrationStatus());
+
+  auto unique_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
+                                      temp_dir(), GetTestThreadTaskRunner(),
+                                      CreateSharedProvider(db_provider.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_UniqueToShared) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a unique DB so our second pass has a unique DB available.
+  auto db_provider_noshared = CreateProviderNoSharedDB();
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_noshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(unique_wrapper.get(), data_set.get());
+  // Kill the wrapper so it doesn't have a lock on the DB anymore.
+  unique_wrapper.reset();
+
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+  VerifyDataInWrapper(shared_wrapper.get(), data_set.get());
+
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_SharedToUnique) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a shared DB so our second pass has a shared DB available.
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(shared_wrapper.get(), data_set.get());
+
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            GetClientMigrationStatus());
+
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  VerifyDataInWrapper(unique_wrapper.get(), data_set.get());
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_UniqueToShared_UniqueObsolete) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a unique DB so our second pass has a unique DB available.
+  auto db_provider_noshared = CreateProviderNoSharedDB();
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_noshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(unique_wrapper.get(), data_set.get());
+  // Kill the wrapper so it doesn't have a lock on the DB anymore.
+  unique_wrapper.reset();
+
+  UpdateClientMetadata(
+      SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
+
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+
+  // Unique db should be deleted in migration. So, shared db should be clean.
+  data_set->clear();
+  VerifyDataInWrapper(shared_wrapper.get(), data_set.get());
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_UniqueToShared_SharedObsolete) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a shared DB so our second pass has a shared DB available.
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(shared_wrapper.get(), data_set.get());
+
+  // Force create an uniquedb, which was deleted by migration.
+  auto db_provider_noshared = CreateProviderNoSharedDB();
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_noshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  unique_wrapper.reset();
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            GetClientMigrationStatus());
+
+  UpdateClientMetadata(
+      SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
+
+  shared_wrapper.reset();
+  db_provider_withshared = CreateProviderWithSharedDB();
+
+  auto shared_wrapper1 =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper1.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+
+  // Shared db should be deleted in migration. So, shared db should be clean.
+  data_set->clear();
+  VerifyDataInWrapper(shared_wrapper1.get(), data_set.get());
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_SharedToUnique_SharedObsolete) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a shared DB so our second pass has a shared DB available.
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(shared_wrapper.get(), data_set.get());
+
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            GetClientMigrationStatus());
+
+  UpdateClientMetadata(
+      SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
+
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+
+  // Shared db should be deleted in migration. So, unique db should be clean.
+  data_set->clear();
+  VerifyDataInWrapper(unique_wrapper.get(), data_set.get());
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+TEST_F(ProtoDatabaseWrapperTest, Migration_SharedToUnique_UniqueObsolete) {
+  auto data_set = std::make_unique<std::vector<std::string>>();
+  data_set->emplace_back("entry1");
+  data_set->emplace_back("entry2");
+  data_set->emplace_back("entry3");
+
+  // First we create a shared DB so our second pass has a shared DB available.
+  auto db_provider_noshared = CreateProviderNoSharedDB();
+  auto unique_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_noshared.get()));
+  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+  AddDataToWrapper(unique_wrapper.get(), data_set.get());
+
+  UpdateClientMetadata(
+      SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
+
+  unique_wrapper.reset();
+
+  auto db_provider_withshared = CreateProviderWithSharedDB();
+  auto shared_wrapper =
+      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
+                    GetTestThreadTaskRunner(),
+                    CreateSharedProvider(db_provider_withshared.get()));
+  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, false,
+                     Enums::InitStatus::kOK);
+
+  // Unique db should be deleted in migration. So, unique db should be clean.
+  data_set->clear();
+  VerifyDataInWrapper(shared_wrapper.get(), data_set.get());
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL,
+            GetClientMigrationStatus());
+}
+
+}  // namespace leveldb_proto
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.cc b/components/leveldb_proto/internal/proto_leveldb_wrapper.cc
similarity index 96%
rename from components/leveldb_proto/proto_leveldb_wrapper.cc
rename to components/leveldb_proto/internal/proto_leveldb_wrapper.cc
index 03005fb..46422ba 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper.cc
+++ b/components/leveldb_proto/internal/proto_leveldb_wrapper.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
 
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper_metrics.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h"
 
 namespace leveldb_proto {
 
@@ -142,7 +142,7 @@
 }
 
 bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
-  if (db_ == nullptr)
+  if (!db_)
     return 0;
 
   return db_->GetApproximateMemoryUse(approx_mem_use);
diff --git a/components/leveldb_proto/proto_leveldb_wrapper.h b/components/leveldb_proto/internal/proto_leveldb_wrapper.h
similarity index 97%
rename from components/leveldb_proto/proto_leveldb_wrapper.h
rename to components/leveldb_proto/internal/proto_leveldb_wrapper.h
index f4d2c493..7ab34b3 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper.h
+++ b/components/leveldb_proto/internal/proto_leveldb_wrapper.h
@@ -1,8 +1,8 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_H_
 
 #include <memory>
 #include <string>
@@ -17,9 +17,9 @@
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_checker.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper_metrics.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
 namespace base {
@@ -36,7 +36,6 @@
 // Construction/calls/destruction should all happen on the same thread.
 class ProtoLevelDBWrapper {
  public:
-
   // All blocking calls/disk access will happen on the provided |task_runner|.
   ProtoLevelDBWrapper(
       const scoped_refptr<base::SequencedTaskRunner>& task_runner);
@@ -320,6 +319,7 @@
     std::unique_ptr<typename Util::Internal<T>::KeyEntryVector> entries_to_save,
     const LevelDB::KeyFilter& delete_key_filter,
     Callbacks::UpdateCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UpdateEntriesWithRemoveFilter<T>(std::move(entries_to_save),
                                    delete_key_filter, std::string(),
                                    std::move(callback));
@@ -462,4 +462,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_H_
diff --git a/components/leveldb_proto/proto_leveldb_wrapper_metrics.cc b/components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.cc
similarity index 98%
rename from components/leveldb_proto/proto_leveldb_wrapper_metrics.cc
rename to components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.cc
index e4b656d..3c2630f 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper_metrics.cc
+++ b/components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.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/leveldb_proto/proto_leveldb_wrapper_metrics.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h"
 
 #include "base/metrics/histogram.h"
 #include "third_party/leveldatabase/env_chromium.h"
diff --git a/components/leveldb_proto/proto_leveldb_wrapper_metrics.h b/components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h
similarity index 85%
rename from components/leveldb_proto/proto_leveldb_wrapper_metrics.h
rename to components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h
index d05bb29..2f8ae4f 100644
--- a/components/leveldb_proto/proto_leveldb_wrapper_metrics.h
+++ b/components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.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_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_METRICS_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_METRICS_H_
 
 #include <string>
 
@@ -37,4 +37,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_LEVELDB_WRAPPER_METRICS_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_PROTO_LEVELDB_WRAPPER_METRICS_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/shared_proto_database.cc b/components/leveldb_proto/internal/shared_proto_database.cc
similarity index 65%
rename from components/leveldb_proto/shared_proto_database.cc
rename to components/leveldb_proto/internal/shared_proto_database.cc
index 74911d4..c8c1a2d 100644
--- a/components/leveldb_proto/shared_proto_database.cc
+++ b/components/leveldb_proto/internal/shared_proto_database.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/shared_proto_database.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
 
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto/shared_db_metadata.pb.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/proto/shared_db_metadata.pb.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 
@@ -31,30 +31,31 @@
     base::TimeDelta::FromSeconds(120);
 
 inline void RunInitStatusCallbackOnCallingSequence(
-    Callbacks::InitStatusCallback callback,
+    SharedProtoDatabase::SharedClientInitCallback callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    Enums::InitStatus status) {
-  callback_task_runner->PostTask(FROM_HERE,
-                                 base::BindOnce(std::move(callback), status));
+    Enums::InitStatus status,
+    SharedDBMetadataProto::MigrationStatus migration_status) {
+  callback_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), status, migration_status));
 }
 
 SharedProtoDatabase::InitRequest::InitRequest(
-    Callbacks::InitStatusCallback callback,
+    SharedClientInitCallback callback,
     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    const std::string& client_name)
+    const std::string& client_db_id)
     : callback(std::move(callback)),
       task_runner(std::move(task_runner)),
-      client_name(client_name) {}
+      client_db_id(client_db_id) {}
 
 SharedProtoDatabase::InitRequest::~InitRequest() = default;
 
-SharedProtoDatabase::SharedProtoDatabase(const std::string& client_name,
+SharedProtoDatabase::SharedProtoDatabase(const std::string& client_db_id,
                                          const base::FilePath& db_dir)
     : task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
       db_dir_(db_dir),
-      db_(std::make_unique<LevelDB>(client_name.c_str())),
+      db_(std::make_unique<LevelDB>(client_db_id.c_str())),
       db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner_)),
       metadata_db_(std::make_unique<LevelDB>(kMetadataDatabaseName)),
       metadata_db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner_)),
@@ -68,91 +69,119 @@
 // PostTaskAndReply is used to ensure that we call the Init callback on its
 // original calling thread.
 void SharedProtoDatabase::GetDatabaseInitStatusAsync(
-    const std::string& client_name,
+    const std::string& client_db_id,
     Callbacks::InitStatusCallback callback) {
   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
-  auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
-  task_runner_->PostTaskAndReply(
-      FROM_HERE, base::DoNothing(),
-      base::BindOnce(&SharedProtoDatabase::CheckCorruptionAndRunInitCallback,
-                     weak_factory_->GetWeakPtr(), client_name,
-                     std::move(callback), std::move(current_task_runner),
-                     init_status_));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SharedProtoDatabase::RunInitCallback,
+                     weak_factory_->GetWeakPtr(), std::move(callback),
+                     base::SequencedTaskRunnerHandle::Get()));
 }
 
-// Should be called when processing client init requests after a corruption.
-void SharedProtoDatabase::UpdateClientCorruptAsync(
-    const std::string& client_name,
-    base::OnceCallback<void(bool)> callback) {
-  metadata_db_wrapper_->GetEntry<SharedDBMetadataProto>(
-      std::string(client_name),
-      base::BindOnce(&SharedProtoDatabase::OnGetClientMetadataForUpdate,
-                     weak_factory_->GetWeakPtr(), client_name,
-                     std::move(callback)));
-}
-
-void SharedProtoDatabase::GetClientCorruptAsync(
-    const std::string& client_name,
+void SharedProtoDatabase::RunInitCallback(
     Callbacks::InitStatusCallback callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
-  // |metadata_db_wrapper_| uses the same TaskRunner as Init and the main
-  // DB, so making this call directly here without PostTasking is safe. In
-  // addition, GetEntry uses PostTaskAndReply so the callback will be triggered
-  // on the calling sequence.
-  metadata_db_wrapper_->GetEntry<SharedDBMetadataProto>(
-      std::string(client_name),
-      base::BindOnce(&SharedProtoDatabase::OnGetClientMetadata,
-                     weak_factory_->GetWeakPtr(), std::move(callback),
-                     std::move(callback_task_runner)));
-}
-
-// As mentioned above, |current_task_runner| is the appropriate calling sequence
-// for the callback since the GetEntry call in GetClientCorruptAsync uses
-// PostTaskAndReply.
-void SharedProtoDatabase::OnGetClientMetadata(
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    bool success,
-    std::unique_ptr<SharedDBMetadataProto> proto) {
-  // If we've made it here, we know that the current status of our database is
-  // OK. Make it return corrupt if the metadata disagrees.
   callback_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback),
-                                metadata_->corruptions() != proto->corruptions()
-                                    ? Enums::InitStatus::kCorrupt
-                                    : Enums::InitStatus::kOK));
+      FROM_HERE, base::BindOnce(std::move(callback), init_status_));
 }
 
-void SharedProtoDatabase::OnGetClientMetadataForUpdate(
-    const std::string& client_name,
-    base::OnceCallback<void(bool)> callback,
-    bool success,
-    std::unique_ptr<SharedDBMetadataProto> proto) {
+void SharedProtoDatabase::UpdateClientMetadataAsync(
+    const std::string& client_db_id,
+    SharedDBMetadataProto::MigrationStatus migration_status,
+    base::OnceCallback<void(bool)> callback) {
+  if (base::SequencedTaskRunnerHandle::Get() != task_runner_) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SharedProtoDatabase::UpdateClientMetadataAsync,
+                       weak_factory_->GetWeakPtr(), client_db_id,
+                       migration_status, std::move(callback)));
+    return;
+  }
   auto update_entries = std::make_unique<
       std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
   SharedDBMetadataProto write_proto;
-  write_proto.CheckTypeAndMergeFrom(*proto);
   write_proto.set_corruptions(metadata_->corruptions());
+  write_proto.set_migration_status(migration_status);
   update_entries->emplace_back(
-      std::make_pair(std::string(client_name), write_proto));
+      std::make_pair(std::string(client_db_id), write_proto));
 
   metadata_db_wrapper_->UpdateEntries<SharedDBMetadataProto>(
       std::move(update_entries), std::make_unique<std::vector<std::string>>(),
       std::move(callback));
 }
 
+void SharedProtoDatabase::GetClientMetadataAsync(
+    const std::string& client_db_id,
+    SharedClientInitCallback callback,
+    scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
+  // |metadata_db_wrapper_| uses the same TaskRunner as Init and the main
+  // DB, so making this call directly here without PostTasking is safe. In
+  // addition, GetEntry uses PostTaskAndReply so the callback will be triggered
+  // on the calling sequence.
+  metadata_db_wrapper_->GetEntry<SharedDBMetadataProto>(
+      std::string(client_db_id),
+      base::BindOnce(&SharedProtoDatabase::OnGetClientMetadata,
+                     weak_factory_->GetWeakPtr(), client_db_id,
+                     std::move(callback), std::move(callback_task_runner)));
+}
+
+// As mentioned above, |current_task_runner| is the appropriate calling sequence
+// for the callback since the GetEntry call in GetClientMetadataAsync uses
+// PostTaskAndReply.
+void SharedProtoDatabase::OnGetClientMetadata(
+    const std::string& client_db_id,
+    SharedClientInitCallback callback,
+    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+    bool success,
+    std::unique_ptr<SharedDBMetadataProto> proto) {
+  // If fetching metadata failed, then ignore the error.
+  if (!success) {
+    RunInitStatusCallbackOnCallingSequence(
+        std::move(callback), std::move(callback_task_runner),
+        Enums::InitStatus::kOK, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
+    return;
+  }
+  if (!proto || !proto->has_migration_status()) {
+    UpdateClientMetadataAsync(
+        client_db_id, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+        base::BindOnce(
+            [](SharedClientInitCallback callback,
+               scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+               bool update_success) {
+              // Do not care about update success since next time we will reset
+              // corruption and migration status to 0.
+              RunInitStatusCallbackOnCallingSequence(
+                  std::move(callback), std::move(callback_task_runner),
+                  Enums::InitStatus::kOK,
+                  SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
+            },
+            std::move(callback), std::move(callback_task_runner)));
+    return;
+  }
+  // If we've made it here, we know that the current status of our database is
+  // OK. Make it return corrupt if the metadata disagrees.
+  RunInitStatusCallbackOnCallingSequence(
+      std::move(callback), std::move(callback_task_runner),
+      metadata_->corruptions() != proto->corruptions()
+          ? Enums::InitStatus::kCorrupt
+          : Enums::InitStatus::kOK,
+      proto->migration_status());
+}
+
 void SharedProtoDatabase::CheckCorruptionAndRunInitCallback(
-    const std::string& client_name,
-    Callbacks::InitStatusCallback callback,
+    const std::string& client_db_id,
+    SharedClientInitCallback callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     Enums::InitStatus status) {
   if (init_status_ == Enums::InitStatus::kOK) {
-    GetClientCorruptAsync(client_name, std::move(callback),
-                          std::move(callback_task_runner));
+    GetClientMetadataAsync(client_db_id, std::move(callback),
+                           std::move(callback_task_runner));
     return;
   }
   RunInitStatusCallbackOnCallingSequence(
-      std::move(callback), std::move(callback_task_runner), init_status_);
+      std::move(callback), std::move(callback_task_runner), init_status_,
+      SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
 }
 
 // Setting |create_if_missing| to false allows us to test whether or not the
@@ -164,37 +193,36 @@
 // with this set to true, and others false.
 void SharedProtoDatabase::Init(
     bool create_if_missing,
-    const std::string& client_name,
-    Callbacks::InitStatusCallback callback,
+    const std::string& client_db_id,
+    SharedClientInitCallback callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
-  // If we succeeded previously, just let the callback know. Otherwise, we'll
-  // continue to try initialization for every new request.
+  // If we succeeded previously, just check for corruption status and run init
+  // callback.
   if (init_state_ == InitState::kSuccess) {
-    callback_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  Enums::InitStatus::kOK /* status */));
+    CheckCorruptionAndRunInitCallback(client_db_id, std::move(callback),
+                                      std::move(callback_task_runner),
+                                      Enums::InitStatus::kOK);
     return;
   }
 
-  if (init_state_ == InitState::kInProgress) {
-    outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
-        std::move(callback), std::move(callback_task_runner), client_name));
+  outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
+      std::move(callback), std::move(callback_task_runner), client_db_id));
+  if (init_state_ == InitState::kInProgress)
     return;
-  }
 
   init_state_ = InitState::kInProgress;
   // First, try to initialize the metadata database.
-  InitMetadataDatabase(create_if_missing, std::move(callback),
-                       std::move(callback_task_runner), 0 /* attempt */,
+  InitMetadataDatabase(create_if_missing, 0 /* attempt */,
                        false /* corruption */);
 }
 
-void SharedProtoDatabase::ProcessOutstandingInitRequests(
-    Enums::InitStatus status) {
+void SharedProtoDatabase::ProcessInitRequests(Enums::InitStatus status) {
+  DCHECK(!outstanding_init_requests_.empty());
+
   // The pairs are stored as (callback, callback_task_runner).
   while (!outstanding_init_requests_.empty()) {
     auto request = std::move(outstanding_init_requests_.front());
-    CheckCorruptionAndRunInitCallback(request->client_name,
+    CheckCorruptionAndRunInitCallback(request->client_db_id,
                                       std::move(request->callback),
                                       std::move(request->task_runner), status);
     outstanding_init_requests_.pop();
@@ -206,20 +234,15 @@
 // the event that the metadata DB is corrupt, at least one retry will be made
 // so that we create the DB from scratch again.
 // |corruption| lets us know whether the retries are because of corruption.
-void SharedProtoDatabase::InitMetadataDatabase(
-    bool create_shared_db_if_missing,
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    int attempt,
-    bool corruption) {
+void SharedProtoDatabase::InitMetadataDatabase(bool create_shared_db_if_missing,
+                                               int attempt,
+                                               bool corruption) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
 
   if (attempt >= kMaxInitMetaDatabaseAttempts) {
     init_state_ = InitState::kFailure;
     init_status_ = Enums::InitStatus::kError;
-    callback_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), init_status_));
-    ProcessOutstandingInitRequests(init_status_);
+    ProcessInitRequests(init_status_);
     return;
   }
 
@@ -230,14 +253,11 @@
       true /* destroy_on_corruption */,
       base::BindOnce(&SharedProtoDatabase::OnMetadataInitComplete,
                      weak_factory_->GetWeakPtr(), create_shared_db_if_missing,
-                     std::move(callback), std::move(callback_task_runner),
                      attempt, corruption));
 }
 
 void SharedProtoDatabase::OnMetadataInitComplete(
     bool create_shared_db_if_missing,
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     int attempt,
     bool corruption,
     Enums::InitStatus metadata_init_status) {
@@ -245,8 +265,7 @@
 
   if (metadata_init_status == Enums::InitStatus::kCorrupt) {
     // Retry InitMetaDatabase to create the metadata database from scratch.
-    InitMetadataDatabase(create_shared_db_if_missing, std::move(callback),
-                         std::move(callback_task_runner), ++attempt,
+    InitMetadataDatabase(create_shared_db_if_missing, ++attempt,
                          true /* corruption */);
     return;
   }
@@ -254,9 +273,7 @@
   if (metadata_init_status != Enums::InitStatus::kOK) {
     init_state_ = InitState::kFailure;
     init_status_ = Enums::InitStatus::kError;
-    RunInitStatusCallbackOnCallingSequence(
-        std::move(callback), std::move(callback_task_runner), init_status_);
-    ProcessOutstandingInitRequests(init_status_);
+    ProcessInitRequests(init_status_);
     return;
   }
 
@@ -267,14 +284,11 @@
       std::string(kGlobalMetadataKey),
       base::BindOnce(&SharedProtoDatabase::OnGetGlobalMetadata,
                      weak_factory_->GetWeakPtr(), create_shared_db_if_missing,
-                     std::move(callback), std::move(callback_task_runner),
                      corruption));
 }
 
 void SharedProtoDatabase::OnGetGlobalMetadata(
     bool create_shared_db_if_missing,
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     bool corruption,
     bool success,
     std::unique_ptr<SharedDBMetadataProto> proto) {
@@ -282,8 +296,7 @@
   if (success && proto) {
     // It existed so let's update our internal |corruption_count_|
     metadata_ = std::move(proto);
-    InitDatabase(create_shared_db_if_missing, std::move(callback),
-                 std::move(callback_task_runner));
+    InitDatabase(create_shared_db_if_missing);
     return;
   }
 
@@ -291,24 +304,14 @@
   // time.
   metadata_.reset(new SharedDBMetadataProto());
   metadata_->set_corruptions(corruption ? 1U : 0U);
-  auto update_entries = std::make_unique<
-      std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
-
-  SharedDBMetadataProto write_proto;
-  write_proto.CheckTypeAndMergeFrom(*metadata_);
-  update_entries->emplace_back(
-      std::make_pair(std::string(kGlobalMetadataKey), write_proto));
-  metadata_db_wrapper_->UpdateEntries<SharedDBMetadataProto>(
-      std::move(update_entries), std::make_unique<std::vector<std::string>>(),
+  metadata_->clear_migration_status();
+  CommitUpdatedGlobalMetadata(
       base::BindOnce(&SharedProtoDatabase::OnFinishCorruptionCountWrite,
-                     weak_factory_->GetWeakPtr(), create_shared_db_if_missing,
-                     std::move(callback), std::move(callback_task_runner)));
+                     weak_factory_->GetWeakPtr(), create_shared_db_if_missing));
 }
 
 void SharedProtoDatabase::OnFinishCorruptionCountWrite(
     bool create_shared_db_if_missing,
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
     bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
   // TODO(thildebr): Should we retry a few times if we fail this? It feels like
@@ -317,20 +320,14 @@
   if (!success) {
     init_state_ = InitState::kFailure;
     init_status_ = Enums::InitStatus::kError;
-    RunInitStatusCallbackOnCallingSequence(
-        std::move(callback), std::move(callback_task_runner), init_status_);
-    ProcessOutstandingInitRequests(init_status_);
+    ProcessInitRequests(init_status_);
     return;
   }
 
-  InitDatabase(create_shared_db_if_missing, std::move(callback),
-               std::move(callback_task_runner));
+  InitDatabase(create_shared_db_if_missing);
 }
 
-void SharedProtoDatabase::InitDatabase(
-    bool create_shared_db_if_missing,
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
+void SharedProtoDatabase::InitDatabase(bool create_shared_db_if_missing) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
   auto options = CreateSimpleOptions();
   options.create_if_missing = create_shared_db_if_missing;
@@ -341,30 +338,10 @@
   db_wrapper_->InitWithDatabase(
       db_.get(), db_dir_, options, false /* destroy_on_corruption */,
       base::BindOnce(&SharedProtoDatabase::OnDatabaseInit,
-                     weak_factory_->GetWeakPtr(), std::move(callback),
-                     std::move(callback_task_runner)));
+                     weak_factory_->GetWeakPtr()));
 }
 
-void SharedProtoDatabase::OnUpdateCorruptionCount(
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    bool success) {
-  // Return the success value of our write to update the corruption counter.
-  // This means that we return kError when the update fails, as a safeguard
-  // against clients trying to further persist data when something's gone
-  // wrong loading a single metadata proto.
-  init_state_ = success ? InitState::kSuccess : InitState::kFailure;
-  init_status_ =
-      success ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kError;
-  RunInitStatusCallbackOnCallingSequence(
-      std::move(callback), std::move(callback_task_runner), init_status_);
-  ProcessOutstandingInitRequests(init_status_);
-}
-
-void SharedProtoDatabase::OnDatabaseInit(
-    Callbacks::InitStatusCallback callback,
-    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-    Enums::InitStatus status) {
+void SharedProtoDatabase::OnDatabaseInit(Enums::InitStatus status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
 
   // Update the corruption counter locally and in the database.
@@ -376,28 +353,17 @@
     // Again, it seems like a failure to update here will indicate something
     // serious has gone wrong with the metadata database.
     metadata_->set_corruptions(metadata_->corruptions() + 1);
-    auto update_entries = std::make_unique<
-        std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
 
-    SharedDBMetadataProto write_proto;
-    write_proto.CheckTypeAndMergeFrom(*metadata_);
-    update_entries->emplace_back(
-        std::make_pair(std::string(kGlobalMetadataKey), write_proto));
-    metadata_db_wrapper_->UpdateEntries<SharedDBMetadataProto>(
-        std::move(update_entries), std::make_unique<std::vector<std::string>>(),
-        base::BindOnce(&SharedProtoDatabase::OnUpdateCorruptionCount,
-                       weak_factory_->GetWeakPtr(), std::move(callback),
-                       std::move(callback_task_runner)));
+    CommitUpdatedGlobalMetadata(
+        base::BindOnce(&SharedProtoDatabase::OnUpdateCorruptionCountAtInit,
+                       weak_factory_->GetWeakPtr()));
     return;
   }
 
   init_status_ = status;
   init_state_ = status == Enums::InitStatus::kOK ? InitState::kSuccess
                                                  : InitState::kFailure;
-
-  callback_task_runner->PostTask(FROM_HERE,
-                                 base::BindOnce(std::move(callback), status));
-  ProcessOutstandingInitRequests(status);
+  ProcessInitRequests(status);
 
   if (init_state_ == InitState::kSuccess) {
     // Create a ProtoLevelDBWrapper just like we create for each client, for
@@ -416,7 +382,34 @@
   }
 }
 
+void SharedProtoDatabase::OnUpdateCorruptionCountAtInit(bool success) {
+  // Return the success value of our write to update the corruption counter.
+  // This means that we return kError when the update fails, as a safeguard
+  // against clients trying to further persist data when something's gone
+  // wrong loading a single metadata proto.
+  init_state_ = success ? InitState::kSuccess : InitState::kFailure;
+  init_status_ =
+      success ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kError;
+  ProcessInitRequests(init_status_);
+}
+
+void SharedProtoDatabase::CommitUpdatedGlobalMetadata(
+    Callbacks::UpdateCallback callback) {
+  auto update_entries = std::make_unique<
+      std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
+
+  SharedDBMetadataProto write_proto;
+  write_proto.CheckTypeAndMergeFrom(*metadata_);
+  update_entries->emplace_back(
+      std::make_pair(std::string(kGlobalMetadataKey), write_proto));
+  metadata_db_wrapper_->UpdateEntries<SharedDBMetadataProto>(
+      std::move(update_entries), std::make_unique<std::vector<std::string>>(),
+      std::move(callback));
+}
+
 SharedProtoDatabase::~SharedProtoDatabase() {
+  task_runner_->DeleteSoon(FROM_HERE, std::move(db_));
+  task_runner_->DeleteSoon(FROM_HERE, std::move(metadata_db_));
   task_runner_->DeleteSoon(FROM_HERE, std::move(weak_factory_));
 }
 
diff --git a/components/leveldb_proto/shared_proto_database.h b/components/leveldb_proto/internal/shared_proto_database.h
similarity index 64%
rename from components/leveldb_proto/shared_proto_database.h
rename to components/leveldb_proto/internal/shared_proto_database.h
index 9e77cf4..074d78a 100644
--- a/components/leveldb_proto/shared_proto_database.h
+++ b/components/leveldb_proto/internal/shared_proto_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_LEVELDB_PROTO_SHARED_PROTO_DATABASE_H_
-#define COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_
 
 #include <queue>
 
@@ -13,36 +13,34 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
-#include "components/leveldb_proto/shared_proto_database_client.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/proto/shared_db_metadata.pb.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/shared_proto_database_client.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 
 class SharedDBMetadataProto;
 
-template <typename T>
-void GetClientInitCallback(
-    base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient<T>>)>
-        callback,
-    std::unique_ptr<SharedProtoDatabaseClient<T>> client,
-    Enums::InitStatus status);
-
 // Controls a single LevelDB database to be used by many clients, and provides
 // a way to get SharedProtoDatabaseClients that allow shared access to the
 // underlying single database.
 class SharedProtoDatabase
     : public base::RefCountedThreadSafe<SharedProtoDatabase> {
  public:
+  using SharedClientInitCallback =
+      base::OnceCallback<void(Enums::InitStatus,
+                              SharedDBMetadataProto::MigrationStatus)>;
+
   // Always returns a SharedProtoDatabaseClient pointer, but that should ONLY
   // be used if the callback returns success.
   template <typename T>
-  std::unique_ptr<SharedProtoDatabaseClient<T>> GetClient(
+  std::unique_ptr<SharedProtoDatabaseClient<T>> GetClientForTesting(
       const std::string& client_namespace,
       const std::string& type_prefix,
       bool create_if_missing,
-      Callbacks::InitStatusCallback callback);
+      SharedClientInitCallback callback);
 
   // A version of GetClient that returns the client in a callback instead of
   // giving back a client instance immediately.
@@ -54,11 +52,13 @@
       base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient<T>>)>
           callback);
 
-  void GetDatabaseInitStatusAsync(const std::string& client_name,
+  void GetDatabaseInitStatusAsync(const std::string& client_db_id,
                                   Callbacks::InitStatusCallback callback);
 
-  void UpdateClientCorruptAsync(const std::string& client_name,
-                                base::OnceCallback<void(bool)> callback);
+  void UpdateClientMetadataAsync(
+      const std::string& client_db_id,
+      SharedDBMetadataProto::MigrationStatus migration_status,
+      Callbacks::UpdateCallback callback);
 
  private:
   friend class base::RefCountedThreadSafe<SharedProtoDatabase>;
@@ -76,15 +76,15 @@
   };
 
   struct InitRequest {
-    InitRequest(Callbacks::InitStatusCallback callback,
+    InitRequest(SharedClientInitCallback callback,
                 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-                const std::string& client_name);
+                const std::string& client_db_id);
 
     ~InitRequest();
 
-    Callbacks::InitStatusCallback callback;
+    SharedClientInitCallback callback;
     scoped_refptr<base::SequencedTaskRunner> task_runner;
-    std::string client_name;
+    std::string client_db_id;
   };
 
   // Make sure to give enough time after startup so that we have less chance of
@@ -92,13 +92,12 @@
   static const base::TimeDelta kDelayToClearObsoleteDatabase;
 
   // Private since we only want to create a singleton of it.
-  SharedProtoDatabase(
-      const std::string& client_name,
-      const base::FilePath& db_dir);
+  SharedProtoDatabase(const std::string& client_db_id,
+                      const base::FilePath& db_dir);
 
   virtual ~SharedProtoDatabase();
 
-  void ProcessOutstandingInitRequests(Enums::InitStatus status);
+  void ProcessInitRequests(Enums::InitStatus status);
 
   template <typename T>
   std::unique_ptr<SharedProtoDatabaseClient<T>> GetClientInternal(
@@ -106,66 +105,47 @@
       const std::string& type_prefix);
 
   void OnGetClientMetadata(
-      Callbacks::InitStatusCallback callback,
+      const std::string& client_db_id,
+      SharedClientInitCallback callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       bool success,
       std::unique_ptr<SharedDBMetadataProto> proto);
-  void OnGetClientMetadataForUpdate(
-      const std::string& client_name,
-      base::OnceCallback<void(bool)> callback,
-      bool success,
-      std::unique_ptr<SharedDBMetadataProto> proto);
 
   // |callback_task_runner| should be the same sequence that Init was called
   // from.
   void Init(bool create_if_missing,
-            const std::string& client_name,
-            Callbacks::InitStatusCallback callback,
+            const std::string& client_db_id,
+            SharedClientInitCallback callback,
             scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
-  void InitMetadataDatabase(
-      bool create_shared_db_if_missing,
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      int attempt,
-      bool corruption);
-  void OnMetadataInitComplete(
-      bool create_shared_db_if_missing,
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      int attempt,
-      bool corruption,
-      Enums::InitStatus status);
-  void OnGetGlobalMetadata(
-      bool create_shared_db_if_missing,
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      bool corruption,
-      bool success,
-      std::unique_ptr<SharedDBMetadataProto> proto);
-  void OnFinishCorruptionCountWrite(
-      bool create_shared_db_if_missing,
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      bool success);
-  void InitDatabase(
-      bool create_shared_db_if_missing,
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
-  void OnUpdateCorruptionCount(
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      bool success);
-  void OnDatabaseInit(
-      Callbacks::InitStatusCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
-      Enums::InitStatus status);
+  void InitMetadataDatabase(bool create_shared_db_if_missing,
+                            int attempt,
+                            bool corruption);
+  void OnMetadataInitComplete(bool create_shared_db_if_missing,
+                              int attempt,
+                              bool corruption,
+                              Enums::InitStatus status);
+  void OnGetGlobalMetadata(bool create_shared_db_if_missing,
+                           bool corruption,
+                           bool success,
+                           std::unique_ptr<SharedDBMetadataProto> proto);
+  void OnFinishCorruptionCountWrite(bool create_shared_db_if_missing,
+                                    bool success);
+  void InitDatabase(bool create_shared_db_if_missing);
+  void OnDatabaseInit(Enums::InitStatus status);
   void CheckCorruptionAndRunInitCallback(
-      const std::string& client_name,
-      Callbacks::InitStatusCallback callback,
+      const std::string& client_db_id,
+      SharedClientInitCallback callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       Enums::InitStatus status);
-  void GetClientCorruptAsync(
-      const std::string& client_name,
+  void GetClientMetadataAsync(
+      const std::string& client_db_id,
+      SharedClientInitCallback callback,
+      scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
+  void OnUpdateCorruptionCountAtInit(bool success);
+
+  void CommitUpdatedGlobalMetadata(Callbacks::UpdateCallback callback);
+
+  void RunInitCallback(
       Callbacks::InitStatusCallback callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
 
@@ -209,7 +189,8 @@
     base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient<T>>)>
         callback,
     std::unique_ptr<SharedProtoDatabaseClient<T>> client,
-    Enums::InitStatus status) {
+    Enums::InitStatus status,
+    SharedDBMetadataProto::MigrationStatus migration_status) {
   // |current_task_runner| is valid because Init already takes the current
   // TaskRunner as a parameter and uses that to trigger this callback when it's
   // finished.
@@ -217,6 +198,9 @@
   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
   if (status != Enums::InitStatus::kOK && status != Enums::InitStatus::kCorrupt)
     client.reset();
+  // Set migration status of client. The metadata database was already updated.
+  if (client)
+    client->set_migration_status(migration_status);
   current_task_runner->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(client)));
 }
@@ -231,10 +215,11 @@
   auto client = GetClientInternal<T>(client_namespace, type_prefix);
   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
+  SharedProtoDatabaseClient<T>* client_ptr = client.get();
   task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&SharedProtoDatabase::Init, weak_factory_->GetWeakPtr(),
-                     create_if_missing, client_namespace,
+                     create_if_missing, client_ptr->client_db_id(),
                      base::BindOnce(&GetClientInitCallback<T>,
                                     std::move(callback), std::move(client)),
                      std::move(current_task_runner)));
@@ -243,19 +228,20 @@
 // TODO(thildebr): Need to pass the client name into this call as well, and use
 // it with the pending requests too so we can clean up the database.
 template <typename T>
-std::unique_ptr<SharedProtoDatabaseClient<T>> SharedProtoDatabase::GetClient(
-    const std::string& client_namespace,
-    const std::string& type_prefix,
-    bool create_if_missing,
-    Callbacks::InitStatusCallback callback) {
+std::unique_ptr<SharedProtoDatabaseClient<T>>
+SharedProtoDatabase::GetClientForTesting(const std::string& client_namespace,
+                                         const std::string& type_prefix,
+                                         bool create_if_missing,
+                                         SharedClientInitCallback callback) {
   DCHECK(base::SequencedTaskRunnerHandle::IsSet());
   auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
+  auto client = GetClientInternal<T>(client_namespace, type_prefix);
   task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&SharedProtoDatabase::Init, weak_factory_->GetWeakPtr(),
-                     create_if_missing, client_namespace, std::move(callback),
-                     std::move(current_task_runner)));
-  return GetClientInternal<T>(client_namespace, type_prefix);
+                     create_if_missing, client->client_db_id(),
+                     std::move(callback), std::move(current_task_runner)));
+  return client;
 }
 
 template <typename T>
@@ -269,4 +255,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_H_
diff --git a/components/leveldb_proto/shared_proto_database_client.cc b/components/leveldb_proto/internal/shared_proto_database_client.cc
similarity index 84%
rename from components/leveldb_proto/shared_proto_database_client.cc
rename to components/leveldb_proto/internal/shared_proto_database_client.cc
index 14baa8e..568905b 100644
--- a/components/leveldb_proto/shared_proto_database_client.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_client.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/shared_proto_database_client.h"
+#include "components/leveldb_proto/internal/shared_proto_database_client.h"
 
 #include "base/strings/strcat.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
-#include "components/leveldb_proto/shared_proto_database.h"
-#include "components/leveldb_proto/shared_proto_database_client_list.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
+#include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 
 namespace leveldb_proto {
 namespace {
@@ -61,17 +61,19 @@
 }
 
 void GetSharedDatabaseInitStatusAsync(
-    const std::string& client_name,
+    const std::string& client_db_id,
     const scoped_refptr<SharedProtoDatabase>& shared_db,
     Callbacks::InitStatusCallback callback) {
-  shared_db->GetDatabaseInitStatusAsync(client_name, std::move(callback));
+  shared_db->GetDatabaseInitStatusAsync(client_db_id, std::move(callback));
 }
 
-void UpdateClientCorruptAsync(
+void UpdateClientMetadataAsync(
     const scoped_refptr<SharedProtoDatabase>& shared_db,
-    const std::string& client_name,
+    const std::string& client_db_id,
+    SharedDBMetadataProto::MigrationStatus migration_status,
     ClientCorruptCallback callback) {
-  shared_db->UpdateClientCorruptAsync(client_name, std::move(callback));
+  shared_db->UpdateClientMetadataAsync(client_db_id, migration_status,
+                                       std::move(callback));
 }
 
 void DestroyObsoleteSharedProtoDatabaseClients(
diff --git a/components/leveldb_proto/shared_proto_database_client.h b/components/leveldb_proto/internal/shared_proto_database_client.h
similarity index 85%
rename from components/leveldb_proto/shared_proto_database_client.h
rename to components/leveldb_proto/internal/shared_proto_database_client.h
index 6d6e294e..8e03504 100644
--- a/components/leveldb_proto/shared_proto_database_client.h
+++ b/components/leveldb_proto/internal/shared_proto_database_client.h
@@ -2,23 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_
-#define COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_CLIENT_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_CLIENT_H_
 
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
-#include "components/leveldb_proto/unique_proto_database.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/proto/shared_db_metadata.pb.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/unique_proto_database.h"
 
 namespace leveldb_proto {
 
 class SharedProtoDatabase;
 
 using ClientCorruptCallback = base::OnceCallback<void(bool)>;
+using SharedClientInitCallback =
+    base::OnceCallback<void(Enums::InitStatus,
+                            SharedDBMetadataProto::MigrationStatus)>;
 
 std::string StripPrefix(const std::string& key, const std::string& prefix);
 std::unique_ptr<std::vector<std::string>> PrefixStrings(
@@ -32,13 +36,15 @@
     const std::string& prefix);
 
 void GetSharedDatabaseInitStatusAsync(
-    const std::string& client_name,
+    const std::string& client_db_id,
     const scoped_refptr<SharedProtoDatabase>& db,
     Callbacks::InitStatusCallback callback);
 
-void UpdateClientCorruptAsync(const scoped_refptr<SharedProtoDatabase>& db,
-                              const std::string& client_name,
-                              ClientCorruptCallback callback);
+void UpdateClientMetadataAsync(
+    const scoped_refptr<SharedProtoDatabase>& db,
+    const std::string& client_db_id,
+    SharedDBMetadataProto::MigrationStatus migration_status,
+    ClientCorruptCallback callback);
 
 // Destroys all the data from obsolete clients, for the given |db_wrapper|
 // instance. |callback| is called once all the obsolete clients data are
@@ -60,10 +66,10 @@
  public:
   virtual ~SharedProtoDatabaseClient();
 
-  void Init(const std::string& client_name,
+  void Init(const std::string& client_uma_name,
             Callbacks::InitStatusCallback callback) override;
 
-  void Init(const char* client_name,
+  void Init(const char* client_uma_name,
             const base::FilePath& database_dir,
             const leveldb_env::Options& options,
             Callbacks::InitCallback callback) override;
@@ -131,6 +137,19 @@
 
   typename Callbacks::InitCallback GetInitCallback() const;
 
+  const std::string& client_db_id() const { return prefix_; }
+
+  void set_migration_status(
+      SharedDBMetadataProto::MigrationStatus migration_status) {
+    migration_status_ = migration_status;
+  }
+
+  void UpdateClientInitMetadata(SharedDBMetadataProto::MigrationStatus);
+
+  SharedDBMetadataProto::MigrationStatus migration_status() const {
+    return migration_status_;
+  }
+
  private:
   friend class SharedProtoDatabase;
   friend class SharedProtoDatabaseTest;
@@ -159,14 +178,15 @@
       std::unique_ptr<typename Util::Internal<T>::KeyEntryVector> kev,
       const std::string& prefix);
 
-  void UpdateClientCorruptionCount();
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   // |is_corrupt_| should be set by the SharedProtoDatabase that creates this
   // when a client is created that doesn't know about a previous shared
   // database corruption.
   bool is_corrupt_ = false;
+  SharedDBMetadataProto::MigrationStatus migration_status_ =
+      SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED;
+
   std::string prefix_;
   std::string client_name_;
 
@@ -200,15 +220,14 @@
 
 template <typename T>
 void SharedProtoDatabaseClient<T>::Init(
-    const std::string& client_name,
+    const std::string& client_uma_name,
     Callbacks::InitStatusCallback callback) {
-  unique_db_->SetMetricsId(client_name);
-  GetSharedDatabaseInitStatusAsync(client_name, parent_db_,
-                                   std::move(callback));
+  unique_db_->SetMetricsId(client_uma_name);
+  GetSharedDatabaseInitStatusAsync(prefix_, parent_db_, std::move(callback));
 }
 
 template <typename T>
-void SharedProtoDatabaseClient<T>::Init(const char* client_name,
+void SharedProtoDatabaseClient<T>::Init(const char* client_uma_name,
                                         const base::FilePath& database_dir,
                                         const leveldb_env::Options& options,
                                         Callbacks::InitCallback callback) {
@@ -366,17 +385,20 @@
 }
 
 template <typename T>
-void SharedProtoDatabaseClient<T>::UpdateClientCorruptionCount() {
+void SharedProtoDatabaseClient<T>::UpdateClientInitMetadata(
+    SharedDBMetadataProto::MigrationStatus migration_status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  migration_status_ = migration_status;
   // Tell the SharedProtoDatabase that we've seen the corruption state so it's
   // safe to update its records for this client.
-  UpdateClientCorruptAsync(parent_db_, client_name_,
-                           base::BindOnce([](bool success) {
-                             // TODO(thildebr): Should we do anything special
-                             // here? If the shared DB can't update the client's
-                             // corruption counter to match its own, then the
-                             // client will think it's corrupt on the next Init
-                             // as well.
-                           }));
+  UpdateClientMetadataAsync(parent_db_, prefix_, migration_status_,
+                            base::BindOnce([](bool success) {
+                              // TODO(thildebr): Should we do anything special
+                              // here? If the shared DB can't update the
+                              // client's corruption counter to match its own,
+                              // then the client will think it's corrupt on the
+                              // next Init as well.
+                            }));
 }
 
 // static
@@ -421,4 +443,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_CLIENT_H_
diff --git a/components/leveldb_proto/shared_proto_database_client_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
similarity index 85%
rename from components/leveldb_proto/shared_proto_database_client_unittest.cc
rename to components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
index 35e9516b..4fabc5a 100644
--- a/components/leveldb_proto/shared_proto_database_client_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/shared_proto_database_client.h"
+#include "components/leveldb_proto/internal/shared_proto_database_client.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -11,8 +11,8 @@
 #include "base/strings/string_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
-#include "components/leveldb_proto/shared_proto_database.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,6 +25,11 @@
 const char* kDefaultNamespace2 = "cfd";
 const char* kDefaultTypePrefix = "tp";
 
+void DeleteSoon(scoped_refptr<SharedProtoDatabase> db,
+                std::unique_ptr<base::ScopedTempDir>) {
+  db.reset();
+}
+
 }  // namespace
 
 class SharedProtoDatabaseClientTest : public testing::Test {
@@ -37,8 +42,11 @@
   }
 
   void TearDown() override {
-    db_.reset();
-    temp_dir_.reset();
+    // TODO(ssid): SharedProtoDatabase should use scoped_refptr and this
+    // shouldn't be required.
+    db_->database_task_runner_for_testing()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&DeleteSoon, std::move(db_), std::move(temp_dir_)));
   }
 
  protected:
@@ -52,13 +60,19 @@
       const std::string& client_namespace,
       const std::string& type_prefix,
       bool create_if_missing,
-      Callbacks::InitStatusCallback callback) {
-    return db_->GetClient<T>(
+      Callbacks::InitStatusCallback callback,
+      SharedDBMetadataProto::MigrationStatus expected_migration_status =
+          SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED) {
+    return db_->GetClientForTesting<T>(
         client_namespace, type_prefix, create_if_missing,
         base::BindOnce(
-            [](Callbacks::InitStatusCallback callback,
-               Enums::InitStatus status) { std::move(callback).Run(status); },
-            std::move(callback)));
+            [](SharedDBMetadataProto::MigrationStatus expected_migration_status,
+               Callbacks::InitStatusCallback callback, Enums::InitStatus status,
+               SharedDBMetadataProto::MigrationStatus migration_status) {
+              EXPECT_EQ(expected_migration_status, migration_status);
+              std::move(callback).Run(status);
+            },
+            expected_migration_status, std::move(callback)));
   }
 
   template <typename T>
@@ -66,7 +80,9 @@
       const std::string& client_namespace,
       const std::string& type_prefix,
       bool create_if_missing,
-      Enums::InitStatus* status) {
+      Enums::InitStatus* status,
+      SharedDBMetadataProto::MigrationStatus expected_migration_status =
+          SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED) {
     base::RunLoop loop;
     auto client = GetClient<T>(
         client_namespace, type_prefix, create_if_missing,
@@ -76,7 +92,8 @@
               *status_out = status;
               std::move(closure).Run();
             },
-            status, loop.QuitClosure()));
+            status, loop.QuitClosure()),
+        expected_migration_status);
     loop.Run();
     return client;
   }
@@ -278,6 +295,22 @@
     SetObsoleteClientListForTesting(nullptr);
   }
 
+  void UpdateMetadataAsync(
+      SharedProtoDatabaseClient<TestProto>* client,
+      SharedDBMetadataProto::MigrationStatus migration_status) {
+    base::RunLoop wait_loop;
+    Callbacks::UpdateCallback wait_callback = base::BindOnce(
+        [](base::OnceClosure closure, bool success) {
+          EXPECT_TRUE(success);
+          std::move(closure).Run();
+        },
+        wait_loop.QuitClosure());
+    client->set_migration_status(migration_status);
+    UpdateClientMetadataAsync(client->parent_db_, client->prefix_,
+                              migration_status, std::move(wait_callback));
+    wait_loop.Run();
+  }
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
@@ -641,4 +674,58 @@
   ASSERT_EQ(keys.size(), 0U);
 }
 
+TEST_F(SharedProtoDatabaseClientTest, UpdateClientMetadataAsync) {
+  auto status = Enums::InitStatus::kError;
+  auto client_a =
+      GetClientAndWait<TestProto>(kDefaultNamespace, kDefaultTypePrefix,
+                                  true /* create_if_missing */, &status);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            client_a->migration_status());
+
+  auto client_b =
+      GetClientAndWait<TestProto>(kDefaultNamespace1, kDefaultTypePrefix,
+                                  true /* create_if_missing */, &status);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            client_b->migration_status());
+
+  auto client_c =
+      GetClientAndWait<TestProto>(kDefaultNamespace2, kDefaultTypePrefix,
+                                  true /* create_if_missing */, &status);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+  EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+            client_c->migration_status());
+
+  UpdateMetadataAsync(client_a.get(),
+                      SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL);
+  UpdateMetadataAsync(
+      client_b.get(),
+      SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
+
+  client_a.reset();
+  client_b.reset();
+  client_c.reset();
+
+  auto client_d = GetClientAndWait<TestProto>(
+      kDefaultNamespace, kDefaultTypePrefix, true /* create_if_missing */,
+      &status, SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+
+  auto client_e = GetClientAndWait<TestProto>(
+      kDefaultNamespace1, kDefaultTypePrefix, true /* create_if_missing */,
+      &status, SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+
+  auto client_f =
+      GetClientAndWait<TestProto>(kDefaultNamespace2, kDefaultTypePrefix,
+                                  true /* create_if_missing */, &status);
+  EXPECT_EQ(status, Enums::InitStatus::kOK);
+
+  UpdateMetadataAsync(client_d.get(),
+                      SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
+  UpdateMetadataAsync(client_e.get(),
+                      SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
+}
+
 }  // namespace leveldb_proto
\ No newline at end of file
diff --git a/components/leveldb_proto/shared_proto_database_provider.cc b/components/leveldb_proto/internal/shared_proto_database_provider.cc
similarity index 87%
rename from components/leveldb_proto/shared_proto_database_provider.cc
rename to components/leveldb_proto/internal/shared_proto_database_provider.cc
index a59389e0..2f45ac4 100644
--- a/components/leveldb_proto/shared_proto_database_provider.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_provider.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/shared_proto_database_provider.h"
+#include "components/leveldb_proto/internal/shared_proto_database_provider.h"
 
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 namespace leveldb_proto {
 
diff --git a/components/leveldb_proto/shared_proto_database_provider.h b/components/leveldb_proto/internal/shared_proto_database_provider.h
similarity index 84%
rename from components/leveldb_proto/shared_proto_database_provider.h
rename to components/leveldb_proto/internal/shared_proto_database_provider.h
index 3d63e63..f5bad1f 100644
--- a/components/leveldb_proto/shared_proto_database_provider.h
+++ b/components/leveldb_proto/internal/shared_proto_database_provider.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_LEVELDB_PROTO_SHARED_PROTO_DATABASE_PROVIDER_H_
-#define COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_PROVIDER_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_PROVIDER_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_PROVIDER_H_
 
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
@@ -42,4 +42,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_PROVIDER_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_SHARED_PROTO_DATABASE_PROVIDER_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/shared_proto_database_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
similarity index 79%
rename from components/leveldb_proto/shared_proto_database_unittest.cc
rename to components/leveldb_proto/internal/shared_proto_database_unittest.cc
index c98e036..bfdf5c39 100644
--- a/components/leveldb_proto/shared_proto_database_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/shared_proto_database.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
 
 #include "base/bind.h"
 #include "base/files/scoped_temp_dir.h"
@@ -10,7 +10,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -26,11 +26,16 @@
                                     const std::string& client_namespace,
                                     const std::string& type_prefix,
                                     base::OnceClosure closure) {
-  db->GetClient<TestProto>(
+  db->GetClientForTesting<TestProto>(
       client_namespace, type_prefix, true /* create_if_missing */,
-      base::BindOnce([](base::OnceClosure closure,
-                        Enums::InitStatus status) { std::move(closure).Run(); },
-                     std::move(closure)));
+      base::BindOnce(
+          [](base::OnceClosure closure, Enums::InitStatus status,
+             SharedDBMetadataProto::MigrationStatus migration_status) {
+            EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                      migration_status);
+            std::move(closure).Run();
+          },
+          std::move(closure)));
 }
 
 }  // namespace
@@ -48,9 +53,13 @@
 
   void InitDB(bool create_if_missing,
               const std::string& client_name,
-              Callbacks::InitStatusCallback callback) {
-    db_->Init(create_if_missing, client_name, std::move(callback),
-              scoped_task_environment_.GetMainThreadTaskRunner());
+              SharedClientInitCallback callback) {
+    db_->task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SharedProtoDatabase::Init,
+                       db_->weak_factory_->GetWeakPtr(), create_if_missing,
+                       client_name, std::move(callback),
+                       scoped_task_environment_.GetMainThreadTaskRunner()));
   }
 
   void KillDB() { db_.reset(); }
@@ -67,11 +76,14 @@
       bool create_if_missing,
       Enums::InitStatus* status) {
     base::RunLoop loop;
-    auto client = db->GetClient<T>(
+    auto client = db->GetClientForTesting<T>(
         client_namespace, type_prefix, create_if_missing,
         base::BindOnce(
             [](Enums::InitStatus* status_out, base::OnceClosure closure,
-               Enums::InitStatus status) {
+               Enums::InitStatus status,
+               SharedDBMetadataProto::MigrationStatus migration_status) {
+              EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                        migration_status);
               *status_out = status;
               std::move(closure).Run();
             },
@@ -149,7 +161,10 @@
   base::RunLoop run_init_loop;
   InitDB(true /* create_if_missing */, kDefaultNamespace,
          base::BindOnce(
-             [](base::OnceClosure signal, Enums::InitStatus status) {
+             [](base::OnceClosure signal, Enums::InitStatus status,
+                SharedDBMetadataProto::MigrationStatus migration_status) {
+               EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                         migration_status);
                ASSERT_EQ(status, Enums::InitStatus::kOK);
                std::move(signal).Run();
              },
diff --git a/components/leveldb_proto/unique_proto_database.h b/components/leveldb_proto/internal/unique_proto_database.h
similarity index 96%
rename from components/leveldb_proto/unique_proto_database.h
rename to components/leveldb_proto/internal/unique_proto_database.h
index 8720b302..c8ed4302 100644
--- a/components/leveldb_proto/unique_proto_database.h
+++ b/components/leveldb_proto/internal/unique_proto_database.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
-#define COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_INTERNAL_UNIQUE_PROTO_DATABASE_H_
+#define COMPONENTS_LEVELDB_PROTO_INTERNAL_UNIQUE_PROTO_DATABASE_H_
 
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_checker.h"
-#include "components/leveldb_proto/leveldb_database.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
+#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 
@@ -319,4 +319,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_INTERNAL_UNIQUE_PROTO_DATABASE_H_
diff --git a/components/leveldb_proto/unique_proto_database_unittest.cc b/components/leveldb_proto/internal/unique_proto_database_unittest.cc
similarity index 99%
rename from components/leveldb_proto/unique_proto_database_unittest.cc
rename to components/leveldb_proto/internal/unique_proto_database_unittest.cc
index 877ee5a..ea578817 100644
--- a/components/leveldb_proto/unique_proto_database_unittest.cc
+++ b/components/leveldb_proto/internal/unique_proto_database_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/unique_proto_database.h"
+#include "components/leveldb_proto/internal/unique_proto_database.h"
 
 #include <stddef.h>
 
@@ -20,7 +20,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/leveldb_proto/proto/shared_db_metadata.proto b/components/leveldb_proto/proto/shared_db_metadata.proto
deleted file mode 100644
index 02c39e8..0000000
--- a/components/leveldb_proto/proto/shared_db_metadata.proto
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-package leveldb_proto;
-
-// A proto to for storing metadata related to a SharedProtoDatabase client.
-//
-// Next tag: 3
-message SharedDBMetadataProto {
-  // For the global database, this is value increases every time a corruption
-  // is discovered, as a way for clients to determine whether there's been a
-  // corruption since the last time they checked.
-  // For clients, this number is set to the global value when they check their
-  // corruption status, to indicate that we don't need to tell them there was
-  // a corruption next time they check.
-  optional uint64 corruptions = 1;
-
-  // Timestamp for when the database was modified.
-  optional uint64 last_modified = 2;
-}
diff --git a/components/leveldb_proto/proto_database_impl.h b/components/leveldb_proto/proto_database_impl.h
deleted file mode 100644
index def7f23..0000000
--- a/components/leveldb_proto/proto_database_impl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
-
-#include "components/leveldb_proto/unique_proto_database.h"
-
-namespace leveldb_proto {
-
-// Part of an ongoing effort to refactor ProtoDatabase. New users of
-// ProtoDatabaseImpl should avoid using this in favor of including
-// unique_proto_database.h and using UniqueProtoDatabase directly.
-// See https://crbug.com/870813.
-template <typename T>
-using ProtoDatabaseImpl = UniqueProtoDatabase<T>;
-
-}  // namespace leveldb_proto
-
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
diff --git a/components/leveldb_proto/proto_database_wrapper_unittest.cc b/components/leveldb_proto/proto_database_wrapper_unittest.cc
deleted file mode 100644
index 00adba7..0000000
--- a/components/leveldb_proto/proto_database_wrapper_unittest.cc
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/leveldb_proto/proto_database_wrapper.h"
-
-#include "base/files/scoped_temp_dir.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread.h"
-#include "components/leveldb_proto/proto_database_provider.h"
-#include "components/leveldb_proto/shared_proto_database_provider.h"
-#include "components/leveldb_proto/testing/proto/test_db.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace leveldb_proto {
-
-namespace {
-
-const std::string kDefaultNamespace = "namespace";
-const std::string kDefaultTypePrefix = "prefix";
-const std::string kDefaultClientName = "client";
-
-}  // namespace
-
-class TestProtoDatabaseProvider : public ProtoDatabaseProvider {
- public:
-  TestProtoDatabaseProvider(const base::FilePath& profile_dir)
-      : ProtoDatabaseProvider(profile_dir) {}
-  TestProtoDatabaseProvider(const base::FilePath& profile_dir,
-                            const scoped_refptr<SharedProtoDatabase>& shared_db)
-      : ProtoDatabaseProvider(profile_dir) {
-    shared_db_ = shared_db;
-  }
-
-  void GetSharedDBInstance(
-      ProtoDatabaseProvider::GetSharedDBInstanceCallback callback,
-      scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {
-    callback_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), shared_db_));
-  }
-
- private:
-  scoped_refptr<SharedProtoDatabase> shared_db_;
-};
-
-class TestSharedProtoDatabaseProvider : public SharedProtoDatabaseProvider {
- public:
-  TestSharedProtoDatabaseProvider(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      base::WeakPtr<ProtoDatabaseProvider> provider_weak_ptr)
-      : SharedProtoDatabaseProvider(std::move(task_runner),
-                                    std::move(provider_weak_ptr)) {}
-};
-
-class ProtoDatabaseWrapperTest : public testing::Test {
- public:
-  void SetUp() override {
-    temp_dir_ = std::make_unique<base::ScopedTempDir>();
-    ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
-    shared_db_temp_dir_ = std::make_unique<base::ScopedTempDir>();
-    ASSERT_TRUE(shared_db_temp_dir_->CreateUniqueTempDir());
-    test_thread_ = std::make_unique<base::Thread>("test_thread");
-    ASSERT_TRUE(test_thread_->Start());
-    shared_db_ = base::WrapRefCounted(
-        new SharedProtoDatabase("client", shared_db_temp_dir_->GetPath()));
-  }
-
-  void TearDown() override {
-    temp_dir_.reset();
-    shared_db_temp_dir_.reset();
-  }
-
-  std::unique_ptr<ProtoDatabaseWrapper<TestProto>> CreateWrapper(
-      const std::string& client_namespace,
-      const std::string& type_prefix,
-      const base::FilePath& db_dir,
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      std::unique_ptr<SharedProtoDatabaseProvider> db_provider) {
-    return std::make_unique<ProtoDatabaseWrapper<TestProto>>(
-        client_namespace, type_prefix, db_dir, task_runner,
-        std::move(db_provider));
-  }
-
-  std::unique_ptr<TestProtoDatabaseProvider> CreateProviderNoSharedDB() {
-    return std::make_unique<TestProtoDatabaseProvider>(
-        shared_db_temp_dir_->GetPath());
-  }
-
-  std::unique_ptr<TestProtoDatabaseProvider> CreateProviderWithSharedDB() {
-    return std::make_unique<TestProtoDatabaseProvider>(
-        shared_db_temp_dir_->GetPath(), shared_db_);
-  }
-
-  std::unique_ptr<TestSharedProtoDatabaseProvider> CreateSharedProvider(
-      TestProtoDatabaseProvider* db_provider) {
-    return std::make_unique<TestSharedProtoDatabaseProvider>(
-        GetTestThreadTaskRunner(), db_provider->weak_factory_.GetWeakPtr());
-  }
-
-  // Uses ProtoDatabaseWrapper's 3 parameter Init to bypass the check that gets
-  // |use_shared_db|'s value.
-  void InitWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
-                   const std::string& client_name,
-                   bool use_shared_db,
-                   Callbacks::InitStatusCallback callback) {
-    wrapper->Init(client_name, use_shared_db, std::move(callback));
-  }
-
-  void InitWrapperAndWait(ProtoDatabaseWrapper<TestProto>* wrapper,
-                          const std::string& client_name,
-                          bool use_shared_db,
-                          Enums::InitStatus expect_status) {
-    base::RunLoop init_loop;
-    InitWrapper(
-        wrapper, client_name, use_shared_db,
-        base::BindOnce(
-            [](base::OnceClosure closure, Enums::InitStatus expect_status,
-               Enums::InitStatus status) {
-              ASSERT_EQ(status, expect_status);
-              std::move(closure).Run();
-            },
-            init_loop.QuitClosure(), expect_status));
-    init_loop.Run();
-  }
-
-  // Just uses each entry's key to fill out the id/data fields in TestProto as
-  // well.
-  void AddDataToWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
-                        std::vector<std::string>* entry_keys) {
-    auto data_set =
-        std::make_unique<std::vector<std::pair<std::string, TestProto>>>();
-    for (const auto& key : *entry_keys) {
-      TestProto proto;
-      proto.set_id(key);
-      proto.set_data(key);
-      data_set->emplace_back(std::make_pair(key, proto));
-    }
-
-    base::RunLoop data_loop;
-    wrapper->UpdateEntries(std::move(data_set),
-                           std::make_unique<std::vector<std::string>>(),
-                           base::BindOnce(
-                               [](base::OnceClosure closure, bool success) {
-                                 ASSERT_TRUE(success);
-                                 std::move(closure).Run();
-                               },
-                               data_loop.QuitClosure()));
-    data_loop.Run();
-  }
-
-  void VerifyDataInWrapper(ProtoDatabaseWrapper<TestProto>* wrapper,
-                           std::vector<std::string>* entry_keys) {
-    base::RunLoop load_loop;
-    wrapper->LoadKeysAndEntries(base::BindOnce(
-        [](base::OnceClosure closure, std::vector<std::string>* entry_keys,
-           bool success,
-           std::unique_ptr<std::map<std::string, TestProto>> keys_entries) {
-          ASSERT_TRUE(success);
-          ASSERT_EQ(entry_keys->size(), keys_entries->size());
-
-          for (const auto& key : *entry_keys) {
-            ASSERT_TRUE(keys_entries->find(key) != keys_entries->end());
-          }
-          std::move(closure).Run();
-        },
-        load_loop.QuitClosure(), entry_keys));
-    load_loop.Run();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> GetTestThreadTaskRunner() {
-    return test_thread_->task_runner();
-  }
-
-  base::FilePath temp_dir() { return temp_dir_->GetPath(); }
-
- private:
-  std::unique_ptr<base::ScopedTempDir> temp_dir_;
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
-  // Shared database.
-  std::unique_ptr<base::Thread> test_thread_;
-  std::unique_ptr<base::Thread> shared_db_thread_;
-  scoped_refptr<SharedProtoDatabase> shared_db_;
-  std::unique_ptr<base::ScopedTempDir> shared_db_temp_dir_;
-};
-
-TEST_F(ProtoDatabaseWrapperTest, FailsBothDatabases) {
-  auto db_provider = CreateProviderNoSharedDB();
-  auto shared_db_provider = CreateSharedProvider(db_provider.get());
-  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                               temp_dir(), GetTestThreadTaskRunner(),
-                               CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kError);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_DontUseShared_NoSharedDB) {
-  auto db_provider = CreateProviderNoSharedDB();
-  auto shared_db_provider = CreateSharedProvider(db_provider.get());
-  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                               temp_dir(), GetTestThreadTaskRunner(),
-                               CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, Fails_UseShared_NoSharedDB_NoUniqueDB) {
-  auto db_provider = CreateProviderNoSharedDB();
-  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                               temp_dir(), GetTestThreadTaskRunner(),
-                               CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kError);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_UseShared_NoSharedDB) {
-  // First we create a unique DB so our second pass has a unique DB available.
-  auto db_provider = CreateProviderNoSharedDB();
-  auto unique_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                                      temp_dir(), GetTestThreadTaskRunner(),
-                                      CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-  // Kill the wrapper so it doesn't have a lock on the DB anymore.
-  unique_wrapper.reset();
-
-  auto shared_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                                      temp_dir(), GetTestThreadTaskRunner(),
-                                      CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, SucceedsWithShared_UseShared_HasSharedDB) {
-  auto db_provider = CreateProviderWithSharedDB();
-  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                               temp_dir(), GetTestThreadTaskRunner(),
-                               CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, SucceedsWithUnique_DontUseShared_HasSharedDB) {
-  auto db_provider = CreateProviderWithSharedDB();
-  auto wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                               temp_dir(), GetTestThreadTaskRunner(),
-                               CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-}
-
-// Migration tests:
-TEST_F(ProtoDatabaseWrapperTest, Migration_EmptyDBs_UniqueToShared) {
-  // First we create a unique DB so our second pass has a unique DB available.
-  auto db_provider_noshared = CreateProviderNoSharedDB();
-  auto unique_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_noshared.get()));
-  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-  // Kill the wrapper so it doesn't have a lock on the DB anymore.
-  unique_wrapper.reset();
-
-  auto db_provider_withshared = CreateProviderWithSharedDB();
-  auto shared_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_withshared.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, Migration_EmptyDBs_SharedToUnique) {
-  // First we create a unique DB so our second pass has a unique DB available.
-  auto db_provider = CreateProviderWithSharedDB();
-  auto shared_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                                      temp_dir(), GetTestThreadTaskRunner(),
-                                      CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-
-  auto unique_wrapper = CreateWrapper(kDefaultNamespace, kDefaultTypePrefix,
-                                      temp_dir(), GetTestThreadTaskRunner(),
-                                      CreateSharedProvider(db_provider.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-}
-
-TEST_F(ProtoDatabaseWrapperTest, Migration_UniqueToShared) {
-  auto data_set = std::make_unique<std::vector<std::string>>();
-  data_set->emplace_back("entry1");
-  data_set->emplace_back("entry2");
-  data_set->emplace_back("entry3");
-
-  // First we create a unique DB so our second pass has a unique DB available.
-  auto db_provider_noshared = CreateProviderNoSharedDB();
-  auto unique_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_noshared.get()));
-  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-  AddDataToWrapper(unique_wrapper.get(), data_set.get());
-  // Kill the wrapper so it doesn't have a lock on the DB anymore.
-  unique_wrapper.reset();
-
-  auto db_provider_withshared = CreateProviderWithSharedDB();
-  auto shared_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_withshared.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-  VerifyDataInWrapper(shared_wrapper.get(), data_set.get());
-}
-
-TEST_F(ProtoDatabaseWrapperTest, Migration_SharedToUnique) {
-  auto data_set = std::make_unique<std::vector<std::string>>();
-  data_set->emplace_back("entry1");
-  data_set->emplace_back("entry2");
-  data_set->emplace_back("entry3");
-
-  // First we create a shared DB so our second pass has a shared DB available.
-  auto db_provider_withshared = CreateProviderWithSharedDB();
-  auto shared_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_withshared.get()));
-  InitWrapperAndWait(shared_wrapper.get(), kDefaultClientName, true,
-                     Enums::InitStatus::kOK);
-  AddDataToWrapper(shared_wrapper.get(), data_set.get());
-
-  auto unique_wrapper =
-      CreateWrapper(kDefaultNamespace, kDefaultTypePrefix, temp_dir(),
-                    GetTestThreadTaskRunner(),
-                    CreateSharedProvider(db_provider_withshared.get()));
-  InitWrapperAndWait(unique_wrapper.get(), kDefaultClientName, false,
-                     Enums::InitStatus::kOK);
-  VerifyDataInWrapper(unique_wrapper.get(), data_set.get());
-}
-
-}  // namespace leveldb_proto
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_database.cc b/components/leveldb_proto/public/proto_database.cc
similarity index 95%
rename from components/leveldb_proto/proto_database.cc
rename to components/leveldb_proto/public/proto_database.cc
index ad4f104..4bb325b 100644
--- a/components/leveldb_proto/proto_database.cc
+++ b/components/leveldb_proto/public/proto_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/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 #include "base/system/sys_info.h"
 
diff --git a/components/leveldb_proto/proto_database.h b/components/leveldb_proto/public/proto_database.h
similarity index 96%
rename from components/leveldb_proto/proto_database.h
rename to components/leveldb_proto/public/proto_database.h
index 7fda2d7b..6a00f332 100644
--- a/components/leveldb_proto/proto_database.h
+++ b/components/leveldb_proto/public/proto_database.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_H_
+#define COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_H_
 
 #include <map>
 #include <vector>
 
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_checker.h"
-#include "components/leveldb_proto/leveldb_database.h"
+#include "components/leveldb_proto/internal/leveldb_database.h"
 
 namespace leveldb_proto {
 
@@ -182,4 +182,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/proto_database_provider.cc b/components/leveldb_proto/public/proto_database_provider.cc
similarity index 92%
rename from components/leveldb_proto/proto_database_provider.cc
rename to components/leveldb_proto/public/proto_database_provider.cc
index 2ea633f..7b00dda 100644
--- a/components/leveldb_proto/proto_database_provider.cc
+++ b/components/leveldb_proto/public/proto_database_provider.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 
 #include "base/files/file_path.h"
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/leveldb_proto/shared_proto_database.h"
+#include "components/leveldb_proto/internal/shared_proto_database.h"
 
 namespace leveldb_proto {
 
diff --git a/components/leveldb_proto/proto_database_provider.h b/components/leveldb_proto/public/proto_database_provider.h
similarity index 83%
rename from components/leveldb_proto/proto_database_provider.h
rename to components/leveldb_proto/public/proto_database_provider.h
index 90d036d8..fa3e1352 100644
--- a/components/leveldb_proto/proto_database_provider.h
+++ b/components/leveldb_proto/public/proto_database_provider.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_H_
-#define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_PROVIDER_H_
+#define COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_PROVIDER_H_
 
 #include "base/files/file_path.h"
 #include "base/sequenced_task_runner.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_database_wrapper.h"
-#include "components/leveldb_proto/shared_proto_database_provider.h"
+#include "components/leveldb_proto/internal/proto_database_wrapper.h"
+#include "components/leveldb_proto/internal/shared_proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 
@@ -25,6 +25,12 @@
 
   static ProtoDatabaseProvider* Create(const base::FilePath& profile_dir);
 
+  template <typename T>
+  static std::unique_ptr<ProtoDatabase<T>> CreateUniqueDB(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+    return std::make_unique<UniqueProtoDatabase<T>>(task_runner);
+  }
+
   // |client_namespace| is the unique prefix to be used in the shared database
   // if the database returned is a SharedDatabaseClient<T>. This name must be
   // present in |kCurrentSharedProtoDatabaseClients|. |type_prefix| is a unique
@@ -84,4 +90,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_H_
+#endif  // COMPONENTS_LEVELDB_PROTO_PUBLIC_PROTO_DATABASE_PROVIDER_H_
diff --git a/components/leveldb_proto/shared_proto_database_client_list.cc b/components/leveldb_proto/public/shared_proto_database_client_list.cc
similarity index 86%
rename from components/leveldb_proto/shared_proto_database_client_list.cc
rename to components/leveldb_proto/public/shared_proto_database_client_list.cc
index 9019b6a15..e5ff035 100644
--- a/components/leveldb_proto/shared_proto_database_client_list.cc
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.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/leveldb_proto/shared_proto_database_client_list.h"
+#include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 
 #include <stddef.h>
 
diff --git a/components/leveldb_proto/shared_proto_database_client_list.h b/components/leveldb_proto/public/shared_proto_database_client_list.h
similarity index 81%
rename from components/leveldb_proto/shared_proto_database_client_list.h
rename to components/leveldb_proto/public/shared_proto_database_client_list.h
index 59ef09e..5fefa7e7 100644
--- a/components/leveldb_proto/shared_proto_database_client_list.h
+++ b/components/leveldb_proto/public/shared_proto_database_client_list.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_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
-#define COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
+#ifndef COMPONENTS_LEVELDB_PROTO_PUBLIC_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
+#define COMPONENTS_LEVELDB_PROTO_PUBLIC_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
 
 #include <stddef.h>
 
@@ -32,4 +32,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
\ No newline at end of file
+#endif  // COMPONENTS_LEVELDB_PROTO_PUBLIC_SHARED_PROTO_DATABASE_CLIENT_LIST_H_
\ No newline at end of file
diff --git a/components/leveldb_proto/testing/fake_db.h b/components/leveldb_proto/testing/fake_db.h
index 740f9f6..474d7f7 100644
--- a/components/leveldb_proto/testing/fake_db.h
+++ b/components/leveldb_proto/testing/fake_db.h
@@ -16,8 +16,8 @@
 #include "base/files/file_path.h"
 #include "base/task/post_task.h"
 #include "base/test/test_simple_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/unique_proto_database.h"
+#include "components/leveldb_proto/internal/unique_proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace leveldb_proto {
 namespace test {
diff --git a/components/leveldb_proto/testing/proto/test_db.proto b/components/leveldb_proto/testing/proto/test_db.proto
index 8498105..9355907e 100644
--- a/components/leveldb_proto/testing/proto/test_db.proto
+++ b/components/leveldb_proto/testing/proto/test_db.proto
@@ -8,7 +8,7 @@
 
 package leveldb_proto;
 
-// A proto to test leveldb_proto::ProtoDatabaseImpl.
+// A proto to test leveldb_proto::ProtoDatabase implementations.
 //
 // Next tag: 3
 message TestProto {
diff --git a/components/link_header_util/OWNERS b/components/link_header_util/OWNERS
index 6449a0b..d5c009c 100644
--- a/components/link_header_util/OWNERS
+++ b/components/link_header_util/OWNERS
@@ -1,5 +1,5 @@
 mek@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
 
 # TEAM: loading-dev@chromium.org
 # COMPONENT: Blink>Loader
diff --git a/components/network_hints/browser/BUILD.gn b/components/network_hints/browser/BUILD.gn
index 351a31c..c2929f4 100644
--- a/components/network_hints/browser/BUILD.gn
+++ b/components/network_hints/browser/BUILD.gn
@@ -15,6 +15,7 @@
     "//ipc",
     "//mojo/public/cpp/bindings",
     "//net",
+    "//services/network/public/cpp",
     "//services/network/public/mojom",
   ]
 }
diff --git a/components/network_hints/browser/DEPS b/components/network_hints/browser/DEPS
index 080d177..6db74759 100644
--- a/components/network_hints/browser/DEPS
+++ b/components/network_hints/browser/DEPS
@@ -3,5 +3,6 @@
   "+ipc",
   "+mojo/public/cpp/bindings",
   "+net",
+  "+services/network/public/cpp",
   "+services/network/public/mojom",
 ]
diff --git a/components/network_hints/browser/network_hints_message_filter.cc b/components/network_hints/browser/network_hints_message_filter.cc
index dcdb51a..299a7f47 100644
--- a/components/network_hints/browser/network_hints_message_filter.cc
+++ b/components/network_hints/browser/network_hints_message_filter.cc
@@ -19,6 +19,7 @@
 #include "net/base/address_list.h"
 #include "net/base/net_errors.h"
 #include "net/log/net_log_with_source.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "url/gurl.h"
@@ -32,7 +33,7 @@
 // This class contains a std::unique_ptr of itself, it is passed in through
 // Start() method, and will be freed by the OnComplete() method when resolving
 // has completed or mojo connection error has happened.
-class DnsLookupRequest : public network::mojom::ResolveHostClient {
+class DnsLookupRequest : public network::ResolveHostClientBase {
  public:
   DnsLookupRequest(int render_process_id, const std::string& hostname)
       : binding_(this),
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc b/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
index d3ec767..59ac52b 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
@@ -24,7 +24,9 @@
 #include "components/variations/net/variations_http_headers.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
@@ -250,16 +252,7 @@
           "A policy will be added before enabling for enterprise users."
         })");
 
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-
-  resource_request->url = GURL(GetFetchEndpoint());
-
-  int cookie_flag = include_cookies_ ? 0 : net::LOAD_DO_NOT_SEND_COOKIES;
-  resource_request->load_flags = net::LOAD_BYPASS_CACHE |
-                                 net::LOAD_DO_NOT_SAVE_COOKIES | cookie_flag |
-                                 net::LOAD_DO_NOT_SEND_AUTH_DATA;
-  resource_request->headers = MakeHeaders();
-  resource_request->method = "GET";
+  auto resource_request = MakeResourceRequest();
 
   auto simple_loader = network::SimpleURLLoader::Create(
       std::move(resource_request), traffic_annotation);
@@ -267,7 +260,29 @@
   return simple_loader;
 }
 
-net::HttpRequestHeaders ContextualSuggestionsFetch::MakeHeaders() const {
+std::unique_ptr<network::ResourceRequest>
+ContextualSuggestionsFetch::MakeResourceRequestForTesting() const {
+  return MakeResourceRequest();
+}
+
+std::unique_ptr<network::ResourceRequest>
+ContextualSuggestionsFetch::MakeResourceRequest() const {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+
+  resource_request->url = GURL(GetFetchEndpoint());
+  resource_request->method = "GET";
+  AppendHeaders(resource_request.get());
+
+  int cookie_flag = include_cookies_ ? 0 : net::LOAD_DO_NOT_SEND_COOKIES;
+  resource_request->load_flags = net::LOAD_BYPASS_CACHE |
+                                 net::LOAD_DO_NOT_SAVE_COOKIES | cookie_flag |
+                                 net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+  return resource_request;
+}
+
+void ContextualSuggestionsFetch::AppendHeaders(
+    network::ResourceRequest* resource_request) const {
   net::HttpRequestHeaders headers;
   std::string serialized_request_body =
       SerializedPivotsRequest(url_.spec(), bcp_language_code_);
@@ -276,14 +291,15 @@
                         base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                         &base64_encoded_body);
   headers.SetHeader("X-Protobuffer-Request-Payload", base64_encoded_body);
-  variations::AppendVariationHeaders(url_, variations::InIncognito::kNo,
+  variations::AppendVariationHeaders(resource_request->url,
+                                     variations::InIncognito::kNo,
                                      variations::SignedIn::kNo, &headers);
 
   UMA_HISTOGRAM_COUNTS_1M(
       "ContextualSuggestions.FetchRequestProtoSizeKB",
       static_cast<int>(base64_encoded_body.length() / 1024));
 
-  return headers;
+  resource_request->headers = headers;
 }
 
 void ContextualSuggestionsFetch::OnURLLoaderComplete(
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_fetch.h b/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
index 96af184..dd8845f 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
+++ b/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
@@ -13,10 +13,10 @@
 #include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
 #include "components/ntp_snippets/contextual/reporting/contextual_suggestions_metrics_reporter.h"
 #include "net/base/load_flags.h"
-#include "net/http/http_request_headers.h"
 #include "url/gurl.h"
 
 namespace network {
+struct ResourceRequest;
 class SimpleURLLoader;
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -43,9 +43,14 @@
       ReportFetchMetricsCallback metrics_callback,
       const scoped_refptr<network::SharedURLLoaderFactory>& loader_factory);
 
+  // Methods for testing.
+  std::unique_ptr<network::ResourceRequest> MakeResourceRequestForTesting()
+      const;
+
  private:
   std::unique_ptr<network::SimpleURLLoader> MakeURLLoader() const;
-  net::HttpRequestHeaders MakeHeaders() const;
+  std::unique_ptr<network::ResourceRequest> MakeResourceRequest() const;
+  void AppendHeaders(network::ResourceRequest*) const;
   void OnURLLoaderComplete(ReportFetchMetricsCallback metrics_callback,
                            std::unique_ptr<std::string> result);
   void ReportFetchMetrics(size_t clusters_size,
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc b/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
index e5c032e8..6b933abd 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
@@ -9,7 +9,11 @@
 
 #include "base/command_line.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_features.h"
+#include "components/variations/variations_http_header_provider.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace contextual_suggestions {
@@ -81,6 +85,40 @@
             ContextualSuggestionsFetch::GetFetchEndpoint());
 }
 
+TEST(ContextualSuggestionsFetch, MakeResourceRequest_VariationsHeader) {
+  scoped_refptr<base::TestSimpleTaskRunner> test_task_runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle handle(test_task_runner);
+  EXPECT_EQ(variations::VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS,
+            variations::VariationsHttpHeaderProvider::GetInstance()
+                ->ForceVariationIds({"12345"}, ""));
+
+  ContextualSuggestionsFetch fetch(GURL("http://test.com"), "en-US", false);
+  std::unique_ptr<network::ResourceRequest> resource_request =
+      fetch.MakeResourceRequestForTesting();
+
+  EXPECT_TRUE(resource_request->headers.HasHeader("X-Client-Data"));
+}
+
+TEST(ContextualSuggestionsFetch,
+     MakeResourceRequest_VariationsHeader_NonGoogleEndpoint) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitchASCII("contextual-suggestions-fetch-endpoint",
+                                  "https://nongoogleendpoint.com");
+  scoped_refptr<base::TestSimpleTaskRunner> test_task_runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle handle(test_task_runner);
+  EXPECT_EQ(variations::VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS,
+            variations::VariationsHttpHeaderProvider::GetInstance()
+                ->ForceVariationIds({"12345"}, ""));
+
+  ContextualSuggestionsFetch fetch(GURL("http://test.com"), "en-US", false);
+  std::unique_ptr<network::ResourceRequest> resource_request =
+      fetch.MakeResourceRequestForTesting();
+
+  EXPECT_FALSE(resource_request->headers.HasHeader("X-Client-Data"));
+}
+
 }  // namespace
 
 }  // namespace contextual_suggestions
diff --git a/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc b/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
index 7d330a8..ea87c03 100644
--- a/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
+++ b/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
@@ -99,7 +99,7 @@
     cached_image_fetcher_.reset();
     database_.reset();
     // We need to run until idle after deleting the database, because
-    // ProtoDatabaseImpl deletes the actual LevelDB asynchronously.
+    // ProtoDatabase deletes the actual LevelDB asynchronously.
     RunUntilIdle();
   }
 
diff --git a/components/ntp_snippets/remote/remote_suggestions_database.cc b/components/ntp_snippets/remote/remote_suggestions_database.cc
index c159fa5..780b544 100644
--- a/components/ntp_snippets/remote/remote_suggestions_database.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_database.cc
@@ -9,11 +9,11 @@
 #include "base/files/file_path.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
 
 using leveldb_proto::ProtoDatabase;
-using leveldb_proto::ProtoDatabaseImpl;
+using leveldb_proto::ProtoDatabaseProvider;
 
 namespace {
 // Statistics are logged to UMA with this string as part of histogram name. They
@@ -43,8 +43,8 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     const base::FilePath& database_dir)
     : RemoteSuggestionsDatabase(
-          std::make_unique<ProtoDatabaseImpl<SnippetProto>>(task_runner),
-          std::make_unique<ProtoDatabaseImpl<SnippetImageProto>>(task_runner),
+          ProtoDatabaseProvider::CreateUniqueDB<SnippetProto>(task_runner),
+          ProtoDatabaseProvider::CreateUniqueDB<SnippetImageProto>(task_runner),
           database_dir) {}
 
 RemoteSuggestionsDatabase::RemoteSuggestionsDatabase(
diff --git a/components/ntp_snippets/remote/remote_suggestions_database.h b/components/ntp_snippets/remote/remote_suggestions_database.h
index c957dde..6945a98 100644
--- a/components/ntp_snippets/remote/remote_suggestions_database.h
+++ b/components/ntp_snippets/remote/remote_suggestions_database.h
@@ -16,7 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "components/ntp_snippets/remote/remote_suggestion.h"
 
 namespace base {
@@ -35,7 +35,7 @@
       base::OnceCallback<void(RemoteSuggestion::PtrVector)>;
   using SnippetImageCallback = base::OnceCallback<void(std::string)>;
 
-  // Creates a RemoteSuggestionsDatabase backed by real ProtoDatabaseImpls.
+  // Creates a RemoteSuggestionsDatabase backed by real ProtoDatabases.
   RemoteSuggestionsDatabase(const base::FilePath& database_dir);
   // Creates a RemoteSuggestionsDatabase backed by the passed-in ProtoDatabases,
   // useful for testing.
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index d5d0365e9..94549ac8 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -330,7 +330,7 @@
 
   ~RemoteSuggestionsProviderImplTest() override {
     // We need to run until idle after deleting the database, because
-    // ProtoDatabaseImpl deletes the actual LevelDB asynchronously on the task
+    // ProtoDatabase deletes the actual LevelDB asynchronously on the task
     // runner. Without this, we'd get reports of memory leaks.
     RunUntilIdle();
   }
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 855d589..7d18e55 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -380,6 +380,7 @@
     "omnibox_controller_unittest.cc",
     "omnibox_edit_model_unittest.cc",
     "omnibox_field_trial_unittest.cc",
+    "omnibox_pedal_implementations_unittest.cc",
     "omnibox_pedal_provider_unittest.cc",
     "omnibox_pedal_unittest.cc",
     "omnibox_popup_model_unittest.cc",
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index bb7dc87..36ab1f4 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -185,7 +185,6 @@
 // Whether this autocomplete match type supports custom descriptions.
 bool AutocompleteMatchHasCustomDescription(const AutocompleteMatch& match) {
   if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_DESKTOP &&
-      OmniboxFieldTrial::IsNewAnswerLayoutEnabled() &&
       match.type == AutocompleteMatchType::CALCULATOR) {
     return true;
   }
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index c0a17d1..b568aa3 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -292,23 +292,29 @@
   has_temporary_text_ = false;
 }
 
-void OmniboxEditModel::Unelide(bool exit_query_in_omnibox) {
+bool OmniboxEditModel::Unelide(bool exit_query_in_omnibox) {
   // Unelision should not occur if the user has already inputted text.
   if (user_input_in_progress())
-    return;
+    return false;
+
+  // No need to unelide if we are already displaying the full URL.
+  LocationBarModel* location_bar_model = controller()->GetLocationBarModel();
+  if (view_->GetText() == location_bar_model->GetFormattedFullURL())
+    return false;
 
   // Early exit if we don't want to exit Query in Omnibox mode, and the omnibox
   // is displaying a query.
-  LocationBarModel* location_bar_model = controller()->GetLocationBarModel();
   if (!exit_query_in_omnibox &&
       location_bar_model->GetDisplaySearchTerms(nullptr))
-    return;
+    return false;
 
   SetUserText(url_for_editing_);
   view_->SetWindowTextAndCaretPos(url_for_editing_, 0, false, false);
 
   // Select all in reverse to ensure the beginning of the URL is shown.
   view_->SelectAll(true /* reversed */);
+
+  return true;
 }
 
 void OmniboxEditModel::OnChanged() {
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index d01e927..9bfda32 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -172,11 +172,13 @@
   // Sets the user_text_ to |text|.
   void SetUserText(const base::string16& text);
 
-  // Unapplies any Steady State Elisions by setting the user text to be
-  // url_for_editing_. This also selects all and enters user-input-in-progress
-  // mode. If |exit_query_in_omnibox| is set to true, this will alse exit
-  // Query in Omnibox mode if the omnibox is showing a query.
-  void Unelide(bool exit_query_in_omnibox);
+  // If the omnibox is currently displaying elided text, this method will
+  // restore the full URL into the user text. After unelision, this selects-all,
+  // enters user-input-in-progress mode, and then returns true.
+  //
+  // If the omnibox is not currently displaying elided text, this method will
+  // no-op and return false.
+  bool Unelide(bool exit_query_in_omnibox);
 
   // Invoked any time the text may have changed in the edit. Notifies the
   // controller.
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index 1c5dc28..d5d5235 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -285,25 +285,29 @@
   location_bar_model()->set_url(GURL("https://www.example.com/"));
   location_bar_model()->set_url_for_display(base::ASCIIToUTF16("example.com"));
 
-  // Verify we show the display text when there is no Query in Omnibox match.
-  model()->ResetDisplayTexts();
-#if defined(OS_IOS)
-  // iOS OmniboxEditModel always provides the full URL as the OmniboxView
-  // permanent display text.
-  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"),
-            model()->GetPermanentDisplayText());
-#else
-  EXPECT_EQ(base::ASCIIToUTF16("example.com"),
-            model()->GetPermanentDisplayText());
-#endif
+  EXPECT_TRUE(model()->ResetDisplayTexts());
+  model()->Revert();
 
   EXPECT_TRUE(model()->CurrentTextIsURL());
 
+#if defined(OS_IOS)
+  // iOS OmniboxEditModel always provides the full URL as the OmniboxView
+  // permanent display text. Unelision should return false.
+  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"),
+            model()->GetPermanentDisplayText());
+  EXPECT_FALSE(model()->Unelide(false /* exit_query_in_omnibox */));
+  EXPECT_FALSE(model()->user_input_in_progress());
+  EXPECT_FALSE(view()->IsSelectAll());
+#else
   // Verify we can unelide and show the full URL properly.
-  model()->Unelide(false /* exit_query_in_omnibox */);
-  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"), view()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("example.com"),
+            model()->GetPermanentDisplayText());
+  EXPECT_TRUE(model()->Unelide(false /* exit_query_in_omnibox */));
   EXPECT_TRUE(model()->user_input_in_progress());
   EXPECT_TRUE(view()->IsSelectAll());
+#endif
+
+  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"), view()->GetText());
   EXPECT_TRUE(model()->CurrentTextIsURL());
 
   // We should still show the current page's icon until the URL is modified.
@@ -316,16 +320,17 @@
   location_bar_model()->set_url(GURL("https://www.example.com/"));
   location_bar_model()->set_url_for_display(base::ASCIIToUTF16("example.com"));
   location_bar_model()->set_display_search_terms(base::ASCIIToUTF16("foobar"));
-  EXPECT_TRUE(model()->ResetDisplayTexts());
 
+  EXPECT_TRUE(model()->ResetDisplayTexts());
   model()->Revert();
+
   EXPECT_EQ(base::ASCIIToUTF16("foobar"), model()->GetPermanentDisplayText());
   EXPECT_EQ(base::ASCIIToUTF16("foobar"), view()->GetText());
   EXPECT_FALSE(model()->CurrentTextIsURL());
   EXPECT_TRUE(model()->ShouldShowCurrentPageIcon());
 
   // Verify we can exit Query in Omnibox mode properly.
-  model()->Unelide(true /* exit_query_in_omnibox */);
+  EXPECT_TRUE(model()->Unelide(true /* exit_query_in_omnibox */));
   EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"), view()->GetText());
   EXPECT_TRUE(model()->user_input_in_progress());
   EXPECT_TRUE(view()->IsSelectAll());
@@ -337,6 +342,27 @@
   EXPECT_FALSE(model()->ShouldShowCurrentPageIcon());
 }
 
+TEST_F(OmniboxEditModelTest, UnelideDoesNothingWhenFullURLAlreadyShown) {
+  location_bar_model()->set_url(GURL("https://www.example.com/"));
+  location_bar_model()->set_url_for_display(
+      base::ASCIIToUTF16("https://www.example.com/"));
+
+  EXPECT_TRUE(model()->ResetDisplayTexts());
+  model()->Revert();
+
+  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"),
+            model()->GetPermanentDisplayText());
+  EXPECT_TRUE(model()->CurrentTextIsURL());
+
+  // Verify Unelide does nothing.
+  EXPECT_FALSE(model()->Unelide(false /* exit_query_in_omnibox */));
+  EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"), view()->GetText());
+  EXPECT_FALSE(model()->user_input_in_progress());
+  EXPECT_FALSE(view()->IsSelectAll());
+  EXPECT_TRUE(model()->CurrentTextIsURL());
+  EXPECT_TRUE(model()->ShouldShowCurrentPageIcon());
+}
+
 TEST_F(OmniboxEditModelTest, DisablePasteAndGoForLongTexts) {
   EXPECT_TRUE(model()->OmniboxEditModel::CanPasteAndGo(
       base::ASCIIToUTF16("short text")));
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 0a5931c..9b295c0 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -774,11 +774,6 @@
   return base::FeatureList::IsEnabled(omnibox::kOmniboxRichEntitySuggestions);
 }
 
-bool OmniboxFieldTrial::IsNewAnswerLayoutEnabled() {
-  // TODO(orinj): The call is now obsolete and can be removed.
-  return true;
-}
-
 bool OmniboxFieldTrial::IsReverseAnswersEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kOmniboxReverseAnswers) ||
          base::FeatureList::IsEnabled(features::kExperimentalUi);
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index aeacce88..967a760 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -432,10 +432,6 @@
   // Returns true if the rich entities flag is enabled.
   static bool IsRichEntitySuggestionsEnabled();
 
-  // Returns true if either the new answer layout flag or the
-  // #upcoming-ui-features flag is enabled.
-  static bool IsNewAnswerLayoutEnabled();
-
   // Returns true if either the reverse answers flag or the
   // #upcoming-ui-features flag is enabled.
   static bool IsReverseAnswersEnabled();
diff --git a/components/omnibox/browser/omnibox_pedal.cc b/components/omnibox/browser/omnibox_pedal.cc
index 176eac4..a12c2bd7 100644
--- a/components/omnibox/browser/omnibox_pedal.cc
+++ b/components/omnibox/browser/omnibox_pedal.cc
@@ -4,6 +4,8 @@
 
 #include "components/omnibox/browser/omnibox_pedal.h"
 
+#include <cctype>
+
 #include "base/strings/utf_string_conversions.h"
 #include "components/omnibox/browser/omnibox_client.h"
 #include "components/omnibox/browser/omnibox_edit_controller.h"
@@ -21,8 +23,61 @@
       hint_short(l10n_util::GetStringUTF16(id_hint_short)),
       suggestion_contents(l10n_util::GetStringUTF16(id_suggestion_contents)) {}
 
-OmniboxPedal::OmniboxPedal(OmniboxPedal::LabelStrings strings)
-    : strings_(strings) {}
+// =============================================================================
+
+OmniboxPedal::SynonymGroup::SynonymGroup(
+    bool required,
+    std::initializer_list<const char*> synonyms)
+    : required_(required) {
+  // The DCHECK logic below is quickly ensuring that the synonyms are provided
+  // in descending order of string length.
+#if DCHECK_IS_ON()
+  size_t min_size = std::numeric_limits<std::size_t>::max();
+#endif
+  synonyms_.reserve(synonyms.size());
+  for (const char* synonym : synonyms) {
+    synonyms_.push_back(base::ASCIIToUTF16(synonym));
+#if DCHECK_IS_ON()
+    size_t size = synonyms_.back().size();
+    DCHECK_LE(size, min_size);
+    min_size = size;
+#endif
+  }
+}
+
+OmniboxPedal::SynonymGroup::SynonymGroup(const SynonymGroup& other) = default;
+
+OmniboxPedal::SynonymGroup::~SynonymGroup() = default;
+
+bool OmniboxPedal::SynonymGroup::EraseFirstMatchIn(
+    base::string16& remaining) const {
+  for (const auto& synonym : synonyms_) {
+    const size_t pos = remaining.find(synonym);
+    if (pos != base::string16::npos) {
+      remaining.erase(pos, synonym.size());
+      return true;
+    }
+  }
+  return !required_;
+}
+
+// =============================================================================
+
+OmniboxPedal::OmniboxPedal(
+    LabelStrings strings,
+    GURL url,
+    std::initializer_list<const char*> triggers,
+    std::initializer_list<const SynonymGroup> synonym_groups)
+    : strings_(strings), url_(url) {
+  triggers_.reserve(triggers.size());
+  for (const char* trigger : triggers) {
+    triggers_.insert(base::ASCIIToUTF16(trigger));
+  }
+  synonym_groups_.reserve(synonym_groups.size());
+  for (const SynonymGroup& group : synonym_groups) {
+    synonym_groups_.push_back(group);
+  }
+}
 
 OmniboxPedal::~OmniboxPedal() {}
 
@@ -67,7 +122,20 @@
 #endif
 
 bool OmniboxPedal::IsTriggerMatch(const base::string16& match_text) const {
-  return triggers_.find(match_text) != triggers_.end();
+  return (triggers_.find(match_text) != triggers_.end()) ||
+         IsConceptMatch(match_text);
+}
+
+bool OmniboxPedal::IsConceptMatch(const base::string16& match_text) const {
+  base::string16 remaining = match_text;
+  for (const auto& group : synonym_groups_) {
+    if (!group.EraseFirstMatchIn(remaining))
+      return false;
+  }
+  // If any non-space is remaining, it means there is something in match_text
+  // that was not covered by groups, so conservatively treat it as non-match.
+  const auto is_space = [](auto c) { return std::isspace(c); };
+  return std::all_of(remaining.begin(), remaining.end(), is_space);
 }
 
 void OmniboxPedal::OpenURL(OmniboxPedal::ExecutionContext& context,
diff --git a/components/omnibox/browser/omnibox_pedal.h b/components/omnibox/browser/omnibox_pedal.h
index 38db74c2..578481e 100644
--- a/components/omnibox/browser/omnibox_pedal.h
+++ b/components/omnibox/browser/omnibox_pedal.h
@@ -7,6 +7,7 @@
 
 #include <unordered_set>
 
+#include "base/gtest_prod_util.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -39,6 +40,37 @@
     const base::string16 suggestion_contents;
   };
 
+  class SynonymGroup {
+   public:
+    // Note: synonyms must be specified in decreasing order by string length
+    // so that longest matches will be detected first.  For example,
+    // "incognito window" must come before "incognito" so that the " window"
+    // part will also be covered by this group -- otherwise it would be left
+    // intact and wrongly treated as uncovered by the checking algorithm.
+    // See OmniboxPedal::IsConceptMatch for the logic that necessitates order.
+    SynonymGroup(bool required, std::initializer_list<const char*> synonyms);
+    SynonymGroup(const SynonymGroup& other);
+    ~SynonymGroup();
+
+    // Removes first matching synonym from given |remaining| string if any are
+    // found.  Returns true if checking may continue; false if no further
+    // checking is required because what remains cannot be a concept match.
+    bool EraseFirstMatchIn(base::string16& remaining) const;
+
+   protected:
+    // If this is true, a synonym of the group must be present for triggering.
+    // If false, then presence is simply allowed and does not inhibit triggering
+    // (any text not covered by groups would stop trigger).
+    bool required_;
+
+    // The set of interchangeable alternative representations for this group:
+    // when trying to clear browsing data, a user may think of 'erase', 'clear',
+    // 'delete', etc.  Even though these are not strictly synonymous in natural
+    // language, they are considered equivalent within the context of intention
+    // to perform this Pedal's action.
+    std::vector<base::string16> synonyms_;
+  };
+
   // ExecutionContext provides the necessary structure for Pedal
   // execution implementations that potentially vary widely, and
   // references are preferred over pointers for members that are
@@ -61,7 +93,11 @@
     base::TimeTicks match_selection_timestamp_;
   };
 
-  OmniboxPedal(LabelStrings strings);
+  OmniboxPedal(
+      LabelStrings strings,
+      GURL url,
+      std::initializer_list<const char*> triggers,
+      std::initializer_list<const OmniboxPedal::SynonymGroup> synonym_groups);
   virtual ~OmniboxPedal();
 
   // Provides read access to labels associated with this Pedal.
@@ -105,9 +141,19 @@
   bool IsTriggerMatch(const base::string16& match_text) const;
 
  protected:
+  FRIEND_TEST_ALL_PREFIXES(OmniboxPedalTest, SynonymGroupErasesFirstMatchOnly);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxPedalTest, SynonymGroupsDriveConceptMatches);
+
+  // If a sufficient set of triggering synonym groups are present in match_text
+  // then it's a concept match and this returns true.  If a required group is
+  // not present, or if match_text contains extraneous text not covered by any
+  // synonym group, then it's not a concept match and this returns false.
+  bool IsConceptMatch(const base::string16& match_text) const;
+
   // Use this for the common case of navigating to a URL.
   void OpenURL(ExecutionContext& context, const GURL& url) const;
 
+  std::vector<SynonymGroup> synonym_groups_;
   std::unordered_set<base::string16> triggers_;
   LabelStrings strings_;
 
diff --git a/components/omnibox/browser/omnibox_pedal_implementations.cc b/components/omnibox/browser/omnibox_pedal_implementations.cc
index 17e00c0b..60c1e6358 100644
--- a/components/omnibox/browser/omnibox_pedal_implementations.cc
+++ b/components/omnibox/browser/omnibox_pedal_implementations.cc
@@ -16,66 +16,71 @@
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
 #endif
 
-// A small convenience wrapper for the common implementation pattern below.
-class OmniboxPedalCommon : public OmniboxPedal {
- public:
-  OmniboxPedalCommon(LabelStrings strings,
-                     GURL url,
-                     std::initializer_list<const char*> triggers)
-      : OmniboxPedal(strings) {
-    url_ = url;
-    for (const char* trigger : triggers) {
-      triggers_.insert(base::ASCIIToUTF16(trigger));
-    }
-  }
-};
-
 // =============================================================================
 
-class OmniboxPedalClearBrowsingData : public OmniboxPedalCommon {
- public:
-  OmniboxPedalClearBrowsingData()
-      : OmniboxPedalCommon(
-            LabelStrings(
-                IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT,
-                IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT_SHORT,
-                IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_SUGGESTION_CONTENTS),
-            GURL("chrome://settings/clearBrowserData"),
-            {
-                "how to clear browsing data on chrome",
-                "how to clear history",
-                "how to clear history on google chrome",
-                "how to clear history on chrome",
-                "how to clear history in google chrome",
-                "how to clear google chrome history",
-                "how to clear history google chrome",
-                "how to clear browsing history in chrome",
-                "clear browsing data",
-                "clear history",
-                "clear browsing data on chrome",
-                "clear history on google chrome",
-                "clear history google chrome",
-                "clear browsing history in chrome",
-                "clear cookies chrome",
-                "clear chrome history",
-                "clear chrome cache",
-                "history clear",
-                "history clear chrome",
-            }) {}
+OmniboxPedalClearBrowsingData::OmniboxPedalClearBrowsingData()
+    : OmniboxPedal(
+          LabelStrings(
+              IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT,
+              IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT_SHORT,
+              IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_SUGGESTION_CONTENTS),
+          GURL("chrome://settings/clearBrowserData"),
+          {
+              "how to clear browsing data on chrome",
+              "how to clear history",
+              "how to clear history on google chrome",
+              "how to clear history on chrome",
+              "how to clear history in google chrome",
+              "how to clear google chrome history",
+              "how to clear history google chrome",
+              "how to clear browsing history in chrome",
+              "clear browsing data",
+              "clear history",
+              "clear browsing data on chrome",
+              "clear history on google chrome",
+              "clear history google chrome",
+              "clear browsing history in chrome",
+              "clear cookies chrome",
+              "clear chrome history",
+              "clear chrome cache",
+              "history clear",
+              "history clear chrome",
+          },
+          {
+              SynonymGroup(false,
+                           {
+                               "google chrome",
+                               "browser",
+                               "chrome",
+                           }),
+              SynonymGroup(true,
+                           {
+                               "delete",
+                               "remove",
+                               "erase",
+                               "clear",
+                               "wipe",
+                           }),
+              SynonymGroup(true,
+                           {
+                               "history",
+                               "cache",
+                               "data",
+                           }),
+          }) {}
 
 #if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
-  const gfx::VectorIcon& GetVectorIcon() const override {
-    return omnibox::kAnswerWhenIsIcon;
-  }
+const gfx::VectorIcon& OmniboxPedalClearBrowsingData::GetVectorIcon() const {
+  return omnibox::kAnswerWhenIsIcon;
+}
 #endif
-};
 
 // =============================================================================
 
-class OmniboxPedalChangeSearchEngine : public OmniboxPedalCommon {
+class OmniboxPedalChangeSearchEngine : public OmniboxPedal {
  public:
   OmniboxPedalChangeSearchEngine()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             LabelStrings(
                 IDS_OMNIBOX_PEDAL_CHANGE_SEARCH_ENGINE_HINT,
                 IDS_OMNIBOX_PEDAL_CHANGE_SEARCH_ENGINE_HINT_SHORT,
@@ -90,61 +95,133 @@
                 "how to set google as default search engine in chrome",
                 "how to make google default search engine",
                 "how to change default search engine in google chrome",
-                "change search engine", "change google search engine",
+                "change search engine",
+                "change google search engine",
                 "change chrome searh engine",
                 "change default search engine in chrome",
-                "change search engine chrome", "change default search chrome",
-                "change search chrome", "switch chrome search engine",
+                "change search engine chrome",
+                "change default search chrome",
+                "change search chrome",
+                "switch chrome search engine",
                 "switch search engine",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "choose",
+                                 "change",
+                                 "switch",
+                                 "select",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "standard search engine",
+                                 "default search engine",
+                                 "search engine",
+                                 "search",
+                             }),
             }) {}
 };
 
 // =============================================================================
 
-class OmniboxPedalManagePasswords : public OmniboxPedalCommon {
+class OmniboxPedalManagePasswords : public OmniboxPedal {
  public:
   OmniboxPedalManagePasswords()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             LabelStrings(
                 IDS_OMNIBOX_PEDAL_MANAGE_PASSWORDS_HINT,
                 IDS_OMNIBOX_PEDAL_MANAGE_PASSWORDS_HINT_SHORT,
                 IDS_OMNIBOX_PEDAL_MANAGE_PASSWORDS_SUGGESTION_CONTENTS),
             GURL("chrome://settings/passwords"),
             {
-                "passwords", "find my passwords", "save passwords in chrome",
-                "view saved passwords", "delete passwords",
-                "find saved passwords", "where does chrome store passwords",
+                "passwords",
+                "find my passwords",
+                "save passwords in chrome",
+                "view saved passwords",
+                "delete passwords",
+                "find saved passwords",
+                "where does chrome store passwords",
                 "how to see passwords in chrome",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "manager",
+                                 "manage",
+                                 "update",
+                                 "change",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "passwords",
+                             }),
             }) {}
 };
 
 // =============================================================================
 
 // TODO(orinj): Use better scoping for existing setting, or link to new UI.
-class OmniboxPedalChangeHomePage : public OmniboxPedalCommon {
+class OmniboxPedalChangeHomePage : public OmniboxPedal {
  public:
   OmniboxPedalChangeHomePage()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             LabelStrings(
                 IDS_OMNIBOX_PEDAL_CHANGE_HOME_PAGE_HINT,
                 IDS_OMNIBOX_PEDAL_CHANGE_HOME_PAGE_HINT_SHORT,
                 IDS_OMNIBOX_PEDAL_CHANGE_HOME_PAGE_SUGGESTION_CONTENTS),
             GURL("chrome://settings/?search=show+home+button"),
             {
-                "how to change home page", "how to change your home page",
-                "how do i change my home page", "change home page google",
-                "home page chrome", "change home chrome",
-                "change chrome home page", "how to change home page on chrome",
-                "how to change home page in chrome", "change chrome home",
+                "how to change home page",
+                "how to change your home page",
+                "how do i change my home page",
+                "change home page google",
+                "home page chrome",
+                "change home chrome",
+                "change chrome home page",
+                "how to change home page on chrome",
+                "how to change home page in chrome",
+                "change chrome home",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "change",
+                                 "choose",
+                                 "set",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "home page",
+                                 "homepage",
+                             }),
             }) {}
 };
 
 // =============================================================================
 
-class OmniboxPedalUpdateCreditCard : public OmniboxPedalCommon {
+class OmniboxPedalUpdateCreditCard : public OmniboxPedal {
  public:
   OmniboxPedalUpdateCreditCard()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             OmniboxPedal::LabelStrings(
                 IDS_OMNIBOX_PEDAL_UPDATE_CREDIT_CARD_HINT,
                 IDS_OMNIBOX_PEDAL_UPDATE_CREDIT_CARD_HINT_SHORT,
@@ -155,24 +232,73 @@
                 "how to remove credit card from google chrome",
                 "remove google chrome credit cards",
                 "access google chrome credit cards",
-                "google chrome credit cards", "chrome credit cards",
-                "get to chrome credit cards", "chrome credit saved",
+                "google chrome credit cards",
+                "chrome credit cards",
+                "get to chrome credit cards",
+                "chrome credit saved",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "update",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "credit card",
+                                 "card info",
+                                 "cards",
+                             }),
             }) {}
 };
 
 // =============================================================================
 
-class OmniboxPedalLaunchIncognito : public OmniboxPedalCommon {
+class OmniboxPedalLaunchIncognito : public OmniboxPedal {
  public:
   OmniboxPedalLaunchIncognito()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             LabelStrings(
                 IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT,
                 IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT_SHORT,
                 IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUGGESTION_CONTENTS),
             GURL(),
             {
-                "what is incognito", "what's incognito mode",
+                "what is incognito",
+                "what's incognito mode",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "launch",
+                                 "start",
+                                 "enter",
+                                 "open",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "incognito window",
+                                 "incognito mode",
+                                 "private window",
+                                 "incognito tab",
+                                 "private mode",
+                                 "dark window",
+                                 "private tab",
+                                 "incognito",
+                                 "dark mode",
+                                 "dark tab",
+                             }),
             }) {}
 
   void Execute(ExecutionContext& context) const override {
@@ -182,20 +308,43 @@
 
 // =============================================================================
 
-class OmniboxPedalTranslate : public OmniboxPedalCommon {
+class OmniboxPedalTranslate : public OmniboxPedal {
  public:
   OmniboxPedalTranslate()
-      : OmniboxPedalCommon(
+      : OmniboxPedal(
             LabelStrings(IDS_OMNIBOX_PEDAL_TRANSLATE_HINT,
                          IDS_OMNIBOX_PEDAL_TRANSLATE_HINT_SHORT,
                          IDS_OMNIBOX_PEDAL_TRANSLATE_SUGGESTION_CONTENTS),
             GURL(),
             {
                 "how to change language in google chrome",
-                "change language chrome", "change chrome language",
-                "change language in chrome", "switch chrome language",
-                "translate language", "translate in chrome",
-                "translate on page", "translate language chrome",
+                "change language chrome",
+                "change chrome language",
+                "change language in chrome",
+                "switch chrome language",
+                "translate language",
+                "translate in chrome",
+                "translate on page",
+                "translate language chrome",
+            },
+            {
+                SynonymGroup(false,
+                             {
+                                 "google chrome",
+                                 "browser",
+                                 "chrome",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "change language",
+                                 "translate",
+                             }),
+                SynonymGroup(true,
+                             {
+                                 "this page",
+                                 "page",
+                                 "this",
+                             }),
             }) {}
 
   void Execute(ExecutionContext& context) const override {
@@ -205,30 +354,44 @@
 
 // =============================================================================
 
-class OmniboxPedalUpdateChrome : public OmniboxPedalCommon {
- public:
-  OmniboxPedalUpdateChrome()
-      : OmniboxPedalCommon(
-            LabelStrings(IDS_OMNIBOX_PEDAL_UPDATE_CHROME_HINT,
-                         IDS_OMNIBOX_PEDAL_UPDATE_CHROME_HINT_SHORT,
-                         IDS_OMNIBOX_PEDAL_UPDATE_CHROME_SUGGESTION_CONTENTS),
-            GURL(),
-            {
-                "how to update google chrome", "how to update chrome",
-                "how do i update google chrome", "how to update chrome browser",
-                "update google chrome", "update chrome",
-                "update chrome browser",
-            }) {}
+OmniboxPedalUpdateChrome::OmniboxPedalUpdateChrome()
+    : OmniboxPedal(
+          LabelStrings(IDS_OMNIBOX_PEDAL_UPDATE_CHROME_HINT,
+                       IDS_OMNIBOX_PEDAL_UPDATE_CHROME_HINT_SHORT,
+                       IDS_OMNIBOX_PEDAL_UPDATE_CHROME_SUGGESTION_CONTENTS),
+          GURL(),
+          {
+              "how to update google chrome",
+              "how to update chrome",
+              "how do i update google chrome",
+              "how to update chrome browser",
+              "update google chrome",
+              "update chrome",
+              "update chrome browser",
+          },
+          {
+              SynonymGroup(true,
+                           {
+                               "google chrome",
+                               "browser",
+                               "chrome",
+                           }),
+              SynonymGroup(true,
+                           {
+                               "upgrade",
+                               "install",
+                               "update",
+                           }),
+          }) {}
 
-  void Execute(ExecutionContext& context) const override {
-    context.client_.OpenUpdateChromeDialog();
-  }
+void OmniboxPedalUpdateChrome::Execute(ExecutionContext& context) const {
+  context.client_.OpenUpdateChromeDialog();
+}
 
-  bool IsReadyToTrigger(
-      const AutocompleteProviderClient& client) const override {
-    return client.IsBrowserUpdateAvailable();
-  }
-};
+bool OmniboxPedalUpdateChrome::IsReadyToTrigger(
+    const AutocompleteProviderClient& client) const {
+  return client.IsBrowserUpdateAvailable();
+}
 
 // =============================================================================
 
diff --git a/components/omnibox/browser/omnibox_pedal_implementations.h b/components/omnibox/browser/omnibox_pedal_implementations.h
index 0550aa5..0d70702 100644
--- a/components/omnibox/browser/omnibox_pedal_implementations.h
+++ b/components/omnibox/browser/omnibox_pedal_implementations.h
@@ -8,7 +8,24 @@
 #include <memory>
 #include <vector>
 
-class OmniboxPedal;
+#include "build/build_config.h"
+#include "components/omnibox/browser/omnibox_pedal.h"
+
+class OmniboxPedalClearBrowsingData : public OmniboxPedal {
+ public:
+  OmniboxPedalClearBrowsingData();
+#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+  const gfx::VectorIcon& GetVectorIcon() const override;
+#endif
+};
+
+class OmniboxPedalUpdateChrome : public OmniboxPedal {
+ public:
+  OmniboxPedalUpdateChrome();
+  void Execute(ExecutionContext& context) const override;
+  bool IsReadyToTrigger(
+      const AutocompleteProviderClient& client) const override;
+};
 
 // Returns the full set of encapsulated OmniboxPedal implementations.
 std::vector<std::unique_ptr<OmniboxPedal>> GetPedalImplementations();
diff --git a/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc b/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc
new file mode 100644
index 0000000..59ef623
--- /dev/null
+++ b/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/omnibox/browser/omnibox_pedal_implementations.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
+#include "components/omnibox/browser/omnibox_pedal_provider.h"
+#include "components/omnibox/browser/test_omnibox_client.h"
+#include "components/omnibox/browser/test_omnibox_edit_controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class OmniboxPedalImplementationsTest : public testing::Test {
+ protected:
+  OmniboxPedalImplementationsTest()
+      : omnibox_client_(new TestOmniboxClient),
+        omnibox_edit_controller_(new TestOmniboxEditController) {}
+
+  base::test::ScopedTaskEnvironment task_environment_;
+  std::unique_ptr<TestOmniboxClient> omnibox_client_;
+  std::unique_ptr<TestOmniboxEditController> omnibox_edit_controller_;
+};
+
+TEST_F(OmniboxPedalImplementationsTest, ClientReadiesPedalUpdateChrome) {
+  MockAutocompleteProviderClient client;
+  const OmniboxPedalUpdateChrome pedal;
+  EXPECT_EQ(false, pedal.IsReadyToTrigger(client));
+  client.set_browser_update_available(true);
+  EXPECT_EQ(true, pedal.IsReadyToTrigger(client));
+}
+
+TEST_F(OmniboxPedalImplementationsTest, ProviderFiltersPedalUpdateChrome) {
+  MockAutocompleteProviderClient client;
+  OmniboxPedalProvider provider(client);
+  const base::string16 trigger = base::ASCIIToUTF16("update chrome");
+  const OmniboxPedal* pedal = provider.FindPedalMatch(trigger);
+  EXPECT_EQ(pedal, nullptr) << "Pedal not filtered by condition.";
+  client.set_browser_update_available(true);
+  pedal = provider.FindPedalMatch(trigger);
+  EXPECT_NE(pedal, nullptr) << "Pedal not discovered though condition is met.";
+  EXPECT_TRUE(pedal->IsTriggerMatch(trigger));
+}
+
+TEST_F(OmniboxPedalImplementationsTest, PedalClearBrowsingDataExecutes) {
+  MockAutocompleteProviderClient client;
+  base::TimeTicks match_selection_timestamp;
+  OmniboxPedal::ExecutionContext context(
+      *omnibox_client_, *omnibox_edit_controller_, match_selection_timestamp);
+  const OmniboxPedalClearBrowsingData pedal;
+  pedal.Execute(context);
+  const GURL& url = omnibox_edit_controller_->destination_url();
+  EXPECT_EQ(url, GURL("chrome://settings/clearBrowserData"));
+}
diff --git a/components/omnibox/browser/omnibox_pedal_provider.h b/components/omnibox/browser/omnibox_pedal_provider.h
index 67e4e7ac..d85d930 100644
--- a/components/omnibox/browser/omnibox_pedal_provider.h
+++ b/components/omnibox/browser/omnibox_pedal_provider.h
@@ -18,7 +18,8 @@
   explicit OmniboxPedalProvider(AutocompleteProviderClient& client);
   ~OmniboxPedalProvider();
 
-  // Returns the Pedal triggered by given match_text or nullptr if none trigger.
+  // Returns the Pedal triggered by given |match_text| or nullptr if none
+  // trigger.
   OmniboxPedal* FindPedalMatch(const base::string16& match_text) const;
 
  protected:
diff --git a/components/omnibox/browser/omnibox_pedal_unittest.cc b/components/omnibox/browser/omnibox_pedal_unittest.cc
index a9faca98..511f3a7 100644
--- a/components/omnibox/browser/omnibox_pedal_unittest.cc
+++ b/components/omnibox/browser/omnibox_pedal_unittest.cc
@@ -5,51 +5,72 @@
 #include "components/omnibox/browser/omnibox_pedal.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/time/time.h"
-#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
+#include "components/omnibox/browser/omnibox_pedal_implementations.h"
 #include "components/omnibox/browser/omnibox_pedal_provider.h"
-#include "components/omnibox/browser/test_omnibox_client.h"
-#include "components/omnibox/browser/test_omnibox_edit_controller.h"
+#include "components/strings/grit/components_strings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 class OmniboxPedalTest : public testing::Test {
  protected:
-  OmniboxPedalTest()
-      : omnibox_client_(new TestOmniboxClient),
-        omnibox_edit_controller_(new TestOmniboxEditController) {}
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  std::unique_ptr<TestOmniboxClient> omnibox_client_;
-  std::unique_ptr<TestOmniboxEditController> omnibox_edit_controller_;
+  OmniboxPedalTest() {}
 };
 
-TEST_F(OmniboxPedalTest, PedalExecutes) {
-  MockAutocompleteProviderClient client;
-  OmniboxPedalProvider provider(client);
-  base::TimeTicks match_selection_timestamp;
-  OmniboxPedal::ExecutionContext context(
-      *omnibox_client_, *omnibox_edit_controller_, match_selection_timestamp);
-  {
-    const base::string16 trigger = base::ASCIIToUTF16("clear history");
-    const OmniboxPedal* pedal = provider.FindPedalMatch(trigger);
-    EXPECT_NE(pedal, nullptr) << "Pedal not registered or not triggered.";
-    EXPECT_TRUE(pedal->IsTriggerMatch(trigger));
-    pedal->Execute(context);
-    const GURL& url = omnibox_edit_controller_->destination_url();
-    EXPECT_EQ(url, GURL("chrome://settings/clearBrowserData"));
-  }
+TEST_F(OmniboxPedalTest, SynonymGroupErasesFirstMatchOnly) {
+  const auto group = OmniboxPedal::SynonymGroup(true, {
+                                                          "hello",
+                                                          "hi",
+                                                      });
+  base::string16 text = base::ASCIIToUTF16("hello hi world");
+  const bool found = group.EraseFirstMatchIn(text);
+  EXPECT_TRUE(found);
+  // Only the first representative should be removed.
+  EXPECT_EQ(text, base::ASCIIToUTF16(" hi world"));
 }
 
-TEST_F(OmniboxPedalTest, PedalIsFiltered) {
-  MockAutocompleteProviderClient client;
-  OmniboxPedalProvider provider(client);
-  const base::string16 trigger = base::ASCIIToUTF16("update chrome");
-  const OmniboxPedal* pedal = provider.FindPedalMatch(trigger);
-  EXPECT_EQ(pedal, nullptr) << "Pedal not filtered by condition.";
-  client.set_browser_update_available(true);
-  pedal = provider.FindPedalMatch(trigger);
-  EXPECT_NE(pedal, nullptr) << "Pedal not discovered though condition is met.";
-  EXPECT_TRUE(pedal->IsTriggerMatch(trigger));
-}
\ No newline at end of file
+TEST_F(OmniboxPedalTest, SynonymGroupsDriveConceptMatches) {
+  OmniboxPedal test_pedal(
+      OmniboxPedal::LabelStrings(
+          IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT,
+          IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_HINT_SHORT,
+          IDS_OMNIBOX_PEDAL_CLEAR_BROWSING_DATA_SUGGESTION_CONTENTS),
+      GURL(),
+      {
+          "test trigger phrase",
+      },
+      {
+          OmniboxPedal::SynonymGroup(false,
+                                     {
+                                         "optional",
+                                     }),
+          OmniboxPedal::SynonymGroup(true,
+                                     {
+                                         "required_a",
+                                     }),
+          OmniboxPedal::SynonymGroup(true,
+                                     {
+                                         "required_b",
+                                     }),
+      });
+  const auto is_concept_match = [&](const char* text) {
+    return test_pedal.IsConceptMatch(base::ASCIIToUTF16(text));
+  };
+
+  // As long as required synonym groups are present, order shouldn't matter.
+  EXPECT_TRUE(is_concept_match("required_a required_b"));
+  EXPECT_TRUE(is_concept_match("required_b required_a"));
+
+  // Optional groups may be added without stopping trigger.
+  EXPECT_TRUE(is_concept_match("required_a required_b optional"));
+  EXPECT_TRUE(is_concept_match("required_a optional required_b"));
+  EXPECT_TRUE(is_concept_match("optional required_b required_a"));
+
+  // Any required group's absence will stop trigger.
+  EXPECT_FALSE(is_concept_match("required_a optional"));
+  EXPECT_FALSE(is_concept_match("nonsense"));
+  EXPECT_FALSE(is_concept_match("nonsense optional"));
+
+  // Presence of extra text will stop trigger even with all required present.
+  EXPECT_FALSE(is_concept_match("required_a required_b nonsense optional"));
+  EXPECT_FALSE(is_concept_match("required_b required_a nonsense"));
+}
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc
index d99d8dfe..f0ef0ec 100644
--- a/components/omnibox/browser/omnibox_popup_model.cc
+++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -208,9 +208,11 @@
   selected_line_state_ = state;
   view_->InvalidateLine(selected_line_);
 
-  // Ensures update of accessibility data.
-  edit_model_->view()->OnTemporaryTextMaybeChanged(
-    edit_model_->view()->GetText(), match, false, false);
+  // Ensures update of accessibility data for button text.
+  if (state == BUTTON_FOCUSED) {
+    edit_model_->view()->OnTemporaryTextMaybeChanged(
+        edit_model_->view()->GetText(), match, false, false);
+  }
 }
 
 void OmniboxPopupModel::TryDeletingCurrentItem() {
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index d7b8558..3ed76f8 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -498,15 +498,8 @@
           // Calculator results include a "= " prefix but we don't want to
           // include this in the search terms.
           suggestion.erase(0, 2);
-          // Additionally, on larger (non-phone) form factors, we don't want to
-          // display it in the suggestion contents either, because those devices
-          // display a suggestion type icon that looks like a '='.
-          if (ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_PHONE &&
-              !OmniboxFieldTrial::IsNewAnswerLayoutEnabled())
-            match_contents.erase(0, 2);
         }
-        if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_DESKTOP &&
-            OmniboxFieldTrial::IsNewAnswerLayoutEnabled()) {
+        if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_DESKTOP) {
           annotation = match_contents;
           match_contents = query;
         }
diff --git a/components/omnibox/browser/suggestion_answer.cc b/components/omnibox/browser/suggestion_answer.cc
index de02c03..0235920 100644
--- a/components/omnibox/browser/suggestion_answer.cc
+++ b/components/omnibox/browser/suggestion_answer.cc
@@ -275,8 +275,7 @@
 
   std::string image_url;
   const base::DictionaryValue* optional_image;
-  if (OmniboxFieldTrial::IsNewAnswerLayoutEnabled() &&
-      answer_json->GetDictionary("i", &optional_image) &&
+  if (answer_json->GetDictionary("i", &optional_image) &&
       optional_image->GetString("d", &image_url)) {
     result->image_url_ = GURL(image_url);
   } else {
@@ -311,9 +310,6 @@
 }
 
 void SuggestionAnswer::InterpretTextTypes() {
-  if (!OmniboxFieldTrial::IsNewAnswerLayoutEnabled())
-    return;
-
   switch (type()) {
     case SuggestionAnswer::ANSWER_TYPE_WEATHER: {
       second_line_.SetTextStyles(SuggestionAnswer::TOP_ALIGNED,
diff --git a/components/optimization_guide/optimization_guide_service.h b/components/optimization_guide/optimization_guide_service.h
index 106d4519..92b8fbb0 100644
--- a/components/optimization_guide/optimization_guide_service.h
+++ b/components/optimization_guide/optimization_guide_service.h
@@ -28,7 +28,9 @@
 
   // Adds the observer and synchronously dispatches the current
   // HintsComponentInfo to it if one is already available.
-  void AddObserver(OptimizationGuideServiceObserver* observer);
+  //
+  // Virtual so it can be mocked out in tests.
+  virtual void AddObserver(OptimizationGuideServiceObserver* observer);
   // Virtual so it can be mocked out in tests.
   virtual void RemoveObserver(OptimizationGuideServiceObserver* observer);
 
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 94cbebb6..0f3e883 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -608,12 +608,20 @@
   RecordMetricOnCompareParsingResult(*observed_password_form);
 
   if (observed_password_form->is_new_password_reliable && !IsBlacklisted()) {
+#if defined(OS_IOS)
+    driver_->FormEligibleForGenerationFound(
+        {.form_name = observed_password_form->form_data.name,
+         .new_password_element = observed_password_form->new_password_element,
+         .confirmation_password_element =
+             observed_password_form->confirmation_password_element});
+#else
     driver_->FormEligibleForGenerationFound(
         {.new_password_renderer_id =
              observed_password_form->new_password_element_renderer_id,
          .confirmation_password_renderer_id =
              observed_password_form
                  ->confirmation_password_element_renderer_id});
+#endif
   }
 
   // TODO(https://crbug.com/831123). Implement correct treating of federated
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index 150421e..dd0e2146 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -456,9 +456,15 @@
   constexpr uint32_t kNoID = FormFieldData::kNotSetFormControlRendererId;
   EXPECT_EQ(kNoID, fill_data.password_field.unique_renderer_id);
   EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
+#if defined(OS_IOS)
+  EXPECT_EQ(ASCIIToUTF16("sign-in"), generation_data.form_name);
+  EXPECT_EQ(ASCIIToUTF16("password"), generation_data.new_password_element);
+  EXPECT_EQ(base::string16(), generation_data.confirmation_password_element);
+#else
   EXPECT_EQ(observed_form_.fields.back().unique_renderer_id,
             generation_data.new_password_renderer_id);
   EXPECT_EQ(kNoID, generation_data.confirmation_password_renderer_id);
+#endif
 }
 
 // Check that generation signal is sent the the renderer when new password
@@ -485,9 +491,15 @@
   fetcher_->SetNonFederated({}, 0u);
 
   task_runner_->FastForwardUntilNoTasksRemain();
+#if defined(OS_IOS)
+  EXPECT_EQ(ASCIIToUTF16("sign-in"), generation_data.form_name);
+  EXPECT_EQ(ASCIIToUTF16("password"), generation_data.new_password_element);
+  EXPECT_EQ(base::string16(), generation_data.confirmation_password_element);
+#else
   EXPECT_EQ(new_password_render_id, generation_data.new_password_renderer_id);
   EXPECT_EQ(confirm_password_render_id,
             generation_data.confirmation_password_renderer_id);
+#endif
 }
 
 TEST_F(NewPasswordFormManagerTest, AutofillWithBlacklistedMatch) {
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 2fb7d4c..4a8b29e 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -424,19 +424,26 @@
     form_without_username.username_value.clear();
     form_saver()->PresaveGeneratedPassword(form_without_username);
   }
-  // If a password had been generated already, a call to
-  // PresaveGeneratedPassword() implies that this password was modified.
+
   if (!has_generated_password_) {
     votes_uploader_.set_generated_password_changed(false);
     metrics_recorder_->SetGeneratedPasswordStatus(
         PasswordFormMetricsRecorder::GeneratedPasswordStatus::
             kPasswordAccepted);
   } else {
-    votes_uploader_.set_generated_password_changed(true);
-    metrics_recorder_->SetGeneratedPasswordStatus(
-        PasswordFormMetricsRecorder::GeneratedPasswordStatus::kPasswordEdited);
+    // If the password is already generated and the new value to presave differs
+    // from the presaved one, then mark that the generated password was changed.
+    // If a user recovers the original generated password, it will be recorded
+    // as a password change.
+    if (generated_password_ != form.password_value) {
+      votes_uploader_.set_generated_password_changed(true);
+      metrics_recorder_->SetGeneratedPasswordStatus(
+          PasswordFormMetricsRecorder::GeneratedPasswordStatus::
+              kPasswordEdited);
+    }
   }
   has_generated_password_ = true;
+  generated_password_ = form.password_value;
   votes_uploader_.set_has_generated_password(true);
 }
 
@@ -444,6 +451,7 @@
   DCHECK(has_generated_password_);
   form_saver()->RemovePresavedPassword();
   has_generated_password_ = false;
+  generated_password_.clear();
   votes_uploader_.set_has_generated_password(false);
   votes_uploader_.set_generated_password_changed(false);
   metrics_recorder_->SetGeneratedPasswordStatus(
@@ -1021,6 +1029,7 @@
   result->pending_credentials_ = pending_credentials_;
   result->is_new_login_ = is_new_login_;
   result->has_generated_password_ = has_generated_password_;
+  result->generated_password_ = generated_password_;
   result->password_overridden_ = password_overridden_;
   result->retry_password_form_password_update_ =
       retry_password_form_password_update_;
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index ad71b91..a1f422a 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -345,9 +345,15 @@
   // to an existing one.
   bool is_new_login_;
 
-  // Whether this form has an auto generated password.
+  // Whether this form has an auto generated password. If the user modifies the
+  // password it remains in status "generated".
   bool has_generated_password_;
 
+  // If |has_generated_password_|, contains a generated password. If the user
+  // modifies the generated password, this field is updated to reflect the
+  // modified value.
+  base::string16 generated_password_;
+
   // Whether the saved password was overridden.
   bool password_overridden_;
 
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 0e2fcad..67f7741 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -3252,6 +3252,8 @@
 
 TEST_F(PasswordFormManagerTest, PresaveGeneratedPasswordAndRemoveIt) {
   PasswordForm credentials = *observed_form();
+  credentials.username_value = ASCIIToUTF16("username");
+  credentials.password_value = ASCIIToUTF16("password");
 
   // Simulate the user accepted a generated password.
   EXPECT_CALL(MockFormSaver::Get(form_manager()),
@@ -3260,6 +3262,14 @@
   EXPECT_TRUE(form_manager()->HasGeneratedPassword());
   EXPECT_FALSE(form_manager()->generated_password_changed());
 
+  // Simulate the user changed the presaved username.
+  credentials.username_value = ASCIIToUTF16("new_username");
+  EXPECT_CALL(MockFormSaver::Get(form_manager()),
+              PresaveGeneratedPassword(credentials));
+  form_manager()->PresaveGeneratedPassword(credentials);
+  EXPECT_TRUE(form_manager()->HasGeneratedPassword());
+  EXPECT_FALSE(form_manager()->generated_password_changed());
+
   // Simulate the user changed the presaved password.
   credentials.password_value = ASCIIToUTF16("changed_password");
   EXPECT_CALL(MockFormSaver::Get(form_manager()),
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 1da2d5b..3949b0b 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -282,7 +282,7 @@
   // No migration was attempted (this value should not occur).
   kNotAttempted = 0,
   // The last attempt was not completed.
-  kFailed = 1,
+  kDeprecatedFailed = 1,
   // All the data is in the encrypted loginDB.
   kCopiedAll = 2,
   // The standard login database is encrypted.
@@ -291,13 +291,20 @@
   kStarted = 4,
   // No access to the native backend.
   kPostponed = 5,
-  // Could not create or write into the temporary file.
-  kFailedCreatedEncrypted = 6,
+  // Could not create or write into the temporary file. Deprecated and replaced
+  // by more precise errors.
+  kDeprecatedFailedCreatedEncrypted = 6,
   // Could not read from the native backend.
   kFailedAccessNative = 7,
   // Could not replace old database.
   kFailedReplace = 8,
-  kMaxValue = kFailedReplace
+  // Could not initialise the temporary encrypted database.
+  kFailedInitEncrypted,
+  // Could not reset th temporary encrypted database.
+  kFailedRecreateEncrypted,
+  // Could not add entries into the temporary encrypted database.
+  kFailedWriteToEncrypted,
+  kMaxValue = kFailedWriteToEncrypted
 };
 
 // Type of the password drop-down shown on focus field.
diff --git a/components/password_manager/ios/js_password_manager.h b/components/password_manager/ios/js_password_manager.h
index 7b240f2..c22d06a5 100644
--- a/components/password_manager/ios/js_password_manager.h
+++ b/components/password_manager/ios/js_password_manager.h
@@ -65,6 +65,16 @@
                 password:(NSString*)password
        completionHandler:(void (^)(BOOL))completionHandler;
 
+// Fills new password field for (optional) |newPasswordIdentifier| and for
+// (optional) confirm password field |confirmPasswordIdentifier| in the form
+// identified by |formData|. Invokes |completionHandler| with true if any fields
+// were filled, false otherwise.
+- (void)fillPasswordForm:(NSString*)formName
+        newPasswordIdentifier:(NSString*)newPasswordIdentifier
+    confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+            generatedPassword:(NSString*)generatedPassword
+            completionHandler:(void (^)(BOOL))completionHandler;
+
 // Designated initializer. |receiver| should not be nil.
 - (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
     NS_DESIGNATED_INITIALIZER;
diff --git a/components/password_manager/ios/js_password_manager.mm b/components/password_manager/ios/js_password_manager.mm
index 1148b76..56eee97 100644
--- a/components/password_manager/ios/js_password_manager.mm
+++ b/components/password_manager/ios/js_password_manager.mm
@@ -122,5 +122,21 @@
              }];
 }
 
+- (void)fillPasswordForm:(NSString*)formName
+        newPasswordIdentifier:(NSString*)newPasswordIdentifier
+    confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+            generatedPassword:(NSString*)generatedPassword
+            completionHandler:(void (^)(BOOL))completionHandler {
+  NSString* script = [NSString
+      stringWithFormat:@"__gCrWeb.passwords."
+                       @"fillPasswordFormWithGeneratedPassword(%@, %@, %@, %@)",
+                       JSONEscape(formName), JSONEscape(newPasswordIdentifier),
+                       JSONEscape(confirmPasswordIdentifier),
+                       JSONEscape(generatedPassword)];
+  [_receiver executeJavaScript:script
+             completionHandler:^(id result, NSError*) {
+               completionHandler([result isEqual:@YES]);
+             }];
+}
 
 @end
diff --git a/components/password_manager/ios/password_form_helper.h b/components/password_manager/ios/password_form_helper.h
index 6e18d56..f6df76f 100644
--- a/components/password_manager/ios/password_form_helper.h
+++ b/components/password_manager/ios/password_form_helper.h
@@ -66,6 +66,16 @@
 - (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
        completionHandler:(nullable void (^)(BOOL))completionHandler;
 
+// Fills new password field for (optional as @"") |newPasswordIdentifier| and
+// for (optional as @"") confirm password field |confirmPasswordIdentifier| in
+// the form identified by |formData|. Invokes |completionHandler| with true if
+// any fields were filled, false otherwise.
+- (void)fillPasswordForm:(NSString*)formName
+        newPasswordIdentifier:(NSString*)newPasswordIdentifier
+    confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+            generatedPassword:(NSString*)generatedPassword
+            completionHandler:(nullable void (^)(BOOL))completionHandler;
+
 // Autofills credentials into the page. Credentials and input fields are
 // specified by |fillData|. Invokes |completionHandler| when finished with YES
 // if successful and NO otherwise.
diff --git a/components/password_manager/ios/password_form_helper.mm b/components/password_manager/ios/password_form_helper.mm
index 1a0f1754..dad1a80 100644
--- a/components/password_manager/ios/password_form_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -367,6 +367,23 @@
        completionHandler:completionHandler];
 }
 
+- (void)fillPasswordForm:(NSString*)formName
+        newPasswordIdentifier:(NSString*)newPasswordIdentifier
+    confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+            generatedPassword:(NSString*)generatedPassword
+            completionHandler:(nullable void (^)(BOOL))completionHandler {
+  // Send JSON over to the web view.
+  [self.jsPasswordManager fillPasswordForm:formName
+                     newPasswordIdentifier:newPasswordIdentifier
+                 confirmPasswordIdentifier:confirmPasswordIdentifier
+                         generatedPassword:generatedPassword
+                         completionHandler:^(BOOL result) {
+                           if (completionHandler) {
+                             completionHandler(result);
+                           }
+                         }];
+}
+
 - (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
                    completionHandler:
                        (nullable void (^)(BOOL))completionHandler {
diff --git a/components/password_manager/ios/resources/password_controller.js b/components/password_manager/ios/resources/password_controller.js
index 25eade0..6ec544fd9 100644
--- a/components/password_manager/ios/resources/password_controller.js
+++ b/components/password_manager/ios/resources/password_controller.js
@@ -202,6 +202,36 @@
 };
 
 /**
+ * Fills all password fields in the form identified by |formName|
+ * with |password|.
+ *
+ * @param {string} formName The name of the form to fill.
+ * @param {string} newPasswordIdentifier The id of password element to fill.
+ * @param {string} confirmPasswordIdentifier The id of confirm password element
+ *   to fill.
+ * @param {string} password The password to fill.
+ * @return {boolean} Whether a password field has been filled.
+*/
+__gCrWeb.passwords['fillPasswordFormWithGeneratedPassword'] = function(
+    formName, newPasswordIdentifier, confirmPasswordIdentifier, password) {
+  var form = __gCrWeb.form.getFormElementFromIdentifier(formName);
+  if (!form)
+    return false;
+  var inputs = getFormInputElements_(form);
+  var newPasswordField =
+      findInputByFieldIdentifier_(inputs, newPasswordIdentifier);
+  if (newPasswordField) {
+    newPasswordField.value = password;
+  }
+  var confirmPasswordField =
+      findInputByFieldIdentifier_(inputs, confirmPasswordIdentifier);
+  if (confirmPasswordField) {
+    confirmPasswordField.value = password;
+  }
+  return !!newPasswordField || !!confirmPasswordField;
+};
+
+/**
  * Given a description of a form (origin, action and input fields),
  * finds that form on the page and fills in the specified username
  * and password.
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 5d757d4..4bed940cf 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -260,7 +260,6 @@
                                             const WebPreferences& preferences)
     : plugin_(plugin) {
   web_view_ = WebView::Create(/*client=*/this,
-                              /*widget_client=*/this,
                               /*is_hidden=*/false,
                               /*compositing_enabled=*/false,
                               /*opener=*/nullptr);
@@ -271,6 +270,7 @@
   WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
       web_view_, this, nullptr,
       mojo::MakeRequest(&document_interface_broker).PassMessagePipe(), nullptr);
+  // The created WebFrameWidget is owned by the |web_frame|.
   WebFrameWidget::CreateForMainFrame(this, web_frame);
 }
 
diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc
index 190f41a1..074db44 100644
--- a/components/policy/core/common/cloud/device_management_service.cc
+++ b/components/policy/core/common/cloud/device_management_service.cc
@@ -304,7 +304,13 @@
     UMA_HISTOGRAM_ENUMERATION("Enterprise.DMServerRequestSuccess",
                               DMServerRequestSuccess::REQUEST_ERROR,
                               DMServerRequestSuccess::REQUEST_MAX);
-    LOG(WARNING) << "DMServer sent an error response: " << response_code;
+    em::DeviceManagementResponse response;
+    if (response.ParseFromString(data)) {
+      LOG(WARNING) << "DMServer sent an error response: " << response_code
+                   << ". " << response.error_message();
+    } else {
+      LOG(WARNING) << "DMServer sent an error response: " << response_code;
+    }
   } else {
     // Success with retries_count_ retries.
     UMA_HISTOGRAM_EXACT_LINEAR(
diff --git a/components/policy/core/common/cloud/mock_cloud_external_data_manager.h b/components/policy/core/common/cloud/mock_cloud_external_data_manager.h
index 7aaa4aed..294004f 100644
--- a/components/policy/core/common/cloud/mock_cloud_external_data_manager.h
+++ b/components/policy/core/common/cloud/mock_cloud_external_data_manager.h
@@ -27,8 +27,8 @@
   MOCK_METHOD0(OnPolicyStoreLoaded, void(void));
   MOCK_METHOD1(Connect, void(scoped_refptr<network::SharedURLLoaderFactory>));
   MOCK_METHOD0(Disconnect, void(void));
-  MOCK_METHOD2(Fetch, void(const std::string&,
-                           const ExternalDataFetcher::FetchCallback&));
+  MOCK_METHOD2(Fetch,
+               void(const std::string&, ExternalDataFetcher::FetchCallback));
 
   std::unique_ptr<ExternalDataFetcher> CreateExternalDataFetcher(
       const std::string& policy);
diff --git a/components/policy/core/common/external_data_fetcher.cc b/components/policy/core/common/external_data_fetcher.cc
index 1bce30a..a7293a05 100644
--- a/components/policy/core/common/external_data_fetcher.cc
+++ b/components/policy/core/common/external_data_fetcher.cc
@@ -35,11 +35,11 @@
          first->policy_ == second->policy_;
 }
 
-void ExternalDataFetcher::Fetch(const FetchCallback& callback) const {
+void ExternalDataFetcher::Fetch(FetchCallback callback) const {
   if (manager_)
-    manager_->Fetch(policy_, callback);
+    manager_->Fetch(policy_, std::move(callback));
   else
-    callback.Run(std::unique_ptr<std::string>());
+    std::move(callback).Run(nullptr);
 }
 
 }  // namespace policy
diff --git a/components/policy/core/common/external_data_fetcher.h b/components/policy/core/common/external_data_fetcher.h
index 0c014e79..07baddeb6 100644
--- a/components/policy/core/common/external_data_fetcher.h
+++ b/components/policy/core/common/external_data_fetcher.h
@@ -20,7 +20,7 @@
 // data for a policy.
 class POLICY_EXPORT ExternalDataFetcher {
  public:
-  typedef base::Callback<void(std::unique_ptr<std::string>)> FetchCallback;
+  typedef base::OnceCallback<void(std::unique_ptr<std::string>)> FetchCallback;
 
   // This instance's Fetch() method will instruct the |manager| to retrieve the
   // external data referenced by the given |policy|.
@@ -41,7 +41,7 @@
   // |callback| will be invoked when the temporary hindrance is resolved. If
   // retrieval is permanently impossible (e.g. |policy_| references data that
   // does not exist on the server), the |callback| will never be invoked.
-  void Fetch(const FetchCallback& callback) const;
+  void Fetch(FetchCallback callback) const;
 
  private:
   base::WeakPtr<ExternalDataManager> manager_;
diff --git a/components/policy/core/common/external_data_manager.h b/components/policy/core/common/external_data_manager.h
index 406cf59..10c7aa3ad 100644
--- a/components/policy/core/common/external_data_manager.h
+++ b/components/policy/core/common/external_data_manager.h
@@ -28,7 +28,7 @@
   // retrieval is permanently impossible (e.g. |policy| references data that
   // does not exist on the server), the |callback| will never be invoked.
   virtual void Fetch(const std::string& policy,
-                     const ExternalDataFetcher::FetchCallback& callback) = 0;
+                     ExternalDataFetcher::FetchCallback callback) = 0;
 };
 
 }  // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 7bfacf4..d172013f 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -13228,7 +13228,7 @@
       When this policy is set to a non-empty list of time intervals:
       Devices will not be able to check for updates automatically during the specified time intervals. Devices that require a rollback or are below the minimum <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version will not be affected by this policy due to potential security issues. Furthermore, this policy will not block update checks requested by users or administrators.
       When this policy is unset or contains no time intervals:
-      No automatic update checks will be blocked by this policy, but they may be blocked by other policies.''',
+      No automatic update checks will be blocked by this policy, but they may be blocked by other policies. This feature is only enabled on Chrome devices configured as auto-launch kiosks. Other devices will not be restricted by this policy.''',
     },
     {
       'id': 454,
diff --git a/components/previews/content/BUILD.gn b/components/previews/content/BUILD.gn
index b3d00926..f19eb777 100644
--- a/components/previews/content/BUILD.gn
+++ b/components/previews/content/BUILD.gn
@@ -6,12 +6,17 @@
   sources = [
     "hint_cache.cc",
     "hint_cache.h",
+    "hint_cache_leveldb_store.cc",
+    "hint_cache_leveldb_store.h",
+    "hint_cache_store.h",
     "previews_content_util.cc",
     "previews_content_util.h",
     "previews_decider_impl.cc",
     "previews_decider_impl.h",
     "previews_hints.cc",
     "previews_hints.h",
+    "previews_hints_util.cc",
+    "previews_hints_util.h",
     "previews_optimization_guide.cc",
     "previews_optimization_guide.h",
     "previews_ui_service.cc",
@@ -25,6 +30,7 @@
     "//components/blacklist/opt_out_blacklist",
     "//components/optimization_guide",
     "//components/optimization_guide/proto:optimization_guide_proto",
+    "//components/previews/content/proto:hint_cache_proto",
     "//components/previews/core",
     "//components/url_matcher",
     "//components/variations",
@@ -37,10 +43,12 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "hint_cache_leveldb_store_unittest.cc",
     "hint_cache_unittest.cc",
     "previews_content_util_unittest.cc",
     "previews_decider_impl_unittest.cc",
     "previews_hints_unittest.cc",
+    "previews_hints_util_unittest.cc",
     "previews_optimization_guide_unittest.cc",
     "previews_ui_service_unittest.cc",
     "previews_user_data_unittest.cc",
@@ -50,8 +58,10 @@
     ":content",
     "//base",
     "//components/blacklist/opt_out_blacklist",
+    "//components/leveldb_proto:test_support",
     "//components/optimization_guide",
     "//components/optimization_guide/proto:optimization_guide_proto",
+    "//components/previews/content/proto:hint_cache_proto",
     "//components/previews/core",
     "//components/variations",
     "//content/public/common",
diff --git a/components/previews/content/DEPS b/components/previews/content/DEPS
index f226b3f4..579b2e9 100644
--- a/components/previews/content/DEPS
+++ b/components/previews/content/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/blacklist/opt_out_blacklist",
+  "+components/leveldb_proto",
   "+components/optimization_guide",
   "+components/url_matcher",
   "+components/variations",
diff --git a/components/previews/content/hint_cache.cc b/components/previews/content/hint_cache.cc
index 3b1acf3..a125f276 100644
--- a/components/previews/content/hint_cache.cc
+++ b/components/previews/content/hint_cache.cc
@@ -4,91 +4,165 @@
 
 #include "components/previews/content/hint_cache.h"
 
+#include <algorithm>
+
+#include "base/bind.h"
 #include "url/gurl.h"
 
 namespace previews {
 
+namespace {
+
 // Realistic minimum length of a host suffix.
 const int kMinHostSuffix = 6;  // eg., abc.tv
 
-HintCache::Data::Data() = default;
+// The default number of hints retained within the memory cache. When the limit
+// is exceeded, the least recently used hint is purged from the cache.
+const size_t kDefaultMaxMemoryCacheHints = 20;
 
-HintCache::Data::~Data() = default;
+}  // namespace
 
-void HintCache::Data::AddHint(const optimization_guide::proto::Hint& hint) {
-  DCHECK_EQ(optimization_guide::proto::HOST_SUFFIX, hint.key_representation());
-  activation_list_.insert(hint.key());
-  memory_cache_[hint.key()] = hint;
+HintCache::HintCache(
+    std::unique_ptr<HintCacheStore> hint_store,
+    base::Optional<int> max_memory_cache_hints /*= base::Optional<int>()*/)
+    : hint_store_(std::move(hint_store)),
+      memory_cache_(
+          std::max(max_memory_cache_hints.value_or(kDefaultMaxMemoryCacheHints),
+                   1)) {
+  DCHECK(hint_store_);
 }
 
-bool HintCache::Data::HasHints() const {
-  return !activation_list_.empty();
+HintCache::~HintCache() = default;
+
+void HintCache::Initialize(bool purge_existing_data,
+                           base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  hint_store_->Initialize(
+      purge_existing_data,
+      base::BindOnce(&HintCache::OnStoreInitialized, base::Unretained(this),
+                     std::move(callback)));
 }
 
-HintCache::Data::Data(Data&& other) = default;
+std::unique_ptr<HintCacheStore::ComponentUpdateData>
+HintCache::MaybeCreateComponentUpdateData(const base::Version& version) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return hint_store_->MaybeCreateComponentUpdateData(version);
+}
 
-HintCache::HintCache(Data&& data) : data_(std::move(data)) {
-  // Detach from sequence as the HintCache can be constructed on a different
-  // thread than it is used on.
-  DETACH_FROM_SEQUENCE(sequence_checker_);
+void HintCache::UpdateComponentData(
+    std::unique_ptr<HintCacheStore::ComponentUpdateData> component_data,
+    base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(component_data);
+
+  // Clear the memory cache prior to updating the store with the new component
+  // data.
+  memory_cache_.Clear();
+
+  hint_store_->UpdateComponentData(std::move(component_data),
+                                   std::move(callback));
 }
 
 bool HintCache::HasHint(const std::string& host) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return !DetermineHostSuffix(host).empty();
+  HintCacheStore::EntryKey hint_entry_key;
+  return FindHintEntryKey(host, &hint_entry_key);
 }
 
 void HintCache::LoadHint(const std::string& host, HintLoadedCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const optimization_guide::proto::Hint* hint = GetHint(host);
-  if (hint) {
-    // Hint already loaded in memory.
-    std::move(callback).Run(*hint);
+
+  HintCacheStore::EntryKey hint_entry_key;
+  if (!FindHintEntryKey(host, &hint_entry_key)) {
+    return;
   }
-  // TODO(dougarnett): Add backing store support to load from.
+
+  // Search for the entry key in the memory cache; if it is not already there,
+  // then asynchronously load it from the store and return.
+  auto hint_it = memory_cache_.Get(hint_entry_key);
+  if (hint_it == memory_cache_.end()) {
+    hint_store_->LoadHint(
+        hint_entry_key,
+        base::BindOnce(&HintCache::OnLoadStoreHint, base::Unretained(this),
+                       std::move(callback)));
+    return;
+  }
+
+  // Run the callback with the hint from the memory cache.
+  std::move(callback).Run(hint_it->second.get());
 }
 
-bool HintCache::IsHintLoaded(const std::string& host) const {
+const optimization_guide::proto::Hint* HintCache::GetHintIfLoaded(
+    const std::string& host) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::string host_suffix = DetermineHostSuffix(host);
-  if (host_suffix.empty()) {
-    return false;
-  }
-  return data_.memory_cache_.find(host_suffix) != data_.memory_cache_.end();
-}
 
-const optimization_guide::proto::Hint* HintCache::GetHint(
-    const std::string& host) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::string host_suffix = DetermineHostSuffix(host);
-  if (host_suffix.empty()) {
+  // Try to retrieve the hint entry key for the host. If no hint exists for the
+  // host, then simply return.
+  HintCacheStore::EntryKey hint_entry_key;
+  if (!FindHintEntryKey(host, &hint_entry_key)) {
     return nullptr;
   }
-  auto it = data_.memory_cache_.find(host_suffix);
-  if (it != data_.memory_cache_.end()) {
-    return &it->second;
+
+  // Find the hint within the memory cache. It will only be available here if
+  // it has been loaded recently enough to be retained within the MRU cache.
+  auto hint_it = memory_cache_.Get(hint_entry_key);
+  if (hint_it != memory_cache_.end()) {
+    return hint_it->second.get();
   }
 
   return nullptr;
 }
 
-std::string HintCache::DetermineHostSuffix(const std::string& host) const {
+void HintCache::OnStoreInitialized(base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::string suffix(host);
+  std::move(callback).Run();
+}
+
+void HintCache::OnLoadStoreHint(
+    HintLoadedCallback callback,
+    const HintCacheStore::EntryKey& hint_entry_key,
+    std::unique_ptr<optimization_guide::proto::Hint> hint) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!hint) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  // Check if the hint was cached in memory after the load was requested from
+  // the store. This can occur if multiple loads for the same entry key occur
+  // consecutively prior to any returning.
+  auto hint_it = memory_cache_.Get(hint_entry_key);
+  if (hint_it == memory_cache_.end()) {
+    hint_it = memory_cache_.Put(hint_entry_key, std::move(hint));
+  }
+
+  std::move(callback).Run(hint_it->second.get());
+}
+
+bool HintCache::FindHintEntryKey(
+    const std::string& host,
+    HintCacheStore::EntryKey* out_hint_entry_key) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(out_hint_entry_key);
+
   // Look for longest host name suffix that has a hint. No need to continue
   // lookups and substring work once get to a root domain like ".com" or
   // ".co.in" (MinHostSuffix length check is a heuristic for that).
-  while (suffix.length() >= kMinHostSuffix) {
-    if (data_.activation_list_.find(suffix) != data_.activation_list_.end()) {
-      return suffix;
+  std::string host_suffix(host);
+  while (host_suffix.length() >= kMinHostSuffix) {
+    // Attempt to find a hint entry key associated with the current host suffix
+    // within the store.
+    if (hint_store_->FindHintEntryKey(host_suffix, out_hint_entry_key)) {
+      return true;
     }
-    size_t pos = suffix.find_first_of('.');
+
+    size_t pos = host_suffix.find_first_of('.');
     if (pos == std::string::npos) {
-      return std::string();
+      break;
     }
-    suffix = suffix.substr(pos + 1);
+    host_suffix = host_suffix.substr(pos + 1);
   }
-  return std::string();
+  return false;
 }
 
 }  // namespace previews
diff --git a/components/previews/content/hint_cache.h b/components/previews/content/hint_cache.h
index 37dfd7c5..a30163f 100644
--- a/components/previews/content/hint_cache.h
+++ b/components/previews/content/hint_cache.h
@@ -5,66 +5,58 @@
 #ifndef COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_H_
 #define COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_H_
 
-#include <map>
 #include <string>
-#include <unordered_set>
 
 #include "base/callback.h"
+#include "base/containers/mru_cache.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/content/hint_cache_store.h"
 
 namespace previews {
 
 using HintLoadedCallback =
-    base::OnceCallback<void(const optimization_guide::proto::Hint&)>;
+    base::OnceCallback<void(const optimization_guide::proto::Hint*)>;
 
-// Holds a set of optimization hints received from the Cacao service.
-// This may include hints received from the ComponentUpdater and hints
-// fetched from a Cacao Optimization Guide Service API. It will hold
-// the host suffix keys of all cached hints (aka, activation list). It
-// will manage a set of the associated hints details in memory and all
-// of the hints in a persisted backing store.
+// Contains a set of optimization hints received from the Cacao service. This
+// may include hints received from the ComponentUpdater and hints fetched from a
+// Cacao Optimization Guide Service API. The availability of hints is queryable
+// via host name. The cache itself consists of a backing store, which allows for
+// asynchronous loading of any available hint, and an MRU memory cache, which
+// can be used to synchronously retrieve recently loaded hints.
 class HintCache {
  public:
-  // Data is used to generate and store the hint data for the cache. After the
-  // hints from the hints protobuf are fully added to Data, it is moved into the
-  // HintCache constructor.
-  class Data {
-   public:
-    Data();
-    ~Data();
+  // Construct the HintCache with a backing store and an optional max memory
+  // cache size. While |hint_store| is required, |max_memory_cache_hints| is
+  // optional and the default max size will be used if it is not provided.
+  explicit HintCache(
+      std::unique_ptr<HintCacheStore> hint_store,
+      base::Optional<int> max_memory_cache_hints = base::Optional<int>());
+  ~HintCache();
 
-    void AddHint(const optimization_guide::proto::Hint& hint);
-    bool HasHints() const;
+  // Initializes the backing store contained within the hint cache and
+  // asynchronously runs the callback after initialization is complete.
+  // If |purge_existing_data| is set to true, then the cache will purge any
+  // pre-existing data and begin in a clean state.
+  void Initialize(bool purge_existing_data, base::OnceClosure callback);
 
-   private:
-    friend class HintCache;
+  // Returns a ComponentUpdateData created by the store, based upon the provided
+  // component version. During component processing, hints from the component
+  // are moved into the component update data. After component processing
+  // completes, the component update data is provided to the backing store in
+  // UpdateComponentData() and used to update its component hints. In the case
+  // the provided component version is not newer than the store's version,
+  // nullptr will be returned by the call.
+  std::unique_ptr<HintCacheStore::ComponentUpdateData>
+  MaybeCreateComponentUpdateData(const base::Version& version) const;
 
-    // The move constructor is private, as it is only intended for use by the
-    // HintCache constructor. An rvalue reference to a HintCache::Data object is
-    // moved into the HintCache's |data_| member variable during construction.
-    Data(Data&& other);
-
-    // The set of host suffixes that have Hint data.
-    std::unordered_set<std::string> activation_list_;
-
-    // The in-memory cache of hints. Maps host suffix to Hint proto.
-    std::map<std::string, optimization_guide::proto::Hint> memory_cache_;
-
-    DISALLOW_COPY_AND_ASSIGN(Data);
-  };
-
-  // The hint cache can only be constructed from a Data object rvalue reference,
-  // which is used to move construct the HintCache's |data_| variable. After
-  // this, |data_| is immutable.
-  //
-  // Example:
-  //   HintCache::Data data;
-  //   data.AddHint(hint1);
-  //   data.AddHint(hint2);
-  //   HintCache hint_cache(std::move(data));
-  explicit HintCache(Data&& data);
+  // Updates the store's component data using the provided ComponentUpdateData
+  // and asynchronously runs the provided callback after the update finishes.
+  void UpdateComponentData(
+      std::unique_ptr<HintCacheStore::ComponentUpdateData> component_data,
+      base::OnceClosure callback);
 
   // Returns whether the cache has a hint data for |host| locally (whether
   // in memory or persisted on disk).
@@ -75,23 +67,43 @@
   // is found for |host|.
   void LoadHint(const std::string& host, HintLoadedCallback callback);
 
-  // Returns whether there is hint data available for |host| in memory.
-  bool IsHintLoaded(const std::string& host) const;
-
-  // Returns the hint data for |host| found in memory, otherwise nullptr.
-  const optimization_guide::proto::Hint* GetHint(const std::string& host) const;
+  // Returns the hint data for |host| if found in memory, otherwise nullptr.
+  const optimization_guide::proto::Hint* GetHintIfLoaded(
+      const std::string& host);
 
  private:
-  // Returns the most specific host suffix of the host name that is present
-  // in the activation list.
-  std::string DetermineHostSuffix(const std::string& host) const;
+  using StoreHintMemoryCache =
+      base::HashingMRUCache<HintCacheStore::EntryKey,
+                            std::unique_ptr<optimization_guide::proto::Hint>>;
 
-  // |data_| contains all of the hint data available in the cache. It is
-  // immutable.
-  const Data data_;
+  // The callback run after the store finishes initialization. This then runs
+  // the callback initially provided by the Initialize() call.
+  void OnStoreInitialized(base::OnceClosure callback);
 
-  // TODO(dougarnett): Add MRU subset support (mru_cache).
-  // TODO(dougarnett): Add a backing store with all hints.
+  // The callback run after the store finishes loading a hint. This adds the
+  // loaded hint to |memory_cache_|, potentially purging the least recently
+  // used element, and then runs the callback initially provided by the
+  // LoadHint() call.
+  void OnLoadStoreHint(HintLoadedCallback callback,
+                       const HintCacheStore::EntryKey& store_hint_entry_key,
+                       std::unique_ptr<optimization_guide::proto::Hint> hint);
+
+  // Finds the most specific host suffix of the host name for which the store
+  // has a hint and populates |out_hint_entry_key| with the hint's corresponding
+  // entry key. Returns true if a hint was successfully found.
+  bool FindHintEntryKey(const std::string& host,
+                        HintCacheStore::EntryKey* out_hint_entry_key) const;
+
+  // The backing store used with this hint cache. Set during construction.
+  const std::unique_ptr<HintCacheStore> hint_store_;
+
+  // The in-memory cache of hints loaded from the store. Maps store EntryKey to
+  // Hint proto. This servers two purposes:
+  //  1. Allows hints to be requested on navigation and retained in memory until
+  //     commit, when they can be synchronously retrieved from the cache.
+  //  2. Reduces churn of needing to reload hints from frequently visited sites
+  //     multiple times during a session.
+  StoreHintMemoryCache memory_cache_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/previews/content/hint_cache_leveldb_store.cc b/components/previews/content/hint_cache_leveldb_store.cc
new file mode 100644
index 0000000..b011f1cf
--- /dev/null
+++ b/components/previews/content/hint_cache_leveldb_store.cc
@@ -0,0 +1,507 @@
+// 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 "components/previews/content/hint_cache_leveldb_store.h"
+
+#include "components/leveldb_proto/public/proto_database_provider.h"
+
+namespace previews {
+
+namespace {
+
+// Statistics are logged to UMA with this string as part of histogram name. They
+// can all be found under LevelDB.*.PreviewsHintCacheStore. Changing this needs
+// synchronization with histograms.xml.
+constexpr char kHintCacheStoreUMAClientName[] = "PreviewsHintCacheStore";
+
+// The folder where the data will be stored on disk.
+constexpr char kHintCacheStoreFolder[] = "previews_hint_cache_store";
+
+// The amount of data to build up in memory before converting to a sorted on-
+// disk file.
+constexpr size_t kDatabaseWriteBufferSizeBytes = 128 * 1024;
+
+// Delimiter that appears between the sections of a store entry key.
+//  Examples:
+//    "[EntryType::kMetadata]_[MetadataType]"
+//    "[EntryType::kComponentHint]_[component_version]_[host]"
+constexpr char kKeySectionDelimiter = '_';
+
+}  // namespace
+
+HintCacheLevelDBStore::HintCacheLevelDBStore(
+    const base::FilePath& database_dir,
+    scoped_refptr<base::SequencedTaskRunner> store_task_runner)
+    : HintCacheLevelDBStore(
+          database_dir,
+          leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
+              previews::proto::StoreEntry>(store_task_runner)) {}
+
+HintCacheLevelDBStore::HintCacheLevelDBStore(
+    const base::FilePath& database_dir,
+    std::unique_ptr<leveldb_proto::ProtoDatabase<previews::proto::StoreEntry>>
+        database)
+    : database_dir_(database_dir),
+      database_(std::move(database)),
+      status_(Status::kUninitialized),
+      component_data_update_in_flight_(false),
+      weak_ptr_factory_(this) {}
+
+HintCacheLevelDBStore::~HintCacheLevelDBStore() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void HintCacheLevelDBStore::Initialize(bool purge_existing_data,
+                                       base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  UpdateStatus(Status::kInitializing);
+
+  // Asynchronously initialize the store and run the callback once
+  // initialization completes. Initialization consists of the following steps:
+  //   1. Initialize the database.
+  //   2. If |purge_existing_data| is set to true, unconditionally purge
+  //      database and skip to step 6.
+  //   3. Otherwise, retrieve the metadata entries (e.g. Schema and Component).
+  //   4. If schema is the wrong version, purge database and skip to step 6.
+  //   5. Otherwise, load all hint entry keys.
+  //   6. Run callback after purging database or retrieving hint entry keys.
+
+  leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
+  options.write_buffer_size = kDatabaseWriteBufferSizeBytes;
+  base::FilePath hint_store_dir =
+      database_dir_.AppendASCII(kHintCacheStoreFolder);
+  database_->Init(kHintCacheStoreUMAClientName, hint_store_dir, options,
+                  base::BindOnce(&HintCacheLevelDBStore::OnDatabaseInitialized,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 purge_existing_data, std::move(callback)));
+}
+
+std::unique_ptr<HintCacheStore::ComponentUpdateData>
+HintCacheLevelDBStore::MaybeCreateComponentUpdateData(
+    const base::Version& version) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(version.IsValid());
+
+  if (!IsAvailable()) {
+    return nullptr;
+  }
+
+  // Component updates are only permitted when the update version is newer than
+  // the store's current one.
+  if (component_version_.has_value() && version <= component_version_) {
+    return nullptr;
+  }
+
+  // Create and return a LevelDBComponentUpdateData object. This object has
+  // hints from the component moved into it and organizes them in a format
+  // usable by the store; the object will returned to the store in
+  // UpdateComponentData().
+  return std::make_unique<LevelDBComponentUpdateData>(version);
+}
+
+void HintCacheLevelDBStore::UpdateComponentData(
+    std::unique_ptr<ComponentUpdateData> component_data,
+    base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(component_data);
+  DCHECK(!component_data_update_in_flight_);
+
+  if (!IsAvailable()) {
+    std::move(callback).Run();
+    return;
+  }
+
+  // If this isn't a newer component version than the store's current one, then
+  // the simply return. There's nothing to update.
+  if (component_version_.has_value() &&
+      component_data->version() <= component_version_) {
+    std::move(callback).Run();
+    return;
+  }
+
+  // Mark that there's now a component data update in-flight. While this is
+  // true, keys and hints will not be returned by the store.
+  component_data_update_in_flight_ = true;
+
+  // Set the component version prior to requesting the update. This ensures that
+  // a second update request for the same component version won't be allowed. In
+  // the case where the update fails, the store will become unavailable, so it's
+  // safe to treat component version in the update as the new one.
+  SetComponentVersion(component_data->version());
+
+  // The current keys are about to be removed, so clear out the keys available
+  // within the store. The keys will be populated after the component data
+  // update completes.
+  hint_entry_keys_.reset();
+
+  LevelDBComponentUpdateData* leveldb_component_data =
+      static_cast<LevelDBComponentUpdateData*>(component_data.get());
+
+  // Purge any component hints that are missing the new component version
+  // prefix.
+  EntryKeyPrefix retain_prefix =
+      GetComponentHintEntryKeyPrefix(component_version_.value());
+  EntryKeyPrefix filter_prefix = GetComponentHintEntryKeyPrefixWithoutVersion();
+
+  // Add the new component data and purge any old component hints from the db.
+  // After processing finishes, OnUpdateComponentData() is called, which loads
+  // the updated hint entry keys from the database.
+  database_->UpdateEntriesWithRemoveFilter(
+      std::move(leveldb_component_data->entries_to_save_),
+      base::BindRepeating(
+          [](const EntryKeyPrefix& retain_prefix,
+             const EntryKeyPrefix& filter_prefix, const std::string& key) {
+            return key.compare(0, retain_prefix.length(), retain_prefix) != 0 &&
+                   key.compare(0, filter_prefix.length(), filter_prefix) == 0;
+          },
+          retain_prefix, filter_prefix),
+      base::BindOnce(&HintCacheLevelDBStore::OnUpdateComponentData,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+bool HintCacheLevelDBStore::FindHintEntryKey(
+    const std::string& host_suffix,
+    EntryKey* out_hint_entry_key) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!component_version_.has_value() ||
+         component_hint_entry_key_prefix_ ==
+             GetComponentHintEntryKeyPrefix(component_version_.value()));
+
+  // Search for a component hint entry with the host suffix.
+  *out_hint_entry_key = component_hint_entry_key_prefix_ + host_suffix;
+  if (hint_entry_keys_ &&
+      hint_entry_keys_->find(*out_hint_entry_key) != hint_entry_keys_->end()) {
+    return true;
+  }
+  return false;
+}
+
+void HintCacheLevelDBStore::LoadHint(const EntryKey& hint_entry_key,
+                                     HintLoadedCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!IsAvailable()) {
+    std::move(callback).Run(hint_entry_key, nullptr);
+    return;
+  }
+
+  database_->GetEntry(hint_entry_key,
+                      base::BindOnce(&HintCacheLevelDBStore::OnLoadHint,
+                                     weak_ptr_factory_.GetWeakPtr(),
+                                     hint_entry_key, std::move(callback)));
+}
+
+HintCacheLevelDBStore::LevelDBComponentUpdateData::LevelDBComponentUpdateData(
+    const base::Version& version)
+    : ComponentUpdateData(version),
+      component_hint_entry_key_prefix_(GetComponentHintEntryKeyPrefix(version)),
+      entries_to_save_(std::make_unique<EntryVector>()) {
+  // Add a component metadata entry to the update data with the component's
+  // version.
+  previews::proto::StoreEntry metadata_component_entry;
+  metadata_component_entry.set_version(version.GetString());
+  entries_to_save_->emplace_back(
+      GetMetadataTypeEntryKey(MetadataType::kComponent),
+      std::move(metadata_component_entry));
+}
+
+HintCacheLevelDBStore::LevelDBComponentUpdateData::
+    ~LevelDBComponentUpdateData() = default;
+
+void HintCacheLevelDBStore::LevelDBComponentUpdateData::MoveHintIntoUpdateData(
+    optimization_guide::proto::Hint&& hint) {
+  // Add a component hint entry to the update data. To avoid any unnecessary
+  // copying, the hint is moved into proto::StoreEntry.
+  EntryKey hint_entry_key = component_hint_entry_key_prefix_ + hint.key();
+  previews::proto::StoreEntry entry_proto;
+  entry_proto.set_allocated_hint(
+      new optimization_guide::proto::Hint(std::move(hint)));
+  entries_to_save_->emplace_back(std::move(hint_entry_key),
+                                 std::move(entry_proto));
+}
+
+// static
+const char HintCacheLevelDBStore::kStoreSchemaVersion[] = "1";
+
+// static
+HintCacheLevelDBStore::EntryKeyPrefix
+HintCacheLevelDBStore::GetMetadataEntryKeyPrefix() {
+  return std::to_string(static_cast<int>(EntryType::kMetadata)) +
+         kKeySectionDelimiter;
+}
+
+// static
+HintCacheStore::EntryKey HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+    MetadataType metadata_type) {
+  return GetMetadataEntryKeyPrefix() +
+         std::to_string(static_cast<int>(metadata_type));
+}
+
+// static
+HintCacheLevelDBStore::EntryKeyPrefix
+HintCacheLevelDBStore::GetComponentHintEntryKeyPrefixWithoutVersion() {
+  return std::to_string(static_cast<int>(EntryType::kComponentHint)) +
+         kKeySectionDelimiter;
+}
+
+// static
+HintCacheLevelDBStore::EntryKeyPrefix
+HintCacheLevelDBStore::GetComponentHintEntryKeyPrefix(
+    const base::Version& component_version) {
+  return GetComponentHintEntryKeyPrefixWithoutVersion() +
+         component_version.GetString() + kKeySectionDelimiter;
+}
+
+void HintCacheLevelDBStore::UpdateStatus(Status new_status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Status::kUninitialized can only transition to Status::kInitializing.
+  DCHECK(status_ != Status::kUninitialized ||
+         new_status == Status::kInitializing);
+  // Status::kInitializing can only transition to Status::kAvailable or
+  // Status::kFailed.
+  DCHECK(status_ != Status::kInitializing || new_status == Status::kAvailable ||
+         new_status == Status::kFailed);
+  // Status::kAvailable can only transition to kStatus::Failed.
+  DCHECK(status_ != Status::kAvailable || new_status == Status::kFailed);
+  // The status can never transition from Status::kFailed.
+  DCHECK(status_ != Status::kFailed || new_status == Status::kFailed);
+
+  // If the database transitions into a failed state from a non-failed state,
+  // then fully destroy it. This ensures that it'll have a clean state the next
+  // time it is created.
+  bool destroy_database =
+      status_ != Status::kFailed && new_status == Status::kFailed;
+
+  status_ = new_status;
+
+  if (destroy_database) {
+    database_->Destroy(
+        base::BindOnce(&HintCacheLevelDBStore::OnDatabaseDestroyed,
+                       weak_ptr_factory_.GetWeakPtr()));
+    ClearComponentVersion();
+    hint_entry_keys_.reset();
+  }
+}
+
+bool HintCacheLevelDBStore::IsAvailable() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return status_ == Status::kAvailable;
+}
+
+void HintCacheLevelDBStore::PurgeDatabase(base::OnceClosure callback) {
+  // When purging the database, update the schema version to the current one.
+  EntryKey schema_entry_key = GetMetadataTypeEntryKey(MetadataType::kSchema);
+  previews::proto::StoreEntry schema_entry;
+  schema_entry.set_version(kStoreSchemaVersion);
+
+  auto entries_to_save = std::make_unique<EntryVector>();
+  entries_to_save->emplace_back(schema_entry_key, schema_entry);
+
+  database_->UpdateEntriesWithRemoveFilter(
+      std::move(entries_to_save),
+      base::BindRepeating(
+          [](const std::string& schema_entry_key, const std::string& key) {
+            return key.compare(0, schema_entry_key.length(),
+                               schema_entry_key) != 0;
+          },
+          schema_entry_key),
+      base::BindOnce(&HintCacheLevelDBStore::OnPurgeDatabase,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void HintCacheLevelDBStore::SetComponentVersion(
+    const base::Version& component_version) {
+  DCHECK(component_version.IsValid());
+  component_version_ = component_version;
+  component_hint_entry_key_prefix_ =
+      GetComponentHintEntryKeyPrefix(component_version_.value());
+}
+
+void HintCacheLevelDBStore::ClearComponentVersion() {
+  component_version_.reset();
+  component_hint_entry_key_prefix_.clear();
+}
+
+void HintCacheLevelDBStore::MaybeLoadHintEntryKeys(base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If the database is unavailable or if there's an in-flight component data
+  // update, then don't load the hint keys. Simply run the callback.
+  if (!IsAvailable() || component_data_update_in_flight_) {
+    std::move(callback).Run();
+    return;
+  }
+
+  // Create a new KeySet object. This is populated by the store's keys as the
+  // filter is run with each key on the DB's background thread. The filter
+  // itself always returns false, ensuring that no entries are ever actually
+  // loaded by the DB. Ownership of the KeySet is passed into the
+  // LoadKeysAndEntriesCallback callback, guaranteeing that the KeySet has a
+  // lifespan longer than the filter calls.
+  std::unique_ptr<EntryKeySet> hint_entry_keys(std::make_unique<EntryKeySet>());
+  EntryKeySet* raw_hint_entry_keys_pointer = hint_entry_keys.get();
+  database_->LoadKeysAndEntriesWithFilter(
+      base::BindRepeating(
+          [](EntryKeySet* hint_entry_keys, const std::string& filter_prefix,
+             const std::string& entry_key) {
+            if (entry_key.compare(0, filter_prefix.length(), filter_prefix) !=
+                0) {
+              hint_entry_keys->insert(entry_key);
+            }
+            return false;
+          },
+          raw_hint_entry_keys_pointer, GetMetadataEntryKeyPrefix()),
+      base::BindOnce(&HintCacheLevelDBStore::OnLoadHintEntryKeys,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(hint_entry_keys),
+                     std::move(callback)));
+}
+
+size_t HintCacheLevelDBStore::GetHintEntryKeyCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return hint_entry_keys_ ? hint_entry_keys_->size() : 0;
+}
+
+void HintCacheLevelDBStore::OnDatabaseInitialized(bool purge_existing_data,
+                                                  base::OnceClosure callback,
+                                                  bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!success) {
+    UpdateStatus(Status::kFailed);
+    std::move(callback).Run();
+    return;
+  }
+
+  // If initialization is set to purge all existing data, then simply trigger
+  // the purge and return. There's no need to load metadata entries that'll
+  // immediately be purged.
+  if (purge_existing_data) {
+    PurgeDatabase(std::move(callback));
+    return;
+  }
+
+  // Load all entries from the DB with the metadata key prefix.
+  database_->LoadKeysAndEntriesWithFilter(
+      leveldb_proto::LevelDB::KeyFilter(), leveldb::ReadOptions(),
+      GetMetadataEntryKeyPrefix(),
+      base::BindOnce(&HintCacheLevelDBStore::OnLoadMetadata,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void HintCacheLevelDBStore::OnDatabaseDestroyed(bool /*success*/) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void HintCacheLevelDBStore::OnLoadMetadata(
+    base::OnceClosure callback,
+    bool success,
+    std::unique_ptr<EntryMap> metadata_entries) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(metadata_entries);
+
+  if (!success) {
+    UpdateStatus(Status::kFailed);
+    std::move(callback).Run();
+    return;
+  }
+
+  // If the schema version in the DB is not the current version, then purge the
+  // database.
+  auto schema_entry =
+      metadata_entries->find(GetMetadataTypeEntryKey(MetadataType::kSchema));
+  if (schema_entry == metadata_entries->end() ||
+      !schema_entry->second.has_version() ||
+      schema_entry->second.version() != kStoreSchemaVersion) {
+    PurgeDatabase(std::move(callback));
+    return;
+  }
+
+  // If the component metadata entry exists, then use it to set the component
+  // version.
+  auto component_entry =
+      metadata_entries->find(GetMetadataTypeEntryKey(MetadataType::kComponent));
+  if (component_entry != metadata_entries->end()) {
+    DCHECK(component_entry->second.has_version());
+    SetComponentVersion(base::Version(component_entry->second.version()));
+  }
+
+  UpdateStatus(Status::kAvailable);
+  MaybeLoadHintEntryKeys(std::move(callback));
+}
+
+void HintCacheLevelDBStore::OnPurgeDatabase(base::OnceClosure callback,
+                                            bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // The database can only be purged during initialization.
+  DCHECK_EQ(status_, Status::kInitializing);
+
+  UpdateStatus(success ? Status::kAvailable : Status::kFailed);
+  std::move(callback).Run();
+}
+
+void HintCacheLevelDBStore::OnUpdateComponentData(base::OnceClosure callback,
+                                                  bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(component_data_update_in_flight_);
+
+  component_data_update_in_flight_ = false;
+  if (!success) {
+    UpdateStatus(Status::kFailed);
+    std::move(callback).Run();
+    return;
+  }
+  MaybeLoadHintEntryKeys(std::move(callback));
+}
+
+void HintCacheLevelDBStore::OnLoadHintEntryKeys(
+    std::unique_ptr<EntryKeySet> hint_entry_keys,
+    base::OnceClosure callback,
+    bool success,
+    std::unique_ptr<EntryMap> /*unused*/) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!hint_entry_keys_);
+
+  if (!success) {
+    UpdateStatus(Status::kFailed);
+    std::move(callback).Run();
+    return;
+  }
+
+  // If the store was set to unavailable after the request was started, or if
+  // there's an in-flight component data update, which means the keys are about
+  // to be invalidated, then the loaded keys should not be considered valid.
+  // Reset the keys so that they are cleared.
+  if (!IsAvailable() || component_data_update_in_flight_) {
+    hint_entry_keys.reset();
+  }
+
+  hint_entry_keys_ = std::move(hint_entry_keys);
+  std::move(callback).Run();
+}
+
+void HintCacheLevelDBStore::OnLoadHint(
+    const std::string& entry_key,
+    HintLoadedCallback callback,
+    bool success,
+    std::unique_ptr<previews::proto::StoreEntry> entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If either the request failed, the store was set to unavailable after the
+  // request was started, or there's an in-flight component data update, which
+  // means the entry is about to be invalidated, then the loaded hint should not
+  // be considered valid. Reset the entry so that no hint is returned to the
+  // requester.
+  if (!success || !IsAvailable() || component_data_update_in_flight_) {
+    entry.reset();
+  }
+
+  // If the hint exists, release it into the Hint unique_ptr contained in the
+  // callback. This eliminates the need for any copies of the entry's hint.
+  std::unique_ptr<optimization_guide::proto::Hint> loaded_hint(
+      entry && entry->has_hint() ? entry->release_hint() : nullptr);
+  std::move(callback).Run(entry_key, std::move(loaded_hint));
+}
+
+}  // namespace previews
diff --git a/components/previews/content/hint_cache_leveldb_store.h b/components/previews/content/hint_cache_leveldb_store.h
new file mode 100644
index 0000000..2dd9bc1
--- /dev/null
+++ b/components/previews/content/hint_cache_leveldb_store.h
@@ -0,0 +1,276 @@
+// 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.
+
+#ifndef COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_
+#define COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_
+
+#include "components/previews/content/hint_cache_store.h"
+
+#include <map>
+#include <string>
+#include <unordered_set>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/previews/content/proto/hint_cache.pb.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace previews {
+
+// Concrete implementation of HintCacheStore using LevelDB. All public calls in
+// the store must be made on the same thread.
+class HintCacheLevelDBStore : public HintCacheStore {
+ public:
+  using StoreEntryProtoDatabase =
+      leveldb_proto::ProtoDatabase<previews::proto::StoreEntry>;
+
+  HintCacheLevelDBStore(
+      const base::FilePath& database_dir,
+      scoped_refptr<base::SequencedTaskRunner> store_task_runner);
+  HintCacheLevelDBStore(const base::FilePath& database_dir,
+                        std::unique_ptr<StoreEntryProtoDatabase> database);
+  ~HintCacheLevelDBStore() override;
+
+  // HintCacheStore overrides:
+  void Initialize(bool purge_existing_data,
+                  base::OnceClosure callback) override;
+
+  std::unique_ptr<ComponentUpdateData> MaybeCreateComponentUpdateData(
+      const base::Version& version) const override;
+
+  void UpdateComponentData(std::unique_ptr<ComponentUpdateData> component_data,
+                           base::OnceClosure callback) override;
+
+  bool FindHintEntryKey(const std::string& host_suffix,
+                        EntryKey* out_hint_entry_key) const override;
+
+  void LoadHint(const EntryKey& hint_entry_key,
+                HintLoadedCallback callback) override;
+
+ private:
+  friend class HintCacheLevelDBStoreTest;
+
+  using EntryKeyPrefix = std::string;
+  using EntryKeySet = std::unordered_set<EntryKey>;
+
+  using EntryVector =
+      leveldb_proto::ProtoDatabase<previews::proto::StoreEntry>::KeyEntryVector;
+  using EntryMap = std::map<EntryKey, previews::proto::StoreEntry>;
+
+  // Status of the store. The store begins in kUninitialized, transitions to
+  // kInitializing after Initialize() is called, and transitions to kAvailable
+  // if initialization successfully completes. In the case where anything fails,
+  // the store transitions to kFailed, at which point it is fully purged and
+  // becomes unusable.
+  enum class Status {
+    kUninitialized,
+    kInitializing,
+    kAvailable,
+    kFailed,
+  };
+
+  // Entry types within the store appear at the start of the keys of entries.
+  // These values are converted into strings within the key: a key starting with
+  // "1_" signifies a metadata entry and one starting with "2_" signifies a
+  // component hint entry. Adding this to the start of the key allows the store
+  // to quickly perform operations on all entries of a specific key type. Given
+  // that entry type comparisons may be performed many times, the entry type
+  // string is kept as small as possible.
+  //  Example metadata entry type key:
+  //   "[EntryType::kMetadata]_[MetadataType::kSchema]"    ==> "1_1"
+  //  Example component hint entry type key:
+  //   "[EntryType::kComponentHint]_[component_version]_[host]"
+  //     ==> "2_55_foo.com"
+  // NOTE: The order and value of the existing entry types within the enum
+  // cannot be changed, but new types can be added to the end.
+  enum class EntryType {
+    kMetadata = 1,
+    kComponentHint = 2,
+  };
+
+  // Metadata types within the store. The metadata type appears at the end of
+  // metadata entry keys. These values are converted into strings within the
+  // key.
+  //  Example metadata type keys:
+  //   "[EntryType::kMetadata]_[MetadataType::kSchema]"    ==> "1_1"
+  //   "[EntryType::kMetadata]_[MetadataType::kComponent]" ==> "1_2"
+  // NOTE: The order and value of the existing metadata types within the enum
+  // cannot be changed, but new types can be added to the end.
+  enum class MetadataType {
+    kSchema = 1,
+    kComponent = 2,
+  };
+
+  // HintCacheLevelDBStore's concrete implementation of ComponentUpdateData.
+  // LevelDBComponentUpdateData is private within HintCacheLevelDBStore. All
+  // classes outside of HintCacheLevelDBStore can only interact with the
+  // ComponentUpdateData base class. LevelDBComponentUpdateData is created by
+  // HintCacheLevelDBStore when MaybeCreateComponentUpdateData() is called and
+  // used to update the store's component data during UpdateComponentData().
+  class LevelDBComponentUpdateData : public ComponentUpdateData {
+   public:
+    explicit LevelDBComponentUpdateData(const base::Version& version);
+    ~LevelDBComponentUpdateData() override;
+
+    // ComponentUpdateData overrides:
+    void MoveHintIntoUpdateData(
+        optimization_guide::proto::Hint&& hint) override;
+
+   private:
+    friend class HintCacheLevelDBStore;
+
+    // The prefix to add to the key of every component hint entry. It is set
+    // during construction, using the provided component version.
+    const EntryKeyPrefix component_hint_entry_key_prefix_;
+
+    // The vector of entries to save. This contains both the metadata component
+    // entry, which is created during construction, using the provided component
+    // version, and the hint entries from the component, which are individually
+    // moved into |entries_to_save_| during calls to MoveHintIntoUpdateData().
+    std::unique_ptr<EntryVector> entries_to_save_;
+  };
+
+  // Current schema version of the hint cache store. When this is changed,
+  // pre-existing store data from an earlier version is purged.
+  static const char kStoreSchemaVersion[];
+
+  // Returns prefix in the key of every metadata entry type entry: "1_"
+  static EntryKeyPrefix GetMetadataEntryKeyPrefix();
+
+  // Returns entry key for the specified metadata type entry: "1_[MetadataType]"
+  static EntryKey GetMetadataTypeEntryKey(MetadataType metadata_type);
+
+  // Returns prefix in the key of every component hint entry: "2_"
+  static EntryKeyPrefix GetComponentHintEntryKeyPrefixWithoutVersion();
+
+  // Returns prefix in the key of component hint entries for the specified
+  // component version: "2_[component_version]_"
+  static EntryKeyPrefix GetComponentHintEntryKeyPrefix(
+      const base::Version& component_version);
+
+  // Updates the status of the store to the specified value, validates the
+  // transition, and destroys the database in the case where the status
+  // transitions to Status::kFailed.
+  void UpdateStatus(Status new_status);
+
+  // Returns true if the current status is Status::kAvailable.
+  bool IsAvailable() const;
+
+  // Asynchronously purges all existing entries from the database and runs the
+  // callback after it completes. This should only be run during initialization.
+  void PurgeDatabase(base::OnceClosure callback);
+
+  // Updates |component_version_| and |component_hint_entry_key_prefix_| for
+  // the new component version. This must be called rather than directly
+  // modifying |component_version_|, as it ensures that both member variables
+  // are updated in sync.
+  void SetComponentVersion(const base::Version& component_version);
+
+  // Resets |component_version_| and |component_hint_entry_key_prefix_| back to
+  // their default state. Called after the database is destroyed.
+  void ClearComponentVersion();
+
+  // Asynchronously loads the hint entry keys from the store, populates
+  // |hint_entry_keys_| with them, and runs the provided callback after they
+  // finish loading. In the case where there is currently an in-flight component
+  // update, this does nothing, as the hint entry keys will be loaded after the
+  // component update completes.
+  void MaybeLoadHintEntryKeys(base::OnceClosure callback);
+
+  // Returns the total hint entry keys contained within the store.
+  size_t GetHintEntryKeyCount() const;
+
+  // Callback that runs after the database finishes being initialized. If
+  // |purge_existing_data| is true, then unconditionally purges the database;
+  // otherwise, triggers loading of the metadata.
+  void OnDatabaseInitialized(bool purge_existing_data,
+                             base::OnceClosure callback,
+                             bool success);
+
+  // Callback that is run after the database finishes being destroyed.
+  void OnDatabaseDestroyed(bool success);
+
+  // Callback that runs after the metadata finishes being loaded. This
+  // validates the schema version, sets the component version, and either purges
+  // the store (on a schema version mismatch) or loads all hint entry keys (on
+  // a schema version match).
+  void OnLoadMetadata(base::OnceClosure callback,
+                      bool success,
+                      std::unique_ptr<EntryMap> metadata_entries);
+
+  // Callback that runs after the database is purged during initialization.
+  void OnPurgeDatabase(base::OnceClosure callback, bool success);
+
+  // Callback that runs after the component data within the store is fully
+  // updated. If the update was successful, it attempts to load all of the hint
+  // entry keys contained within the database.
+  void OnUpdateComponentData(base::OnceClosure callback, bool success);
+
+  // Callback that runs after the hint entry keys are fully loaded. If there's
+  // currently an in-flight component update, then the hint entry keys will be
+  // loaded again after the component update completes, so the results are
+  // tossed; otherwise, |hint_entry_keys| is moved into |hint_entry_keys_|.
+  // Regardless of the outcome of loading the keys, the callback always runs.
+  void OnLoadHintEntryKeys(std::unique_ptr<EntryKeySet> hint_entry_keys,
+                           base::OnceClosure callback,
+                           bool success,
+                           std::unique_ptr<EntryMap> unused);
+
+  // Callback that runs after a hint entry is loaded from the database. If
+  // there's currently an in-flight component update, then the hint is about to
+  // be invalidated, so results are tossed; otherwise, the hint is released into
+  // the callback, allowing the caller to own the hint without copying it.
+  // Regardless of the success or failure of retrieving the key, the callback
+  // always runs (it simply runs with a nullptr on failure).
+  void OnLoadHint(const EntryKey& entry_key,
+                  HintLoadedCallback callback,
+                  bool success,
+                  std::unique_ptr<previews::proto::StoreEntry> entry);
+
+  // Path to the directory for the profile using this store.
+  const base::FilePath database_dir_;
+
+  // Proto database used by the store.
+  const std::unique_ptr<StoreEntryProtoDatabase> database_;
+
+  // The current status of the store. It should only be updated through
+  // UpdateStatus(), which validates status transitions and triggers
+  // accompanying logic.
+  Status status_;
+
+  // The current component version of the store. This should only be updated
+  // via SetComponentVersion(), which ensures that both |component_version_|
+  // and |component_hint_key_prefix_| are updated at the same time.
+  base::Optional<base::Version> component_version_;
+  // The current entry key prefix shared by all component hints containd within
+  // the store. While this could be generated on the fly using
+  // |component_version_|, it is retaind separately as an optimization, as it
+  // is needed often.
+  EntryKeyPrefix component_hint_entry_key_prefix_;
+
+  // If a component data update is in the middle of being processed; when this
+  // is true, keys and hints will not be returned by the store.
+  bool component_data_update_in_flight_;
+
+  // The keys of the hints available within the store.
+  std::unique_ptr<EntryKeySet> hint_entry_keys_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<HintCacheLevelDBStore> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(HintCacheLevelDBStore);
+};
+
+}  // namespace previews
+
+#endif  // COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_
diff --git a/components/previews/content/hint_cache_leveldb_store_unittest.cc b/components/previews/content/hint_cache_leveldb_store_unittest.cc
new file mode 100644
index 0000000..ccf076c
--- /dev/null
+++ b/components/previews/content/hint_cache_leveldb_store_unittest.cc
@@ -0,0 +1,814 @@
+// 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 "components/previews/content/hint_cache_leveldb_store.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using leveldb_proto::test::FakeDB;
+using testing::Mock;
+
+namespace previews {
+
+namespace {
+
+constexpr char kDefaultComponentVersion[] = "1.0.0";
+constexpr char kUpdateComponentVersion[] = "2.0.0";
+
+std::string GetHostSuffix(size_t id) {
+  // Host suffix alternates between two different domain types depending on
+  // whether the id is odd or even.
+  if (id % 2 == 0) {
+    return "domain" + std::to_string(id) + ".org";
+  } else {
+    return "different.domain" + std::to_string(id) + ".co.in";
+  }
+}
+
+enum class MetadataSchemaState {
+  kMissing,
+  kInvalid,
+  kValid,
+};
+
+}  // namespace
+
+class HintCacheLevelDBStoreTest : public testing::Test {
+ public:
+  using StoreEntry = previews::proto::StoreEntry;
+  using StoreEntryMap = std::map<HintCacheStore::EntryKey, StoreEntry>;
+
+  HintCacheLevelDBStoreTest() : db_(nullptr) {}
+
+  void TearDown() override { last_loaded_hint_.reset(); }
+
+  // Initializes the entries contained within the database on startup.
+  void SeedInitialData(
+      MetadataSchemaState state,
+      base::Optional<size_t> component_hint_count = base::Optional<size_t>()) {
+    db_store_.clear();
+
+    // Add a metadata schema entry if its state isn't kMissing. The version
+    // entry version is set to the store's current version if the state is
+    // kValid; otherwise, it's set to the invalid version of "0".
+    if (state == MetadataSchemaState::kValid) {
+      db_store_[HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+                    HintCacheLevelDBStore::MetadataType::kSchema)]
+          .set_version(HintCacheLevelDBStore::kStoreSchemaVersion);
+    } else if (state == MetadataSchemaState::kInvalid) {
+      db_store_[HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+                    HintCacheLevelDBStore::MetadataType::kSchema)]
+          .set_version("0");
+    }
+
+    // If the database is being seeded with component hints, it is indicated
+    // with a provided count. Add the component metadata with the default
+    // component version and then add the indicated number of component hints.
+    if (component_hint_count) {
+      db_store_[HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+                    HintCacheLevelDBStore::MetadataType::kComponent)]
+          .set_version(kDefaultComponentVersion);
+      HintCacheLevelDBStore::EntryKeyPrefix component_hint_key_prefix =
+          HintCacheLevelDBStore::GetComponentHintEntryKeyPrefix(
+              base::Version(kDefaultComponentVersion));
+      for (size_t i = 0; i < component_hint_count.value(); ++i) {
+        std::string host_suffix = GetHostSuffix(i);
+        StoreEntry& entry = db_store_[component_hint_key_prefix + host_suffix];
+        optimization_guide::proto::Hint* hint = entry.mutable_hint();
+        hint->set_key(host_suffix);
+        hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+        optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
+        page_hint->set_page_pattern("page pattern " + std::to_string(i));
+      }
+    }
+  }
+
+  // Moves the specified number of component hints into the update data.
+  void SeedUpdateData(HintCacheStore::ComponentUpdateData* update_data,
+                      size_t component_hint_count) {
+    for (size_t i = 0; i < component_hint_count; ++i) {
+      std::string host_suffix = GetHostSuffix(i);
+      optimization_guide::proto::Hint hint;
+      hint.set_key(host_suffix);
+      hint.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+      optimization_guide::proto::PageHint* page_hint = hint.add_page_hints();
+      page_hint->set_page_pattern("page pattern " + std::to_string(i));
+      update_data->MoveHintIntoUpdateData(std::move(hint));
+    }
+  }
+
+  void CreateDatabase() {
+    // Reset everything.
+    db_ = nullptr;
+    hint_store_.reset();
+
+    // Setup the fake db and the class under test.
+    auto db = std::make_unique<FakeDB<StoreEntry>>(&db_store_);
+    db_ = db.get();
+    hint_store_ = std::make_unique<HintCacheLevelDBStore>(base::FilePath(),
+                                                          std::move(db));
+  }
+
+  void InitializeDatabase(bool success, bool purge_existing_data = false) {
+    EXPECT_CALL(*this, OnInitialized());
+    hint_store()->Initialize(
+        purge_existing_data,
+        base::BindOnce(&HintCacheLevelDBStoreTest::OnInitialized,
+                       base::Unretained(this)));
+    // OnDatabaseInitialized callback
+    db()->InitCallback(success);
+  }
+
+  void InitializeStore(MetadataSchemaState state,
+                       bool purge_existing_data = false) {
+    InitializeDatabase(true /*=success*/, purge_existing_data);
+
+    if (purge_existing_data) {
+      // OnPurgeDatabase callback
+      db()->UpdateCallback(true);
+      return;
+    }
+
+    // OnLoadMetadata callback
+    db()->LoadCallback(true);
+    if (state == MetadataSchemaState::kValid) {
+      // OnLoadHintEntryKeys callback
+      db()->LoadCallback(true);
+    } else {
+      // OnPurgeDatabase callback
+      db()->UpdateCallback(true);
+    }
+  }
+
+  void UpdateComponentData(
+      std::unique_ptr<HintCacheStore::ComponentUpdateData> component_data,
+      bool update_success = true,
+      bool load_hint_entry_keys_success = true) {
+    EXPECT_CALL(*this, OnUpdateComponentData());
+    hint_store()->UpdateComponentData(
+        std::move(component_data),
+        base::BindOnce(&HintCacheLevelDBStoreTest::OnUpdateComponentData,
+                       base::Unretained(this)));
+    // OnUpdateComponentData callback
+    db()->UpdateCallback(update_success);
+    if (update_success) {
+      // OnLoadHintEntryKeys callback
+      db()->LoadCallback(load_hint_entry_keys_success);
+    }
+  }
+
+  bool IsMetadataSchemaEntryKeyPresent() const {
+    return IsKeyPresent(HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+        HintCacheLevelDBStore::MetadataType::kSchema));
+  }
+
+  // Verifies that the component metadata has the expected version and all
+  // expected component hints are present.
+  void ExpectComponentHintsPresent(const std::string& version,
+                                   int count) const {
+    const auto& metadata_entry =
+        db_store_.find(HintCacheLevelDBStore::GetMetadataTypeEntryKey(
+            HintCacheLevelDBStore::MetadataType::kComponent));
+    if (metadata_entry != db_store_.end()) {
+      EXPECT_EQ(metadata_entry->second.version(), version);
+    } else {
+      FAIL() << "No component metadata found";
+    }
+
+    HintCacheLevelDBStore::EntryKeyPrefix component_hint_entry_key_prefix =
+        HintCacheLevelDBStore::GetComponentHintEntryKeyPrefix(
+            base::Version(version));
+    for (int i = 0; i < count; ++i) {
+      std::string host_suffix = GetHostSuffix(i);
+      HintCacheStore::EntryKey hint_entry_key =
+          component_hint_entry_key_prefix + host_suffix;
+      const auto& hint_entry = db_store_.find(hint_entry_key);
+      if (hint_entry == db_store_.end()) {
+        FAIL() << "No entry found for component hint: " << hint_entry_key;
+        continue;
+      }
+
+      if (!hint_entry->second.has_hint()) {
+        FAIL() << "Component hint entry does not have hint: " << hint_entry_key;
+        continue;
+      }
+
+      EXPECT_EQ(hint_entry->second.hint().key(), host_suffix);
+    }
+  }
+
+  // Returns true if the data is present for the given key.
+  bool IsKeyPresent(const HintCacheStore::EntryKey& entry_key) const {
+    return db_store_.find(entry_key) != db_store_.end();
+  }
+
+  size_t GetDBStoreEntryCount() const { return db_store_.size(); }
+  size_t GetStoreHintEntryKeyCount() const {
+    return hint_store_->GetHintEntryKeyCount();
+  }
+
+  HintCacheStore* hint_store() { return hint_store_.get(); }
+  FakeDB<previews::proto::StoreEntry>* db() { return db_; }
+
+  const HintCacheStore::EntryKey& last_loaded_hint_entry_key() const {
+    return last_loaded_hint_entry_key_;
+  }
+
+  optimization_guide::proto::Hint* last_loaded_hint() {
+    return last_loaded_hint_.get();
+  }
+
+  void OnHintLoaded(
+      const HintCacheStore::EntryKey& hint_entry_key,
+      std::unique_ptr<optimization_guide::proto::Hint> loaded_hint) {
+    last_loaded_hint_entry_key_ = hint_entry_key;
+    last_loaded_hint_ = std::move(loaded_hint);
+  }
+
+  MOCK_METHOD0(OnInitialized, void());
+  MOCK_METHOD0(OnUpdateComponentData, void());
+
+ private:
+  FakeDB<previews::proto::StoreEntry>* db_;
+  StoreEntryMap db_store_;
+  std::unique_ptr<HintCacheLevelDBStore> hint_store_;
+
+  HintCacheStore::EntryKey last_loaded_hint_entry_key_;
+  std::unique_ptr<optimization_guide::proto::Hint> last_loaded_hint_;
+
+  DISALLOW_COPY_AND_ASSIGN(HintCacheLevelDBStoreTest);
+};
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnInitializeWithNoInitialData) {
+  SeedInitialData(MetadataSchemaState::kMissing);
+  CreateDatabase();
+  InitializeDatabase(false /*=success*/);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnLoadMetadataWithNoInitialData) {
+  SeedInitialData(MetadataSchemaState::kMissing);
+  CreateDatabase();
+  InitializeDatabase(true /*=success*/);
+
+  // OnLoadMetadata callback
+  db()->LoadCallback(false);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnUpdateMetadataNoInitialData) {
+  SeedInitialData(MetadataSchemaState::kMissing);
+  CreateDatabase();
+
+  InitializeDatabase(true /*=success*/);
+
+  // OnLoadMetadata callback
+  db()->LoadCallback(true);
+  // OnPurgeDatabase callback
+  db()->UpdateCallback(false);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, InitializeFailedOnInitializeWithInitialData) {
+  SeedInitialData(MetadataSchemaState::kValid, 10);
+  CreateDatabase();
+  InitializeDatabase(false /*=success*/);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnLoadMetadataWithInitialData) {
+  SeedInitialData(MetadataSchemaState::kValid, 10);
+  CreateDatabase();
+  InitializeDatabase(true /*=success*/);
+
+  // OnLoadMetadata callback
+  db()->LoadCallback(false);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnUpdateMetadataWithInvalidSchemaEntry) {
+  SeedInitialData(MetadataSchemaState::kInvalid, 10);
+  CreateDatabase();
+  InitializeDatabase(true /*=success*/);
+
+  // OnLoadMetadata callback
+  db()->LoadCallback(true);
+  // OnPurgeDatabase callback
+  db()->UpdateCallback(false);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeFailedOnLoadHintEntryKeysWithInitialData) {
+  SeedInitialData(MetadataSchemaState::kValid, 10);
+  CreateDatabase();
+  InitializeDatabase(true /*=success*/);
+
+  // OnLoadMetadata callback
+  db()->LoadCallback(true);
+  // OnLoadHintEntryKeys callback
+  db()->LoadCallback(false);
+
+  // In the case where initialization fails, the store should be fully purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, InitializeSucceededWithoutSchemaEntry) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kMissing;
+  SeedInitialData(schema_state);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // The store should contain the schema metadata entry and nothing else.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+}
+
+TEST_F(HintCacheLevelDBStoreTest, InitializeSucceededWithInvalidSchemaEntry) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kInvalid;
+  SeedInitialData(schema_state);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // The store should contain the schema metadata entry and nothing else.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+}
+
+TEST_F(HintCacheLevelDBStoreTest, InitializeSucceededWithValidSchemaEntry) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // The store should contain the schema metadata entry and nothing else.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeSucceededWithInvalidSchemaEntryAndInitialData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kInvalid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // The store should contain the schema metadata entry and nothing else, as
+  // the initial component hints are all purged.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+}
+
+TEST_F(HintCacheLevelDBStoreTest, InitializeSucceededWithPurgeExistingData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state, true /*=purge_existing_data*/);
+
+  // The store should contain the schema metadata entry and nothing else.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       InitializeSucceededWithValidSchemaEntryAndInitialData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t component_hint_count = 10;
+  SeedInitialData(schema_state, component_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // The store should contain the schema metadata entry, the component metadata
+  // entry, and all of the initial component hints.
+  EXPECT_EQ(GetDBStoreEntryCount(),
+            static_cast<size_t>(component_hint_count + 2));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count);
+
+  EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
+  ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count);
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentUpdateDataFailsForUninitializedStore) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+
+  // ComponentUpdateData can't be created when the store isn't initialized.
+  EXPECT_FALSE(hint_store()->MaybeCreateComponentUpdateData(
+      base::Version(kUpdateComponentVersion)));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentUpdateDataFailsForEarlierVersion) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // No ComponentUpdateData should be created when the component version of the
+  // update is older than the store's component version.
+  EXPECT_FALSE(
+      hint_store()->MaybeCreateComponentUpdateData(base::Version("0.0.0")));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentUpdateDataFailsForCurrentVersion) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // No ComponentUpdateData should be created when the component version of the
+  // update is the same as the store's component version.
+  EXPECT_FALSE(hint_store()->MaybeCreateComponentUpdateData(
+      base::Version(kDefaultComponentVersion)));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentUpdateDataSucceedsWithNoPreexistingVersion) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // ComponentUpdateData should be created when there is no pre-existing
+  // component.
+  EXPECT_TRUE(hint_store()->MaybeCreateComponentUpdateData(
+      base::Version(kDefaultComponentVersion)));
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentUpdateDataSucceedsForNewerVersion) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // ComponentUpdateData should be created when the component version of the
+  // update is newer than the store's component version.
+  EXPECT_TRUE(hint_store()->MaybeCreateComponentUpdateData(
+      base::Version(kUpdateComponentVersion)));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, UpdateComponentDataUpdateEntriesFails) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), 5);
+
+  UpdateComponentData(std::move(update_data), false /*update_success*/);
+
+  // The store should be purged if the component data update fails.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, UpdateComponentDataGetKeysFails) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 10);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), 5);
+
+  UpdateComponentData(std::move(update_data), true /*update_success*/,
+                      false /*load_hints_keys_success*/);
+
+  // The store should be purged if loading the keys after the component update
+  // fails.
+  EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, UpdateComponentData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count = 5;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), update_hint_count);
+  UpdateComponentData(std::move(update_data));
+
+  // When the component update succeeds, the store should contain the schema
+  // metadata entry, the component metadata entry, and all of the update's
+  // component hints.
+  EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count);
+  ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       UpdateComponentDataAfterInitializationDataPurge) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count = 5;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state, true /*=purge_existing_data*/);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), update_hint_count);
+  UpdateComponentData(std::move(update_data));
+
+  // When the component update succeeds, the store should contain the schema
+  // metadata entry, the component metadata entry, and all of the update's
+  // component hints.
+  EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count);
+  ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
+}
+
+TEST_F(HintCacheLevelDBStoreTest,
+       CreateComponentDataWithAlreadyUpdatedVersionFails) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count = 5;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), update_hint_count);
+  UpdateComponentData(std::move(update_data));
+
+  // ComponentUpdateData should not be created for a second component update
+  // with the same version as the first component update.
+  EXPECT_FALSE(hint_store()->MaybeCreateComponentUpdateData(
+      base::Version(kUpdateComponentVersion)));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, UpdateComponentDataWithUpdatedVersionFails) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count_1 = 5;
+  size_t update_hint_count_2 = 15;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // Create two updates for the same component version with different counts.
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data_1 =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data_2 =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data_1);
+  SeedUpdateData(update_data_1.get(), update_hint_count_1);
+  ASSERT_TRUE(update_data_2);
+  SeedUpdateData(update_data_2.get(), update_hint_count_2);
+
+  // Update the component data with the same component version twice:
+  // first with |update_data_1| and then with |update_data_2|.
+  UpdateComponentData(std::move(update_data_1));
+
+  EXPECT_CALL(*this, OnUpdateComponentData());
+  hint_store()->UpdateComponentData(
+      std::move(update_data_2),
+      base::BindOnce(&HintCacheLevelDBStoreTest::OnUpdateComponentData,
+                     base::Unretained(this)));
+
+  // Verify that the store is populated with the component data from
+  // |update_data_1| and not |update_data_2|.
+  EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count_1 + 2);
+  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count_1);
+  ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count_1);
+}
+
+TEST_F(HintCacheLevelDBStoreTest, LoadHintOnUnavailableStore) {
+  size_t initial_hint_count = 10;
+  SeedInitialData(MetadataSchemaState::kValid, initial_hint_count);
+  CreateDatabase();
+
+  const HintCacheStore::EntryKey kInvalidEntryKey = "invalid";
+  hint_store()->LoadHint(
+      kInvalidEntryKey, base::BindOnce(&HintCacheLevelDBStoreTest::OnHintLoaded,
+                                       base::Unretained(this)));
+
+  // Verify that the OnHintLoaded callback runs when the store is unavailable
+  // and that both the key and the hint were correctly set in it.
+  EXPECT_EQ(last_loaded_hint_entry_key(), kInvalidEntryKey);
+  EXPECT_FALSE(last_loaded_hint());
+}
+
+TEST_F(HintCacheLevelDBStoreTest, LoadHintFailure) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t hint_count = 10;
+  SeedInitialData(schema_state, hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  const HintCacheStore::EntryKey kInvalidEntryKey = "invalid";
+  hint_store()->LoadHint(
+      kInvalidEntryKey, base::BindOnce(&HintCacheLevelDBStoreTest::OnHintLoaded,
+                                       base::Unretained(this)));
+
+  // OnLoadHint callback
+  db()->GetCallback(false);
+
+  // Verify that the OnHintLoaded callback runs when the store is unavailable
+  // and that both the key and the hint were correctly set in it.
+  EXPECT_EQ(last_loaded_hint_entry_key(), kInvalidEntryKey);
+  EXPECT_FALSE(last_loaded_hint());
+}
+
+TEST_F(HintCacheLevelDBStoreTest, LoadHintSuccessInitialData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t hint_count = 10;
+  SeedInitialData(schema_state, hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // Verify that all component hints in the initial data can successfully be
+  // loaded from the store.
+  for (size_t i = 0; i < hint_count; ++i) {
+    std::string host_suffix = GetHostSuffix(i);
+    HintCacheStore::EntryKey hint_entry_key;
+    if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+      FAIL() << "Hint entry not found for host suffix: " << host_suffix;
+      continue;
+    }
+
+    hint_store()->LoadHint(
+        hint_entry_key, base::BindOnce(&HintCacheLevelDBStoreTest::OnHintLoaded,
+                                       base::Unretained(this)));
+
+    // OnLoadHint callback
+    db()->GetCallback(true);
+
+    EXPECT_EQ(last_loaded_hint_entry_key(), hint_entry_key);
+    if (!last_loaded_hint()) {
+      FAIL() << "Loaded hint NULL for entry key: " << hint_entry_key;
+      continue;
+    }
+
+    EXPECT_EQ(last_loaded_hint()->key(), host_suffix);
+  }
+}
+
+TEST_F(HintCacheLevelDBStoreTest, LoadHintSuccessUpdateData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count = 5;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), update_hint_count);
+  UpdateComponentData(std::move(update_data));
+
+  // Verify that all component hints within a successful component update can
+  // be loaded from the store.
+  for (size_t i = 0; i < update_hint_count; ++i) {
+    std::string host_suffix = GetHostSuffix(i);
+    HintCacheStore::EntryKey hint_entry_key;
+    if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+      FAIL() << "Hint entry not found for host suffix: " << host_suffix;
+      continue;
+    }
+
+    hint_store()->LoadHint(
+        hint_entry_key, base::BindOnce(&HintCacheLevelDBStoreTest::OnHintLoaded,
+                                       base::Unretained(this)));
+
+    // OnLoadHint callback
+    db()->GetCallback(true);
+
+    EXPECT_EQ(last_loaded_hint_entry_key(), hint_entry_key);
+    if (!last_loaded_hint()) {
+      FAIL() << "Loaded hint NULL for entry key: " << hint_entry_key;
+      continue;
+    }
+
+    EXPECT_EQ(last_loaded_hint()->key(), host_suffix);
+  }
+}
+
+TEST_F(HintCacheLevelDBStoreTest, FindHintEntryKeyOnUnavailableStore) {
+  size_t initial_hint_count = 10;
+  SeedInitialData(MetadataSchemaState::kValid, initial_hint_count);
+  CreateDatabase();
+
+  std::string host_suffix = GetHostSuffix(0);
+  HintCacheStore::EntryKey hint_entry_key;
+
+  // Verify that hint entry keys can't be found when the store is unavailable.
+  EXPECT_FALSE(hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
+}
+
+TEST_F(HintCacheLevelDBStoreTest, FindHintEntryKeyInitialData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t hint_count = 10;
+  SeedInitialData(schema_state, hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  // Verify that all hints contained within the initial store data are reported
+  // as being found and hints that are not containd within the initial data are
+  // properly reported as not being found.
+  for (size_t i = 0; i < hint_count * 2; ++i) {
+    std::string host_suffix = GetHostSuffix(i);
+    HintCacheStore::EntryKey hint_entry_key;
+    bool success = hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
+    EXPECT_EQ(success, i < hint_count);
+  }
+}
+
+TEST_F(HintCacheLevelDBStoreTest, FindHintEntryKeyUpdateData) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  size_t initial_hint_count = 10;
+  size_t update_hint_count = 5;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_store()->MaybeCreateComponentUpdateData(
+          base::Version(kUpdateComponentVersion));
+  ASSERT_TRUE(update_data);
+  SeedUpdateData(update_data.get(), update_hint_count);
+  UpdateComponentData(std::move(update_data));
+
+  // Verify that all hints contained within the component update are reported
+  // by the store as being found and hints that are not containd within the
+  // component update are properly reported as not being found.
+  for (size_t i = 0; i < update_hint_count * 2; ++i) {
+    std::string host_suffix = GetHostSuffix(i);
+    HintCacheStore::EntryKey hint_entry_key;
+    bool success = hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
+    EXPECT_EQ(success, i < update_hint_count);
+  }
+}
+
+}  // namespace previews
diff --git a/components/previews/content/hint_cache_store.h b/components/previews/content/hint_cache_store.h
new file mode 100644
index 0000000..069a645
--- /dev/null
+++ b/components/previews/content/hint_cache_store.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_STORE_H_
+#define COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/version.h"
+
+namespace optimization_guide {
+namespace proto {
+class Hint;
+}  // namespace proto
+}  // namespace optimization_guide
+
+namespace previews {
+
+// Abstract base class for the HintCache backing store, which is responsible for
+// storing all hints that are locally available. While the HintCache itself may
+// retain some hints in a memory cache, all of its hints are initially loaded
+// asynchronously by the store. All calls to the HintCacheStore must be made
+// from the same thread.
+class HintCacheStore {
+ public:
+  using EntryKey = std::string;
+  using HintLoadedCallback = base::OnceCallback<void(
+      const std::string&,
+      std::unique_ptr<optimization_guide::proto::Hint>)>;
+
+  // Abstract base class for storing hint component update data. Concrete
+  // derived classes store the data in a form usable by their creating concrete
+  // HintCacheStore. The data itself is populated by moving component hints into
+  // it on a background thread; it is then used to update the store's component
+  // data on the UI thread. Each concrete HintCacheStore class must implement
+  // its own concrete ComponentUpdateData, which provides a store-specific
+  // implementation of MoveHintIntoUpdateData().
+  class ComponentUpdateData {
+   public:
+    explicit ComponentUpdateData(const base::Version& version)
+        : version_(version) {
+      DCHECK(version_.IsValid());
+    }
+    virtual ~ComponentUpdateData() = default;
+
+    const base::Version& version() const { return version_; }
+
+    // Pure virtual function for moving a hint into ComponentUpdateData. After
+    // MoveHintIntoUpdateData() is called, |hint| is no longer valid.
+    virtual void MoveHintIntoUpdateData(
+        optimization_guide::proto::Hint&& hint) = 0;
+
+   private:
+    // The component version of the update data.
+    base::Version version_;
+  };
+
+  HintCacheStore() = default;
+  virtual ~HintCacheStore() = default;
+
+  // Pure virtual function for initializing the store. What initialization
+  // entails is dependent upon the concrete store. If |purge_existing_data| is
+  // set to true, then the cache is purged during initialization and starts in
+  // a fresh state. When initialization completes, the provided callback is run
+  // asynchronously.
+  virtual void Initialize(bool purge_existing_data,
+                          base::OnceClosure callback) = 0;
+
+  // Pure virtual function for creating and returning a concrete
+  // ComponentUpdateData object. This object is used to collect hints within a
+  // component in a format usable by the concrete HintCacheStore on a background
+  // thread and is later returned to the store in UpdateComponentData(). The
+  // ComponentUpdateData object is only created when the provided component
+  // version is newer than the store's version, indicating fresh hints. If the
+  // component's version is not newer than the store's version, then no
+  // ComponentUpdateData is created and nullptr is returned. This prevents
+  // unnecessary processing of the component's hints by the caller.
+  std::unique_ptr<ComponentUpdateData> virtual MaybeCreateComponentUpdateData(
+      const base::Version& version) const = 0;
+
+  // Pure virtual function for updating the component data (both version and
+  // hints) contained within the store. When this is called, all pre-existing
+  // component data within the store is purged and only the new data is
+  // retained. After the store is fully updated with the new component data,
+  // the callback is run asynchronously
+  virtual void UpdateComponentData(
+      std::unique_ptr<ComponentUpdateData> component_data,
+      base::OnceClosure callback) = 0;
+
+  // Pure virtual function for finding a hint entry key associated with the
+  // specified host suffix. Returns true if a hint entry key is found, in which
+  // case |out_hint_entry_key| is populated with the key.
+  virtual bool FindHintEntryKey(const std::string& host_suffix,
+                                EntryKey* out_hint_entry_key) const = 0;
+
+  // Pure virtual function for loading the hint specified by |hint_entry_key|.
+  // After the load finishes, the hint data is passed to |callback|. In the case
+  // where the hint cannot be loaded, the callback is run with a nullptr.
+  // Depending on the load result, the callback may be synchronous or
+  // asynchronous.
+  virtual void LoadHint(const EntryKey& hint_entry_key,
+                        HintLoadedCallback callback) = 0;
+};
+
+}  // namespace previews
+
+#endif  // COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_STORE_H_
diff --git a/components/previews/content/hint_cache_unittest.cc b/components/previews/content/hint_cache_unittest.cc
index cf9ffbd2..759d605 100644
--- a/components/previews/content/hint_cache_unittest.cc
+++ b/components/previews/content/hint_cache_unittest.cc
@@ -8,7 +8,11 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/previews/content/hint_cache_leveldb_store.h"
 #include "components/previews/core/previews_experiments.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -17,27 +21,116 @@
 
 namespace {
 
+std::string GetHostDomainOrg(int index) {
+  return "host.domain" + std::to_string(index) + ".org";
+}
+
 class HintCacheTest : public testing::Test {
  public:
-  HintCacheTest() {}
+  HintCacheTest() : loaded_hint_(nullptr) {}
 
   ~HintCacheTest() override {}
 
-  void LoadCallback(const optimization_guide::proto::Hint& hint) {
-    loaded_hint_ = std::make_unique<optimization_guide::proto::Hint>(hint);
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+  void TearDown() override { DestroyHintCache(); }
+
+ protected:
+  // Creates and initializes the hint cache and hint cache store and waits for
+  // the callback indicating that initialization is complete.
+  void CreateAndInitializeHintCache(int memory_cache_size,
+                                    bool purge_existing_data = false) {
+    hint_cache_ = std::make_unique<HintCache>(
+        std::make_unique<HintCacheLevelDBStore>(
+            temp_dir_.GetPath(),
+            scoped_task_environment_.GetMainThreadTaskRunner()),
+        memory_cache_size);
+    is_store_initialized_ = false;
+    hint_cache_->Initialize(purge_existing_data,
+                            base::BindOnce(&HintCacheTest::OnStoreInitialized,
+                                           base::Unretained(this)));
+    while (!is_store_initialized_) {
+      RunUntilIdle();
+    }
+  }
+
+  void DestroyHintCache() {
+    hint_cache_.reset();
+    loaded_hint_ = nullptr;
+    is_store_initialized_ = false;
+    is_component_data_updated_ = false;
+    on_load_hint_callback_called = false;
+
+    RunUntilIdle();
+  }
+
+  HintCache* hint_cache() { return hint_cache_.get(); }
+
+  // Updates the cache with |component_data| and waits for callback indicating
+  // that the update is complete.
+  void UpdateComponentData(
+      std::unique_ptr<HintCacheStore::ComponentUpdateData> component_data) {
+    is_component_data_updated_ = false;
+    hint_cache_->UpdateComponentData(
+        std::move(component_data),
+        base::BindOnce(&HintCacheTest::OnUpdateComponentData,
+                       base::Unretained(this)));
+    while (!is_component_data_updated_) {
+      RunUntilIdle();
+    }
+  }
+
+  // Loads hint for the specified host from the cache and waits for callback
+  // indicating that loading the hint is complete.
+  void LoadHint(const std::string& host) {
+    on_load_hint_callback_called = false;
+    loaded_hint_ = nullptr;
+    hint_cache_->LoadHint(host, base::BindOnce(&HintCacheTest::OnLoadHint,
+                                               base::Unretained(this)));
+    while (!on_load_hint_callback_called) {
+      RunUntilIdle();
+    }
   }
 
   const optimization_guide::proto::Hint* GetLoadedHint() const {
-    return loaded_hint_.get();
+    return loaded_hint_;
   }
 
  private:
-  std::unique_ptr<optimization_guide::proto::Hint> loaded_hint_;
+  void RunUntilIdle() {
+    scoped_task_environment_.RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void OnStoreInitialized() { is_store_initialized_ = true; }
+  void OnUpdateComponentData() { is_component_data_updated_ = true; }
+  void OnLoadHint(const optimization_guide::proto::Hint* hint) {
+    on_load_hint_callback_called = true;
+    loaded_hint_ = hint;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::ScopedTempDir temp_dir_;
+
+  std::unique_ptr<HintCache> hint_cache_;
+  const optimization_guide::proto::Hint* loaded_hint_;
+
+  bool is_store_initialized_;
+  bool is_component_data_updated_;
+  bool on_load_hint_callback_called;
 
   DISALLOW_COPY_AND_ASSIGN(HintCacheTest);
 };
 
-TEST_F(HintCacheTest, TestMemoryCache) {
+TEST_F(HintCacheTest, ComponentUpdate) {
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version("2.0.0");
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version);
+  ASSERT_TRUE(update_data);
+
   optimization_guide::proto::Hint hint1;
   hint1.set_key("subdomain.domain.org");
   hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
@@ -48,51 +141,340 @@
   hint3.set_key("otherhost.subdomain.domain.org");
   hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
 
-  HintCache::Data data;
-  data.AddHint(hint1);
-  data.AddHint(hint2);
-  data.AddHint(hint3);
-  HintCache hint_cache(std::move(data));
+  update_data->MoveHintIntoUpdateData(std::move(hint1));
+  update_data->MoveHintIntoUpdateData(std::move(hint2));
+  update_data->MoveHintIntoUpdateData(std::move(hint3));
+
+  UpdateComponentData(std::move(update_data));
 
   // Not matched
-  EXPECT_FALSE(hint_cache.HasHint("domain.org"));
-  EXPECT_FALSE(hint_cache.HasHint("othersubdomain.domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org"));
 
   // Matched
-  EXPECT_TRUE(hint_cache.HasHint("otherhost.subdomain.domain.org"));
-  EXPECT_TRUE(hint_cache.HasHint("host.subdomain.domain.org"));
-  EXPECT_TRUE(hint_cache.HasHint("subhost.host.subdomain.domain.org"));
-
-  // Cached in memory
-  EXPECT_TRUE(hint_cache.IsHintLoaded("host.domain.org"));
-  EXPECT_TRUE(hint_cache.IsHintLoaded("otherhost.subdomain.domain.org"));
-  EXPECT_TRUE(hint_cache.IsHintLoaded("host.subdomain.domain.org"));
-  EXPECT_TRUE(hint_cache.IsHintLoaded("subhost.host.subdomain.domain.org"));
-
-  // Matched key
-  EXPECT_EQ(hint2.key(), hint_cache.GetHint("host.domain.org")->key());
-  EXPECT_EQ(hint3.key(),
-            hint_cache.GetHint("otherhost.subdomain.domain.org")->key());
-  EXPECT_EQ(hint1.key(),
-            hint_cache.GetHint("host.subdomain.domain.org")->key());
+  EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org"));
 }
 
-TEST_F(HintCacheTest, TestMemoryCacheLoadCallback) {
+TEST_F(HintCacheTest, ComponentUpdateWithSameVersionIgnored) {
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version("2.0.0");
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version);
+  ASSERT_TRUE(update_data);
+
+  UpdateComponentData(std::move(update_data));
+
+  EXPECT_FALSE(hint_cache()->MaybeCreateComponentUpdateData(version));
+}
+
+TEST_F(HintCacheTest, ComponentUpdateWithEarlierVersionIgnored) {
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version_1("1.0.0");
+  base::Version version_2("2.0.0");
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version_2);
+  ASSERT_TRUE(update_data);
+
+  UpdateComponentData(std::move(update_data));
+
+  EXPECT_FALSE(hint_cache()->MaybeCreateComponentUpdateData(version_1));
+}
+
+TEST_F(HintCacheTest, ComponentUpdateWithLaterVersionProcessed) {
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version_1("1.0.0");
+  base::Version version_2("2.0.0");
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data_1 =
+      hint_cache()->MaybeCreateComponentUpdateData(version_1);
+  ASSERT_TRUE(update_data_1);
+
   optimization_guide::proto::Hint hint1;
   hint1.set_key("subdomain.domain.org");
   hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Hint hint2;
+  hint2.set_key("host.domain.org");
+  hint2.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Hint hint3;
+  hint3.set_key("otherhost.subdomain.domain.org");
+  hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
 
-  HintCache::Data data;
-  data.AddHint(hint1);
-  HintCache hint_cache(std::move(data));
+  update_data_1->MoveHintIntoUpdateData(std::move(hint1));
+  update_data_1->MoveHintIntoUpdateData(std::move(hint2));
+  update_data_1->MoveHintIntoUpdateData(std::move(hint3));
 
-  EXPECT_TRUE(hint_cache.IsHintLoaded("host.subdomain.domain.org"));
-  hint_cache.LoadHint(
-      "host.subdomain.domain.org",
-      base::BindOnce(&HintCacheTest::LoadCallback, base::Unretained(this)));
+  UpdateComponentData(std::move(update_data_1));
+
+  // Not matched
+  EXPECT_FALSE(hint_cache()->HasHint("domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org"));
+
+  // Matched
+  EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org"));
+
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data_2 =
+      hint_cache()->MaybeCreateComponentUpdateData(version_2);
+  ASSERT_TRUE(update_data_2);
+
+  optimization_guide::proto::Hint hint4;
+  hint4.set_key("subdomain.domain2.org");
+  hint4.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Hint hint5;
+  hint5.set_key("host.domain2.org");
+  hint5.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Hint hint6;
+  hint6.set_key("otherhost.subdomain.domain2.org");
+  hint6.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+  update_data_2->MoveHintIntoUpdateData(std::move(hint4));
+  update_data_2->MoveHintIntoUpdateData(std::move(hint5));
+  update_data_2->MoveHintIntoUpdateData(std::move(hint6));
+
+  UpdateComponentData(std::move(update_data_2));
+
+  // Not matched
+  EXPECT_FALSE(hint_cache()->HasHint("otherhost.subdomain.domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("host.subdomain.domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("subhost.host.subdomain.domain.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("domain2.org"));
+  EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain2.org"));
+
+  // Matched
+  EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain2.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain2.org"));
+  EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain2.org"));
+}
+
+TEST_F(HintCacheTest, ComponentHintsAvailableAfterRestart) {
+  for (int i = 0; i < 2; ++i) {
+    const int kMemoryCacheSize = 5;
+    CreateAndInitializeHintCache(kMemoryCacheSize,
+                                 false /*=purge_existing_data*/);
+
+    base::Version version("2.0.0");
+
+    std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+        hint_cache()->MaybeCreateComponentUpdateData(version);
+    if (i == 0) {
+      ASSERT_TRUE(update_data);
+
+      optimization_guide::proto::Hint hint1;
+      hint1.set_key("subdomain.domain.org");
+      hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+      optimization_guide::proto::Hint hint2;
+      hint2.set_key("host.domain.org");
+      hint2.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+      optimization_guide::proto::Hint hint3;
+      hint3.set_key("otherhost.subdomain.domain.org");
+      hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+      update_data->MoveHintIntoUpdateData(std::move(hint1));
+      update_data->MoveHintIntoUpdateData(std::move(hint2));
+      update_data->MoveHintIntoUpdateData(std::move(hint3));
+
+      UpdateComponentData(std::move(update_data));
+    } else {
+      EXPECT_FALSE(update_data);
+    }
+
+    // Not matched
+    EXPECT_FALSE(hint_cache()->HasHint("domain.org"));
+    EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org"));
+
+    // Matched
+    EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org"));
+    EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org"));
+    EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org"));
+
+    DestroyHintCache();
+  }
+}
+
+TEST_F(HintCacheTest, ComponentHintsUpdatableAfterRestartWithPurge) {
+  for (int i = 0; i < 2; ++i) {
+    const int kMemoryCacheSize = 5;
+    CreateAndInitializeHintCache(kMemoryCacheSize,
+                                 true /*=purge_existing_data*/);
+
+    base::Version version("2.0.0");
+
+    std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+        hint_cache()->MaybeCreateComponentUpdateData(version);
+    ASSERT_TRUE(update_data);
+
+    optimization_guide::proto::Hint hint1;
+    hint1.set_key("subdomain.domain.org");
+    hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+    optimization_guide::proto::Hint hint2;
+    hint2.set_key("host.domain.org");
+    hint2.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+    optimization_guide::proto::Hint hint3;
+    hint3.set_key("otherhost.subdomain.domain.org");
+    hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+    update_data->MoveHintIntoUpdateData(std::move(hint1));
+    update_data->MoveHintIntoUpdateData(std::move(hint2));
+    update_data->MoveHintIntoUpdateData(std::move(hint3));
+
+    UpdateComponentData(std::move(update_data));
+
+    // Not matched
+    EXPECT_FALSE(hint_cache()->HasHint("domain.org"));
+    EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org"));
+
+    // Matched
+    EXPECT_TRUE(hint_cache()->HasHint("otherhost.subdomain.domain.org"));
+    EXPECT_TRUE(hint_cache()->HasHint("host.subdomain.domain.org"));
+    EXPECT_TRUE(hint_cache()->HasHint("subhost.host.subdomain.domain.org"));
+
+    DestroyHintCache();
+  }
+}
+
+TEST_F(HintCacheTest, ComponentHintsNotRetainedAfterRestartWithPurge) {
+  for (int i = 0; i < 2; ++i) {
+    const int kMemoryCacheSize = 5;
+    CreateAndInitializeHintCache(kMemoryCacheSize,
+                                 true /*=purge_existing_data*/);
+
+    base::Version version("2.0.0");
+
+    std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+        hint_cache()->MaybeCreateComponentUpdateData(version);
+    if (i == 0) {
+      ASSERT_TRUE(update_data);
+
+      optimization_guide::proto::Hint hint1;
+      hint1.set_key("subdomain.domain.org");
+      hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+      optimization_guide::proto::Hint hint2;
+      hint2.set_key("host.domain.org");
+      hint2.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+      optimization_guide::proto::Hint hint3;
+      hint3.set_key("otherhost.subdomain.domain.org");
+      hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+      update_data->MoveHintIntoUpdateData(std::move(hint1));
+      update_data->MoveHintIntoUpdateData(std::move(hint2));
+      update_data->MoveHintIntoUpdateData(std::move(hint3));
+
+      UpdateComponentData(std::move(update_data));
+    } else {
+      EXPECT_TRUE(update_data);
+    }
+
+    // Not matched
+    EXPECT_FALSE(hint_cache()->HasHint("domain.org"));
+    EXPECT_FALSE(hint_cache()->HasHint("othersubdomain.domain.org"));
+
+    // Maybe matched
+    bool should_match = (i == 0);
+    EXPECT_EQ(hint_cache()->HasHint("otherhost.subdomain.domain.org"),
+              should_match);
+    EXPECT_EQ(hint_cache()->HasHint("host.subdomain.domain.org"), should_match);
+    EXPECT_EQ(hint_cache()->HasHint("subhost.host.subdomain.domain.org"),
+              should_match);
+
+    DestroyHintCache();
+  }
+}
+
+TEST_F(HintCacheTest, TestMemoryCacheLeastRecentlyUsedPurge) {
+  const int kTestHintCount = 10;
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version("1.0.0");
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version);
+  ASSERT_TRUE(update_data);
+
+  for (int i = 0; i < kTestHintCount; ++i) {
+    optimization_guide::proto::Hint hint;
+    hint.set_key(GetHostDomainOrg(i));
+    hint.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+    update_data->MoveHintIntoUpdateData(std::move(hint));
+  }
+
+  UpdateComponentData(std::move(update_data));
+
+  for (int i = kTestHintCount - 1; i >= 0; --i) {
+    std::string host = GetHostDomainOrg(i);
+    EXPECT_TRUE(hint_cache()->HasHint(host));
+    LoadHint(host);
+    ASSERT_TRUE(GetLoadedHint());
+    EXPECT_EQ(GetLoadedHint()->key(), host);
+  }
+
+  for (int i = 0; i < kTestHintCount; ++i) {
+    std::string host = GetHostDomainOrg(i);
+    if (i < kMemoryCacheSize) {
+      ASSERT_TRUE(hint_cache()->GetHintIfLoaded(host));
+      EXPECT_EQ(GetHostDomainOrg(i),
+                hint_cache()->GetHintIfLoaded(host)->key());
+    } else {
+      EXPECT_FALSE(hint_cache()->GetHintIfLoaded(host));
+    }
+    EXPECT_TRUE(hint_cache()->HasHint(host));
+  }
+}
+
+TEST_F(HintCacheTest, TestHostNotInCache) {
+  const int kTestHintCount = 10;
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version("1.0.0");
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version);
+  ASSERT_TRUE(update_data);
+
+  for (int i = 0; i < kTestHintCount; ++i) {
+    optimization_guide::proto::Hint hint;
+    hint.set_key(GetHostDomainOrg(i));
+    hint.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+    update_data->MoveHintIntoUpdateData(std::move(hint));
+  }
+
+  UpdateComponentData(std::move(update_data));
+
+  EXPECT_FALSE(hint_cache()->HasHint(GetHostDomainOrg(kTestHintCount)));
+}
+
+TEST_F(HintCacheTest, TestMemoryCacheLoadCallback) {
+  const int kMemoryCacheSize = 5;
+  CreateAndInitializeHintCache(kMemoryCacheSize);
+
+  base::Version version("1.0.0");
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> update_data =
+      hint_cache()->MaybeCreateComponentUpdateData(version);
+  ASSERT_TRUE(update_data);
+
+  std::string hint_key = "subdomain.domain.org";
+  optimization_guide::proto::Hint hint;
+  hint.set_key(hint_key);
+  hint.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  update_data->MoveHintIntoUpdateData(std::move(hint));
+
+  UpdateComponentData(std::move(update_data));
+
+  EXPECT_FALSE(hint_cache()->GetHintIfLoaded("host.subdomain.domain.org"));
+  LoadHint("host.subdomain.domain.org");
+  EXPECT_TRUE(hint_cache()->GetHintIfLoaded("host.subdomain.domain.org"));
 
   EXPECT_TRUE(GetLoadedHint());
-  EXPECT_EQ(hint1.key(), GetLoadedHint()->key());
+  EXPECT_EQ(hint_key, GetLoadedHint()->key());
 }
 
 }  // namespace
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc
index 2a8dffd..ce9b27d 100644
--- a/components/previews/content/previews_decider_impl.cc
+++ b/components/previews/content/previews_decider_impl.cc
@@ -92,6 +92,28 @@
   return false;
 }
 
+// Returns true if ECT should be checked for |type| only at the commit time. If
+// true is returned, then ECT need not be checked at the navigation time.
+bool CheckForUnknownECTOnlyAtCommitTime(PreviewsType type) {
+  switch (type) {
+    case PreviewsType::NOSCRIPT:
+    case PreviewsType::RESOURCE_LOADING_HINTS:
+      return true;
+    case PreviewsType::LOFI:
+    case PreviewsType::LITE_PAGE_REDIRECT:
+    case PreviewsType::OFFLINE:
+    case PreviewsType::LITE_PAGE:
+      return false;
+    case PreviewsType::NONE:
+    case PreviewsType::UNSPECIFIED:
+    case PreviewsType::DEPRECATED_AMP_REDIRECTION:
+    case PreviewsType::LAST:
+      break;
+  }
+  NOTREACHED();
+  return false;
+}
+
 bool IsPreviewsBlacklistIgnoredViaFlag() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kIgnorePreviewsBlacklist);
@@ -289,7 +311,8 @@
   // perform its own ECT check and for previews with hints because the hints may
   // specify variable ECT thresholds for slow page hints.
   if (!is_drp_server_preview && !ShouldCheckOptimizationHints(type)) {
-    if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
+    if (effective_connection_type_ == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN &&
+        !CheckForUnknownECTOnlyAtCommitTime(type)) {
       return PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE;
     }
     passed_reasons->push_back(
@@ -337,7 +360,8 @@
 
 bool PreviewsDeciderImpl::LoadPageHints(const GURL& url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return previews_opt_guide_->MaybeLoadOptimizationHints(url);
+  return previews_opt_guide_->MaybeLoadOptimizationHints(url,
+                                                         base::DoNothing());
 }
 
 bool PreviewsDeciderImpl::GetResourceLoadingHints(
@@ -427,20 +451,8 @@
         PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER);
   }
 
-  // For NoScript, ensure it is whitelisted for this request.
-  if (type == PreviewsType::NOSCRIPT) {
-    net::EffectiveConnectionType ect_threshold =
-        params::GetECTThresholdForPreview(type);
-    if (!previews_opt_guide_->IsWhitelisted(previews_data, url, type,
-                                            &ect_threshold)) {
-      return PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER;
-    }
-    passed_reasons->push_back(
-        PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER);
-  }
-
-  // Note: allow ResourceLoadingHints since the guide is available. Hints may
-  // need to be loaded from it for commit time detail check.
+  // Note: allow NoScript and ResourceLoadingHints since the guide is available.
+  // Page hints may need to be loaded from it for commit time detail check.
 
   return PreviewsEligibilityReason::ALLOWED;
 }
@@ -475,26 +487,31 @@
   // connection type as offline when the Android APIs incorrectly return device
   // connectivity as null. See https://crbug.com/838969. So, we do not trigger
   // previews when |ect| is net::EFFECTIVE_CONNECTION_TYPE_OFFLINE.
+  net::EffectiveConnectionType ect = previews_data->navigation_ect();
+  if (CheckForUnknownECTOnlyAtCommitTime(type) &&
+      ect == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
+    // Update the |ect| to the current value.
+    ect = effective_connection_type_;
+  }
+
   if (ShouldCheckForUnknownECT(params::GetSessionMaxECTThreshold()) &&
-      previews_data->navigation_ect() ==
-          net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
+      ect == net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
     return PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE;
   }
   passed_reasons->push_back(
       PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE);
 
-  if (previews_data->navigation_ect() ==
-      net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
+  if (ect == net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
     return PreviewsEligibilityReason::DEVICE_OFFLINE;
   }
   passed_reasons->push_back(PreviewsEligibilityReason::DEVICE_OFFLINE);
 
-  if (previews_data->navigation_ect() > ect_threshold) {
+  if (ect > ect_threshold) {
     return PreviewsEligibilityReason::NETWORK_NOT_SLOW;
   }
   passed_reasons->push_back(PreviewsEligibilityReason::NETWORK_NOT_SLOW);
 
-  if (previews_data->navigation_ect() > params::GetSessionMaxECTThreshold()) {
+  if (ect > params::GetSessionMaxECTThreshold()) {
     return PreviewsEligibilityReason::NETWORK_NOT_SLOW_FOR_SESSION;
   }
   passed_reasons->push_back(
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index 4434522..d87fbd0 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -15,6 +15,8 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/memory/ref_counted.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
@@ -133,8 +135,11 @@
  public:
   TestPreviewsOptimizationGuide(
       optimization_guide::OptimizationGuideService* optimization_guide_service,
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
-      : PreviewsOptimizationGuide(optimization_guide_service, ui_task_runner) {}
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+      const base::FilePath& test_path)
+      : PreviewsOptimizationGuide(optimization_guide_service,
+                                  ui_task_runner,
+                                  test_path) {}
   ~TestPreviewsOptimizationGuide() override {}
 
   // PreviewsOptimizationGuide:
@@ -357,6 +362,8 @@
     variations::testing::ClearAllVariationParams();
   }
 
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
   void TearDown() override { ui_service_.reset(); }
 
   void InitializeUIServiceWithoutWaitingForBlackList() {
@@ -375,7 +382,8 @@
         std::move(previews_decider_impl), std::make_unique<TestOptOutStore>(),
         std::make_unique<TestPreviewsOptimizationGuide>(
             &optimization_guide_service_,
-            scoped_task_environment_.GetMainThreadTaskRunner()),
+            scoped_task_environment_.GetMainThreadTaskRunner(),
+            temp_dir_.GetPath()),
         base::BindRepeating(&IsPreviewFieldTrialEnabled),
         std::make_unique<PreviewsLogger>(), std::move(allowed_types),
         &network_quality_tracker_));
@@ -404,6 +412,7 @@
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::ScopedTempDir temp_dir_;
   base::FieldTrialList field_trial_list_;
   TestPreviewsDeciderImpl* previews_decider_impl_;
   optimization_guide::OptimizationGuideService optimization_guide_service_;
@@ -821,19 +830,22 @@
 
   base::HistogramTester histogram_tester;
   PreviewsUserData user_data(kDefaultPageId);
+#if defined(OS_ANDROID)
+  // Enabled by default on Android. NOSCRIPT always allowed at navigation start
+  // to handle asynchronous loading of page hints; non-whitelisted ones are
+  // later blocked on commit.
+  EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
+      &user_data, GURL("https://www.google.com"), false,
+      PreviewsType::NOSCRIPT));
+  histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason.NoScript",
+      static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+#else   // !defined(OS_ANDROID)
+  // Disabled by default on non-Android.
   EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
       &user_data, GURL("https://www.google.com"), false,
       PreviewsType::NOSCRIPT));
-#if defined(OS_ANDROID)
-  // Enabled by default on Android but no server whitelist.
-  histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 1);
-  histogram_tester.ExpectUniqueSample(
-      "Previews.EligibilityReason.NoScript",
-      static_cast<int>(
-          PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
-      1);
-#else   // !defined(OS_ANDROID)
-  // Disabled by default on non-Android.
   histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 0);
 #endif  // defined(OS_ANDROID)
 }
@@ -875,25 +887,20 @@
   base::HistogramTester histogram_tester;
 
   PreviewsUserData user_data(kDefaultPageId);
-  // First verify no preview for non-whitelisted url.
-  EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
+  // First verify preview allowed for non-whitelisted url; they're always
+  // allowed at navigation start to enable asynchronous loading of page hints.
+  EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
       &user_data, GURL("https://www.google.com"), false,
       PreviewsType::NOSCRIPT));
 
-  histogram_tester.ExpectUniqueSample(
-      "Previews.EligibilityReason.NoScript",
-      static_cast<int>(
-          PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
-      1);
-
-  // Now verify preview for whitelisted url.
+  // Now verify preview allowed for whitelisted url.
   EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
       &user_data, GURL("https://whitelisted.example.com"), false,
       PreviewsType::NOSCRIPT));
 
   histogram_tester.ExpectBucketCount(
       "Previews.EligibilityReason.NoScript",
-      static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+      static_cast<int>(PreviewsEligibilityReason::ALLOWED), 2);
 }
 
 TEST_F(PreviewsDeciderImplTest, NoScriptCommitTimeWhitelistCheck) {
@@ -948,6 +955,7 @@
 
   // Verify preview not allowed for whitelisted url for unknown network quality.
   {
+    ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
     base::HistogramTester histogram_tester;
     PreviewsUserData user_data(kDefaultPageId);
     user_data.set_navigation_ect(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
@@ -982,9 +990,70 @@
         1);
   }
 
+  // Verify that navigation time ECT is given precedence over commit time ECT,
+  // if the former is available.
+  {
+    ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+    base::test::ScopedFeatureList nested_scoped_list;
+    nested_scoped_list.InitAndEnableFeatureWithParameters(
+        features::kSlowPageTriggering,
+        {{"session_max_ect_trigger", "Slow-2G"}});
+    base::HistogramTester histogram_tester;
+    PreviewsUserData user_data(kDefaultPageId);
+    user_data.set_navigation_ect(net::EFFECTIVE_CONNECTION_TYPE_2G);
+    EXPECT_FALSE(previews_decider_impl()->ShouldCommitPreview(
+        &user_data, GURL("https://whitelisted.example.com"),
+        PreviewsType::NOSCRIPT));
+
+    histogram_tester.ExpectUniqueSample(
+        "Previews.EligibilityReason.NoScript",
+        static_cast<int>(
+            PreviewsEligibilityReason::NETWORK_NOT_SLOW_FOR_SESSION),
+        1);
+  }
+
+  // Verify preview allowed for network slower than session ECT threshold when
+  // ECT is not available at navigation time.
+  {
+    ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+    base::test::ScopedFeatureList nested_scoped_list;
+    nested_scoped_list.InitAndEnableFeatureWithParameters(
+        features::kSlowPageTriggering,
+        {{"session_max_ect_trigger", "Slow-2G"}});
+    base::HistogramTester histogram_tester;
+    PreviewsUserData user_data(kDefaultPageId);
+    user_data.set_navigation_ect(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
+    EXPECT_TRUE(previews_decider_impl()->ShouldCommitPreview(
+        &user_data, GURL("https://whitelisted.example.com"),
+        PreviewsType::NOSCRIPT));
+
+    histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 0);
+  }
+
+  // Verify preview not allowed for session limited ECT threshold when ECT is
+  // not available at navigation time.
+  {
+    ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_4G);
+    base::test::ScopedFeatureList nested_scoped_list;
+    nested_scoped_list.InitAndEnableFeatureWithParameters(
+        features::kSlowPageTriggering,
+        {{"session_max_ect_trigger", "Slow-2G"}});
+    base::HistogramTester histogram_tester;
+    PreviewsUserData user_data(kDefaultPageId);
+    user_data.set_navigation_ect(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
+    EXPECT_FALSE(previews_decider_impl()->ShouldCommitPreview(
+        &user_data, GURL("https://whitelisted.example.com"),
+        PreviewsType::NOSCRIPT));
+
+    histogram_tester.ExpectUniqueSample(
+        "Previews.EligibilityReason.NoScript",
+        static_cast<int>(PreviewsEligibilityReason::NETWORK_NOT_SLOW), 1);
+  }
+
   // Verify preview allowed for session when navigation ECT is unknown but max
   // trigger threshold is 4G.
   {
+    ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
     base::test::ScopedFeatureList nested_scoped_list;
     nested_scoped_list.InitAndEnableFeatureWithParameters(
         features::kSlowPageTriggering, {{"session_max_ect_trigger", "4G"}});
@@ -1459,7 +1528,8 @@
       ::testing::Contains(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT));
 }
 
-TEST_F(PreviewsDeciderImplTest, LogDecisionMadeNetworkQualityNotAvailable) {
+TEST_F(PreviewsDeciderImplTest,
+       LoFi_LogDecisionMadeNetworkQualityNotAvailable) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       {features::kPreviews, features::kClientLoFi}, {});
@@ -1495,6 +1565,51 @@
   EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
   auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
+  for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
+    EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
+  }
+}
+
+TEST_F(PreviewsDeciderImplTest,
+       ResourceLoadingHints_LogDecisionMadeNetworkQualityNotAvailable) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kPreviews, features::kResourceLoadingHints,
+       features::kOptimizationHints},
+      {});
+  InitializeUIService();
+  std::unique_ptr<TestPreviewsBlackList> blacklist =
+      std::make_unique<TestPreviewsBlackList>(
+          PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
+  previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
+
+  auto expected_reason = PreviewsEligibilityReason::ALLOWED;
+  auto expected_type = PreviewsType::RESOURCE_LOADING_HINTS;
+
+  std::vector<PreviewsEligibilityReason> checked_decisions = {
+      PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE,
+      PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
+      PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
+      PreviewsEligibilityReason::USER_BLACKLISTED,
+      PreviewsEligibilityReason::HOST_BLACKLISTED,
+      PreviewsEligibilityReason::RELOAD_DISALLOWED,
+  };
+
+  ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
+  PreviewsUserData user_data(kDefaultPageId);
+  previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
+      &user_data, GURL("http://www.google.com"), false, expected_type);
+
+  base::RunLoop().RunUntilIdle();
+  // Testing correct log method is called.
+  EXPECT_THAT(ui_service()->decision_reasons(),
+              ::testing::Contains(expected_reason));
+  EXPECT_THAT(ui_service()->decision_types(),
+              ::testing::Contains(expected_type));
+
+  EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
+  auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
+  EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
     EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
@@ -1538,7 +1653,6 @@
   EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
   auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
-  EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
     EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
   }
@@ -1583,7 +1697,6 @@
   EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
   auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
-  EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
     EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
   }
@@ -1658,7 +1771,6 @@
   EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
   auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
-  EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
     EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
   }
@@ -1690,7 +1802,6 @@
       PreviewsEligibilityReason::USER_BLACKLISTED,
       PreviewsEligibilityReason::HOST_BLACKLISTED,
       PreviewsEligibilityReason::RELOAD_DISALLOWED,
-      PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER,
   };
   PreviewsUserData user_data(kDefaultPageId);
   EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
@@ -1707,7 +1818,6 @@
   EXPECT_EQ(1UL, ui_service()->decision_passed_reasons().size());
   auto actual_passed_reasons = ui_service()->decision_passed_reasons()[0];
   EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
-  EXPECT_EQ(checked_decisions.size(), actual_passed_reasons.size());
   for (size_t i = 0; i < actual_passed_reasons.size(); i++) {
     EXPECT_EQ(checked_decisions[i], actual_passed_reasons[i]);
   }
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index 627c16c..5a8011f4 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -4,20 +4,17 @@
 
 #include "components/previews/content/previews_hints.h"
 
-#include <array>
-#include <string>
+#include <unordered_set>
 
 #include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/hints_component_util.h"
-#include "components/optimization_guide/url_pattern_with_wildcards.h"
 #include "components/previews/core/bloom_filter.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_switches.h"
@@ -40,7 +37,7 @@
 // Returns false if the processing should not continue because the
 // file exists with the same version (indicating that processing that version
 // failed previously (possibly crash or shutdown). Should be run in the
-// background (e.g., same task as PreviewsHints::CreateFromHintsComponent()).
+// background (e.g., same task as PreviewsHints::Create()).
 bool CreateSentinelFile(const base::FilePath& sentinel_path,
                         const base::Version& version) {
   DCHECK(version.IsValid());
@@ -81,8 +78,9 @@
 // configuration is complete and should be done in the background (e.g.,
 // same task as Hints.CreateFromConfig).
 void DeleteSentinelFile(const base::FilePath& sentinel_path) {
-  if (!base::DeleteFile(sentinel_path, false /* recursive */))
+  if (!base::DeleteFile(sentinel_path, false /* recursive */)) {
     DLOG(ERROR) << "Error deleting sentinel file";
+  }
 }
 
 // Enumerates the possible outcomes of processing previews hints. Used in UMA
@@ -94,7 +92,8 @@
   kProcessedNoPreviewsHints = 0,
   kProcessedPreviewsHints = 1,
   kFailedFinishProcessing = 2,
-  kMaxValue = kFailedFinishProcessing
+  kSkippedProcessingPreviewsHints = 3,
+  kMaxValue = kSkippedProcessingPreviewsHints
 };
 
 // Enumerates status event of processing optimization filters (such as the
@@ -151,186 +150,55 @@
   }
 }
 
-bool IsDisabledExperimentalOptimization(
-    const optimization_guide::proto::Optimization& optimization) {
-  // If this optimization has been marked with an experiment name, consider it
-  // disabled unless an experiment with that name is running. Experiment names
-  // are configured with the experiment_name parameter to the
-  // kOptimizationHintsExperiments feature.
-  //
-  // If kOptimizationHintsExperiments is disabled, getting the param value
-  // returns an empty string. Since experiment names are not allowed to be
-  // empty strings, all experiments will be disabled if the feature is
-  // disabled.
-  if (optimization.has_experiment_name() &&
-      !optimization.experiment_name().empty() &&
-      optimization.experiment_name() !=
-          base::GetFieldTrialParamValueByFeature(
-              features::kOptimizationHintsExperiments,
-              features::kOptimizationHintsExperimentNameParam)) {
-    return true;
-  }
-  return false;
-}
-
-// If |hint| contains page hints, then this function adds a pared down version
-// of the hint to |stripped_hints_with_page_hints|, removing all of the hint's
-// top-level optimizations and only retaining the first enabled optimization of
-// each preview type within each page hint.
-// |total_page_patterns_with_resource_loading_hints_received| and
-// |total_resource_loading_hints_received| have their totals updated as
-// resource loading hints are encountered.
-void MaybeAddHintToStrippedHintsToHintCacheData(
+void UpdateTotalsForHintsWithPageHints(
     const optimization_guide::proto::Hint& hint,
-    HintCache::Data* hint_cache_data,
     size_t* total_page_patterns_with_resource_loading_hints_received,
     size_t* total_resource_loading_hints_received) {
-  DCHECK(hint_cache_data);
+  DCHECK(!hint.page_hints().empty());
   DCHECK(total_page_patterns_with_resource_loading_hints_received);
   DCHECK(total_resource_loading_hints_received);
 
-  if (hint.page_hints().empty()) {
-    return;
-  }
-
-  if (previews::params::IsResourceLoadingHintsEnabled()) {
-    UMA_HISTOGRAM_COUNTS("ResourceLoadingHints.PageHints.ProcessedCount",
-                         hint.page_hints().size());
-  }
-
-  // |stripped_hint| is a copy of |hint| with top-level optimizations and
-  // disabled experimental optimizations within the page hints stripped out.
-  optimization_guide::proto::Hint stripped_hint;
-
   for (const auto& page_hint : hint.page_hints()) {
-    // Track the preview types encountered for |page_hint|. Only the first
-    // supported one will be kept for any one preview type.
-    std::array<bool, static_cast<int>(PreviewsType::LAST)>
-        encountered_preview_types;
-    encountered_preview_types.fill(false);
-
-    // Initially set the added page hint to nullptr. This will be set the first
-    // time an enabled optimization is encountered.
-    optimization_guide::proto::PageHint* added_page_hint = nullptr;
-
     for (const auto& optimization : page_hint.whitelisted_optimizations()) {
-      if (IsDisabledExperimentalOptimization(optimization)) {
-        continue;
-      }
-
       base::Optional<PreviewsType> previews_type =
           ConvertProtoOptimizationTypeToPreviewsType(
               optimization.optimization_type());
-
-      // Only add the first encountered optimization of a previews type.
-      if (!previews_type ||
-          encountered_preview_types[static_cast<int>(*previews_type)]) {
+      if (!previews_type) {
         continue;
       }
-      encountered_preview_types[static_cast<int>(*previews_type)] = true;
 
-      // If this is a resource loading hints optimization, then add it to the
-      // resource loading hints totals.
       if (previews_type == PreviewsType::RESOURCE_LOADING_HINTS) {
-        // Always skip over resource loading hints when they're disabled.
-        if (!previews::params::IsResourceLoadingHintsEnabled()) {
-          continue;
-        }
-
         (*total_page_patterns_with_resource_loading_hints_received)++;
         (*total_resource_loading_hints_received) +=
             optimization.resource_loading_hints_size();
-
-        // If the total page patterns with resource loading hints has reached
-        // the cap, then no additional resource loading hints optimizations can
-        // be added to page hints.
-        if (*total_page_patterns_with_resource_loading_hints_received >
-            previews::params::GetMaxPageHintsInMemoryThreshhold()) {
-          continue;
-        }
       } else {
         DCHECK_EQ(optimization.resource_loading_hints_size(), 0);
       }
-
-      // If this page hint hasn't been added to the stripped hint yet, then add
-      // it now and populate its non-whitelisted optimization fields.
-      if (!added_page_hint) {
-        added_page_hint = stripped_hint.add_page_hints();
-        added_page_hint->set_page_pattern(page_hint.page_pattern());
-        if (page_hint.has_max_ect_trigger()) {
-          added_page_hint->set_max_ect_trigger(page_hint.max_ect_trigger());
-        }
-      }
-      auto* added_optimization =
-          added_page_hint->add_whitelisted_optimizations();
-      *added_optimization = optimization;
     }
   }
-
-  if (stripped_hint.page_hints_size() > 0) {
-    stripped_hint.set_key_representation(hint.key_representation());
-    stripped_hint.set_key(hint.key());
-    hint_cache_data->AddHint(stripped_hint);
-  }
 }
 
-void RecordProcessHintsResult(PreviewsProcessHintsResult result) {
-  base::UmaHistogramEnumeration("Previews.ProcessHintsResult", result);
-}
-
-void RecordOptimizationFilterStatus(PreviewsType previews_type,
-                                    PreviewsOptimizationFilterStatus status) {
-  std::string histogram_name =
-      base::StringPrintf("Previews.OptimizationFilterStatus.%s",
-                         GetStringNameForType(previews_type).c_str());
-  base::UmaHistogramEnumeration(histogram_name, status);
-}
-
-}  // namespace
-
-PreviewsHints::PreviewsHints() {
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-}
-
-PreviewsHints::~PreviewsHints() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-// static
-std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromHintsComponent(
-    const optimization_guide::HintsComponentInfo& info) {
-  std::unique_ptr<optimization_guide::proto::Configuration> config =
-      ProcessHintsComponent(info);
-  if (!config) {
-    return nullptr;
+PreviewsProcessHintsResult ProcessConfigurationHints(
+    optimization_guide::proto::Configuration* config,
+    HintCacheStore::ComponentUpdateData* component_update_data) {
+  DCHECK(config);
+  // If there's no component update data, then there's nothing to do. This
+  // component is not newer than the one contained within the hint cache.
+  if (!component_update_data) {
+    return PreviewsProcessHintsResult::kSkippedProcessingPreviewsHints;
   }
 
-  base::FilePath sentinel_path(info.path.DirName().Append(kSentinelFileName));
-  if (!CreateSentinelFile(sentinel_path, info.version)) {
-    RecordProcessHintsResult(
-        PreviewsProcessHintsResult::kFailedFinishProcessing);
-    return nullptr;
-  }
+  std::unordered_set<std::string> seen_host_suffixes;
 
-  std::unique_ptr<PreviewsHints> hints = CreateFromHintsConfiguration(*config);
-
-  // Completed processing hints data without crashing so clear sentinel.
-  DeleteSentinelFile(sentinel_path);
-  return hints;
-}
-
-// static
-std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromHintsConfiguration(
-    const optimization_guide::proto::Configuration& config) {
-  std::unique_ptr<PreviewsHints> hints(new PreviewsHints());
-  HintCache::Data hint_cache_data;
-
-  std::set<std::string> seen_host_suffixes;
-
+  size_t total_processed_hints_with_page_hints = 0;
   size_t total_page_patterns_with_resource_loading_hints_received = 0;
   size_t total_resource_loading_hints_received = 0;
-  // Process hint configuration.
-  for (const auto& hint : config.hints()) {
+
+  // Process each hint in the the hint configuration. The hints are mutable
+  // because once processing is completed on each individual hint, it is moved
+  // into the component update data. This eliminates the need to make any
+  // additional copies of the hints.
+  for (auto& hint : *(config->mutable_hints())) {
     // We only support host suffixes at the moment. Skip anything else.
     // One |hint| applies to one host URL suffix.
     if (hint.key_representation() != optimization_guide::proto::HOST_SUFFIX) {
@@ -353,14 +221,23 @@
     }
     seen_host_suffixes.insert(hint_key);
 
-    // If this hint contains page hints, then add a pared down version of the
-    // hint to the initial hints that are used to populate the hint cache,
-    // removing all of the hint's top-level optimizations and only retaining the
-    // first enabled optimization of each preview type within each page hint.
-    MaybeAddHintToStrippedHintsToHintCacheData(
-        hint, &hint_cache_data,
-        &total_page_patterns_with_resource_loading_hints_received,
-        &total_resource_loading_hints_received);
+    if (!hint.page_hints().empty()) {
+      ++total_processed_hints_with_page_hints;
+      UpdateTotalsForHintsWithPageHints(
+          hint, &total_page_patterns_with_resource_loading_hints_received,
+          &total_resource_loading_hints_received);
+
+      if (previews::params::IsResourceLoadingHintsEnabled()) {
+        UMA_HISTOGRAM_COUNTS("ResourceLoadingHints.PageHints.ProcessedCount",
+                             hint.page_hints().size());
+      }
+
+      // Now that processing is finished on |hint|, move it into the component
+      // data.
+      // WARNING: Do not use |hint| after this call. Its contents will no
+      // longer be valid.
+      component_update_data->MoveHintIntoUpdateData(std::move(hint));
+    }
   }
 
   if (previews::params::IsResourceLoadingHintsEnabled()) {
@@ -372,24 +249,103 @@
         total_resource_loading_hints_received);
   }
 
-  if (hint_cache_data.HasHints()) {
-    hints->hint_cache_ =
-        std::make_unique<HintCache>(std::move(hint_cache_data));
+  return total_processed_hints_with_page_hints > 0
+             ? PreviewsProcessHintsResult::kProcessedPreviewsHints
+             : PreviewsProcessHintsResult::kProcessedNoPreviewsHints;
+}
+
+void RecordProcessHintsResult(PreviewsProcessHintsResult result) {
+  base::UmaHistogramEnumeration("Previews.ProcessHintsResult", result);
+}
+
+void RecordOptimizationFilterStatus(PreviewsType previews_type,
+                                    PreviewsOptimizationFilterStatus status) {
+  std::string histogram_name =
+      base::StringPrintf("Previews.OptimizationFilterStatus.%s",
+                         GetStringNameForType(previews_type).c_str());
+  base::UmaHistogramEnumeration(histogram_name, status);
+}
+
+}  // namespace
+
+PreviewsHints::PreviewsHints(
+    std::unique_ptr<HintCacheStore::ComponentUpdateData> component_update_data)
+    : hint_cache_(nullptr),
+      component_update_data_(std::move(component_update_data)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+PreviewsHints::~PreviewsHints() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+// static
+std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromHintsComponent(
+    const optimization_guide::HintsComponentInfo& info,
+    std::unique_ptr<HintCacheStore::ComponentUpdateData>
+        component_update_data) {
+  std::unique_ptr<optimization_guide::proto::Configuration> config =
+      ProcessHintsComponent(info);
+  if (!config) {
+    return nullptr;
   }
 
-  // Extract any supported large scale blacklists from the configuration.
-  hints->ParseOptimizationFilters(config);
+  base::FilePath sentinel_path(info.path.DirName().Append(kSentinelFileName));
+  if (!CreateSentinelFile(sentinel_path, info.version)) {
+    RecordProcessHintsResult(
+        PreviewsProcessHintsResult::kFailedFinishProcessing);
+    return nullptr;
+  }
 
-  RecordProcessHintsResult(
-      !hints->hint_cache_
-          ? PreviewsProcessHintsResult::kProcessedNoPreviewsHints
-          : PreviewsProcessHintsResult::kProcessedPreviewsHints);
+  std::unique_ptr<PreviewsHints> hints = CreateFromHintsConfiguration(
+      std::move(config), std::move(component_update_data));
+
+  // Completed processing hints data without crashing so clear sentinel.
+  DeleteSentinelFile(sentinel_path);
   return hints;
 }
 
+// static
+std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromHintsConfiguration(
+    std::unique_ptr<optimization_guide::proto::Configuration> config,
+    std::unique_ptr<HintCacheStore::ComponentUpdateData>
+        component_update_data) {
+  // Process the hints within the configuration. This will move the hints from
+  // |config| into |component_update_data|.
+  PreviewsProcessHintsResult process_hints_result =
+      ProcessConfigurationHints(config.get(), component_update_data.get());
+
+  // Construct the PrevewsHints object with |component_update_data|, which will
+  // later be used to update the HintCache's component data during Initialize().
+  std::unique_ptr<PreviewsHints> hints(
+      new PreviewsHints(std::move(component_update_data)));
+
+  // Extract any supported large scale blacklists from the configuration.
+  hints->ParseOptimizationFilters(*config);
+
+  // Now that processing is complete, record the result.
+  RecordProcessHintsResult(process_hints_result);
+
+  return hints;
+}
+
+void PreviewsHints::Initialize(HintCache* hint_cache,
+                               base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!hint_cache_);
+  DCHECK(hint_cache);
+  hint_cache_ = hint_cache;
+  if (component_update_data_) {
+    hint_cache_->UpdateComponentData(std::move(component_update_data_),
+                                     std::move(callback));
+  } else {
+    std::move(callback).Run();
+  }
+}
+
 void PreviewsHints::ParseOptimizationFilters(
     const optimization_guide::proto::Configuration& config) {
-  for (const auto blacklist : config.optimization_blacklists()) {
+  for (const auto& blacklist : config.optimization_blacklists()) {
     base::Optional<PreviewsType> previews_type =
         ConvertProtoOptimizationTypeToPreviewsType(
             blacklist.optimization_type());
@@ -407,7 +363,7 @@
                                        kFailedServerBlacklistDuplicateConfig);
         continue;
       }
-      auto bloom_filter_proto = blacklist.bloom_filter();
+      const auto& bloom_filter_proto = blacklist.bloom_filter();
       DCHECK_GT(bloom_filter_proto.num_hash_functions(), 0u);
       DCHECK_GT(bloom_filter_proto.num_bits(), 0u);
       DCHECK(bloom_filter_proto.has_data());
@@ -445,52 +401,34 @@
   }
 }
 
-// static
-const optimization_guide::proto::PageHint* PreviewsHints::FindPageHint(
-    const GURL& document_url,
-    const optimization_guide::proto::Hint& hint) {
-  if (hint.page_hints_size() == 0)
-    return nullptr;
-  std::string url = document_url.spec();
-  for (const auto& page_hint : hint.page_hints()) {
-    if (page_hint.page_pattern().empty())
-      continue;
-    optimization_guide::URLPatternWithWildcards url_pattern(
-        page_hint.page_pattern());
-    if (url_pattern.Matches(url)) {
-      // Return the first matching page hint.
-      return &page_hint;
-    }
-  }
-  return nullptr;
-}
-
 bool PreviewsHints::IsWhitelisted(
     const GURL& url,
     PreviewsType type,
     int* out_inflation_percent,
     net::EffectiveConnectionType* out_ect_threshold) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!hint_cache_) {
-    return false;
-  }
+  DCHECK(hint_cache_);
 
   if (!url.has_host()) {
     return false;
   }
 
+  if (type == previews::PreviewsType::RESOURCE_LOADING_HINTS &&
+      !previews::params::IsResourceLoadingHintsEnabled()) {
+    return false;
+  }
+
   // Now check HintCache for a loaded entry with a PageHint that matches |url|
   // that whitelists the optimization.
-  std::string host = url.host();
-  if (!hint_cache_->IsHintLoaded(host)) {
+  const optimization_guide::proto::Hint* hint =
+      hint_cache_->GetHintIfLoaded(url.host());
+  if (!hint) {
     // TODO(dougarnett): Add UMA histogram counts for both cases of HasHint().
     return false;
   }
 
-  const optimization_guide::proto::Hint* hint = hint_cache_->GetHint(host);
   const optimization_guide::proto::PageHint* matched_page_hint =
-      FindPageHint(url, *hint);
+      FindPageHintForURL(url, hint);
   if (!matched_page_hint) {
     return false;
   }
@@ -499,8 +437,9 @@
        matched_page_hint->whitelisted_optimizations()) {
     if (ConvertProtoOptimizationTypeToPreviewsType(
             optimization.optimization_type()) == type) {
-      // TODO(jegray): When persistence is added for hints, address handling of
-      // disabled experimental optimizations.
+      if (IsDisabledExperimentalOptimization(optimization)) {
+        continue;
+      }
       // Found whitelisted optimization.
       *out_inflation_percent = optimization.inflation_percent();
       if (matched_page_hint->has_max_ect_trigger()) {
@@ -529,8 +468,9 @@
       return false;
     }
 
-    if (lite_page_redirect_blacklist_)
+    if (lite_page_redirect_blacklist_) {
       return lite_page_redirect_blacklist_->ContainsHostSuffix(url);
+    }
   }
 
   return false;
@@ -540,8 +480,9 @@
     const GURL& url,
     HintLoadedCallback callback) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(hint_cache_);
 
-  if (!hint_cache_ || !url.has_host()) {
+  if (!url.has_host()) {
     return false;
   }
 
@@ -553,26 +494,17 @@
     const GURL& url,
     std::vector<std::string>* out_resource_patterns_to_block) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!hint_cache_) {
-    return false;
-  }
+  DCHECK(hint_cache_);
+  DCHECK(previews::params::IsResourceLoadingHintsEnabled());
 
   // First find matched page hint.
-
-  std::string host = url.host();
-  if (!hint_cache_->IsHintLoaded(host)) {
-    return false;
-  }
-  const optimization_guide::proto::Hint* hint = hint_cache_->GetHint(host);
   const optimization_guide::proto::PageHint* matched_page_hint =
-      FindPageHint(url, *hint);
+      FindPageHintForURL(url, hint_cache_->GetHintIfLoaded(url.host()));
   if (!matched_page_hint) {
     return false;
   }
 
   // Now populate the resource patterns.
-
   for (const auto& optimization :
        matched_page_hint->whitelisted_optimizations()) {
     if (optimization.optimization_type() !=
@@ -580,8 +512,10 @@
       continue;
     }
 
-    // TODO(jegray): When persistence is added for hints, address handling of
-    // disabled experimental optimizations.
+    if (IsDisabledExperimentalOptimization(optimization)) {
+      continue;
+    }
+
     for (const auto& resource_loading_hint :
          optimization.resource_loading_hints()) {
       if (!resource_loading_hint.resource_pattern().empty() &&
@@ -600,9 +534,7 @@
 void PreviewsHints::LogHintCacheMatch(const GURL& url,
                                       bool is_committed,
                                       net::EffectiveConnectionType ect) const {
-  if (!hint_cache_) {
-    return;
-  }
+  DCHECK(hint_cache_);
 
   if (hint_cache_->HasHint(url.host())) {
     if (!is_committed) {
@@ -613,13 +545,13 @@
       UMA_HISTOGRAM_ENUMERATION(
           "Previews.OptimizationGuide.HintCache.HasHint.AtCommit", ect,
           net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
-      if (hint_cache_->IsHintLoaded(url.host())) {
+      const optimization_guide::proto::Hint* hint =
+          hint_cache_->GetHintIfLoaded(url.host());
+      if (hint) {
         UMA_HISTOGRAM_ENUMERATION(
             "Previews.OptimizationGuide.HintCache.HostMatch.AtCommit", ect,
             net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
-        const optimization_guide::proto::Hint* hint =
-            hint_cache_->GetHint(url.host());
-        if (FindPageHint(url, *hint) != nullptr) {
+        if (FindPageHintForURL(url, hint)) {
           UMA_HISTOGRAM_ENUMERATION(
               "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit", ect,
               net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
diff --git a/components/previews/content/previews_hints.h b/components/previews/content/previews_hints.h
index 4b28e16..aa9f3f7e 100644
--- a/components/previews/content/previews_hints.h
+++ b/components/previews/content/previews_hints.h
@@ -6,15 +6,14 @@
 #define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_
 
 #include <memory>
-#include <set>
-#include <utility>
+#include <string>
 #include <vector>
 
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/previews/content/hint_cache.h"
-#include "components/previews/content/previews_hints.h"
+#include "components/previews/content/previews_hints_util.h"
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/host_filter.h"
 #include "net/nqe/effective_connection_type.h"
@@ -27,28 +26,36 @@
 
 namespace previews {
 
-// Holds previews hints extracted from the configuration.
+// Holds previews hints extracted from a hint component.
 class PreviewsHints {
  public:
   ~PreviewsHints();
 
-  // Creates a Hints instance from the provided hints component. This must be
-  // called using a background task runner as it requires a significant amount
-  // of processing.
+  // Loads the component associated with the provided component info and creates
+  // a PreviewsHints instance from it. If |component_update_info| is provided,
+  // then the page hints from the component will be moved into it and later used
+  // to update the component hints in the hint cache store. This must be called
+  // using a background task runner as it requires a significant amount of
+  // processing.
   static std::unique_ptr<PreviewsHints> CreateFromHintsComponent(
-      const optimization_guide::HintsComponentInfo& info);
+      const optimization_guide::HintsComponentInfo& info,
+      std::unique_ptr<HintCacheStore::ComponentUpdateData>
+          component_update_data);
 
   // Creates a Hints instance from the provided hints configuration. This must
   // be called using a background task runner as it requires a significant
   // amount of processing.
   static std::unique_ptr<PreviewsHints> CreateFromHintsConfiguration(
-      const optimization_guide::proto::Configuration& config);
+      std::unique_ptr<optimization_guide::proto::Configuration> config,
+      std::unique_ptr<HintCacheStore::ComponentUpdateData>
+          component_update_data);
 
-  // Returns the matching PageHint for |document_url| if found in |hint|.
-  // TODO(dougarnett): Consider moving to some hint_util file.
-  static const optimization_guide::proto::PageHint* FindPageHint(
-      const GURL& document_url,
-      const optimization_guide::proto::Hint& hint);
+  // Set |hint_cache_| and updates the hint cache's component data if
+  // |component_update_data_| is not a nullptr. In the case where
+  // |component_update_data_| is a nullptr, the callback is run synchronously;
+  // otherwise, it is run asynchronously after the cache's component data update
+  // completes.
+  void Initialize(HintCache* hint_cache, base::OnceClosure callback);
 
   // Whether the URL is whitelisted for the given previews type. If so,
   // |out_inflation_percent| and |out_ect_threshold| will be populated if
@@ -83,15 +90,24 @@
  private:
   friend class PreviewsHintsTest;
 
-  PreviewsHints();
+  // Constructs PreviewsHints with |component_update_data|. This
+  // ComponentUpdateData is later moved into the HintCache during Initialize().
+  PreviewsHints(std::unique_ptr<HintCacheStore::ComponentUpdateData>
+                    component_update_data);
 
   // Parses optimization filters from |config| and populates corresponding
   // supported blacklists in this object.
   void ParseOptimizationFilters(
       const optimization_guide::proto::Configuration& config);
 
-  // Holds the hint cache (if any optimizations using it are enabled).
-  std::unique_ptr<HintCache> hint_cache_;
+  // Holds a pointer to the hint cache; the cache is owned by the optimization
+  // guide, which is guaranteed to outlive PreviewsHints.
+  HintCache* hint_cache_;
+
+  // ComponentUpdateData provided by the HintCache and populated during
+  // PreviewsHints::Create(). |component_update_data_| is set during
+  // construction and moved into the HintCache during Initialize().
+  std::unique_ptr<HintCacheStore::ComponentUpdateData> component_update_data_;
 
   // Blacklist of host suffixes for LITE_PAGE_REDIRECT Previews.
   std::unique_ptr<HostFilter> lite_page_redirect_blacklist_;
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc
index ca27396..474cf01 100644
--- a/components/previews/content/previews_hints_unittest.cc
+++ b/components/previews/content/previews_hints_unittest.cc
@@ -9,10 +9,15 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/content/hint_cache.h"
+#include "components/previews/content/hint_cache_leveldb_store.h"
+#include "components/previews/content/previews_hints_util.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -65,18 +70,50 @@
 
 class PreviewsHintsTest : public testing::Test {
  public:
-  PreviewsHintsTest() : previews_hints_(nullptr) {}
+  PreviewsHintsTest() {}
 
   ~PreviewsHintsTest() override {}
 
-  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    hint_cache_ =
+        std::make_unique<HintCache>(std::make_unique<HintCacheLevelDBStore>(
+            temp_dir_.GetPath(),
+            scoped_task_environment_.GetMainThreadTaskRunner()));
+
+    is_store_initialized_ = false;
+    hint_cache_->Initialize(
+        false /*=purge_existing_data*/,
+        base::BindOnce(&PreviewsHintsTest::OnStoreInitialized,
+                       base::Unretained(this)));
+    while (!is_store_initialized_) {
+      RunUntilIdle();
+    }
+  }
+
+  void TearDown() override {
+    previews_hints_.reset();
+    RunUntilIdle();
+    hint_cache_.reset();
+    RunUntilIdle();
+  }
 
   void ParseConfig(const optimization_guide::proto::Configuration& config) {
     optimization_guide::HintsComponentInfo info(
         base::Version("1.0"),
         temp_dir_.GetPath().Append(FILE_PATH_LITERAL("somefile.pb")));
     ASSERT_NO_FATAL_FAILURE(WriteConfigToFile(config, info.path));
-    previews_hints_ = PreviewsHints::CreateFromHintsComponent(info);
+    previews_hints_ = PreviewsHints::CreateFromHintsComponent(
+        info, hint_cache_->MaybeCreateComponentUpdateData(info.version));
+
+    are_previews_hints_initialized_ = false;
+    previews_hints_->Initialize(
+        hint_cache_.get(),
+        base::BindOnce(&PreviewsHintsTest::OnPreviewsHintsInitialized,
+                       base::Unretained(this)));
+    while (!are_previews_hints_initialized_) {
+      RunUntilIdle();
+    }
   }
 
   PreviewsHints* previews_hints() { return previews_hints_.get(); }
@@ -85,7 +122,29 @@
     return previews_hints_->lite_page_redirect_blacklist_.get() != nullptr;
   }
 
+ protected:
+  bool MaybeLoadHintAndCheckIsWhitelisted(
+      const GURL& url,
+      PreviewsType type,
+      int* out_inflation_percent,
+      net::EffectiveConnectionType* out_ect_threshold);
+
+  void MaybeLoadHintAndLogHintCacheMatch(const GURL& url,
+                                         bool is_committed,
+                                         net::EffectiveConnectionType ect);
+
+  void RunUntilIdle() {
+    scoped_task_environment_.RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
  private:
+  void OnStoreInitialized() { is_store_initialized_ = true; }
+  void OnPreviewsHintsInitialized() { are_previews_hints_initialized_ = true; }
+  void OnHintLoaded(const optimization_guide::proto::Hint* /*unused*/) {
+    is_hint_loaded_ = true;
+  }
+
   void WriteConfigToFile(const optimization_guide::proto::Configuration& config,
                          const base::FilePath& filePath) {
     std::string serialized_config;
@@ -95,60 +154,51 @@
                               serialized_config.length()));
   }
 
+  void MaybeLoadHint(const GURL& url);
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::ScopedTempDir temp_dir_;
+
+  bool is_store_initialized_;
+  bool are_previews_hints_initialized_;
+  bool is_hint_loaded_;
+
+  std::unique_ptr<HintCache> hint_cache_;
   std::unique_ptr<PreviewsHints> previews_hints_;
 };
 
+bool PreviewsHintsTest::MaybeLoadHintAndCheckIsWhitelisted(
+    const GURL& url,
+    PreviewsType type,
+    int* out_inflation_percent,
+    net::EffectiveConnectionType* out_ect_threshold) {
+  MaybeLoadHint(url);
+  return previews_hints_->IsWhitelisted(url, type, out_inflation_percent,
+                                        out_ect_threshold);
+}
+
+void PreviewsHintsTest::MaybeLoadHintAndLogHintCacheMatch(
+    const GURL& url,
+    bool is_committed,
+    net::EffectiveConnectionType ect) {
+  MaybeLoadHint(url);
+  previews_hints_->LogHintCacheMatch(url, is_committed, ect);
+}
+
+void PreviewsHintsTest::MaybeLoadHint(const GURL& url) {
+  is_hint_loaded_ = false;
+  if (previews_hints_->MaybeLoadOptimizationHints(
+          url, base::BindOnce(&PreviewsHintsTest::OnHintLoaded,
+                              base::Unretained(this)))) {
+    while (!is_hint_loaded_) {
+      RunUntilIdle();
+    }
+  }
+}
+
 // NOTE: most of the PreviewsHints tests are still included in the tests for
 // PreviewsOptimizationGuide.
 
-TEST_F(PreviewsHintsTest, FindPageHintForSubstringPagePattern) {
-  optimization_guide::proto::Hint hint1;
-
-  // Page hint for "/one/"
-  optimization_guide::proto::PageHint* page_hint1 = hint1.add_page_hints();
-  page_hint1->set_page_pattern("foo.org/*/one/");
-
-  // Page hint for "two"
-  optimization_guide::proto::PageHint* page_hint2 = hint1.add_page_hints();
-  page_hint2->set_page_pattern("two");
-
-  // Page hint for "three.jpg"
-  optimization_guide::proto::PageHint* page_hint3 = hint1.add_page_hints();
-  page_hint3->set_page_pattern("three.jpg");
-
-  EXPECT_EQ(nullptr, PreviewsHints::FindPageHint(GURL(""), hint1));
-  EXPECT_EQ(nullptr,
-            PreviewsHints::FindPageHint(GURL("https://www.foo.org/"), hint1));
-  EXPECT_EQ(nullptr, PreviewsHints::FindPageHint(
-                         GURL("https://www.foo.org/one"), hint1));
-
-  EXPECT_EQ(nullptr, PreviewsHints::FindPageHint(
-                         GURL("https://www.foo.org/one/"), hint1));
-  EXPECT_EQ(page_hint1, PreviewsHints::FindPageHint(
-                            GURL("https://www.foo.org/pages/one/"), hint1));
-  EXPECT_EQ(page_hint1,
-            PreviewsHints::FindPageHint(
-                GURL("https://www.foo.org/pages/subpages/one/"), hint1));
-  EXPECT_EQ(page_hint1, PreviewsHints::FindPageHint(
-                            GURL("https://www.foo.org/pages/one/two"), hint1));
-  EXPECT_EQ(page_hint1,
-            PreviewsHints::FindPageHint(
-                GURL("https://www.foo.org/pages/one/two/three.jpg"), hint1));
-
-  EXPECT_EQ(page_hint2,
-            PreviewsHints::FindPageHint(
-                GURL("https://www.foo.org/pages/onetwo/three.jpg"), hint1));
-  EXPECT_EQ(page_hint2,
-            PreviewsHints::FindPageHint(
-                GURL("https://www.foo.org/one/two/three.jpg"), hint1));
-  EXPECT_EQ(page_hint2,
-            PreviewsHints::FindPageHint(GURL("https://one.two.org"), hint1));
-
-  EXPECT_EQ(page_hint3, PreviewsHints::FindPageHint(
-                            GURL("https://www.foo.org/bar/three.jpg"), hint1));
-}
-
 TEST_F(PreviewsHintsTest, LogHintCacheMatch) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
@@ -173,10 +223,10 @@
   base::HistogramTester histogram_tester;
 
   // First verify no histogram counts for non-matching URL host.
-  previews_hints()->LogHintCacheMatch(
+  MaybeLoadHintAndLogHintCacheMatch(
       GURL("https://someotherdomain.com/news/story.html"),
       false /* is_committed */, net::EFFECTIVE_CONNECTION_TYPE_3G);
-  previews_hints()->LogHintCacheMatch(
+  MaybeLoadHintAndLogHintCacheMatch(
       GURL("https://someotherdomain.com/news/story2.html"),
       true /* is_committed */, net::EFFECTIVE_CONNECTION_TYPE_4G);
   histogram_tester.ExpectTotalCount(
@@ -189,10 +239,10 @@
       "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit", 0);
 
   // Now verify do have histogram counts for matching URL host.
-  previews_hints()->LogHintCacheMatch(
+  MaybeLoadHintAndLogHintCacheMatch(
       GURL("https://somedomain.org/news/story.html"), false /* is_committed */,
       net::EFFECTIVE_CONNECTION_TYPE_3G);
-  previews_hints()->LogHintCacheMatch(
+  MaybeLoadHintAndLogHintCacheMatch(
       GURL("https://somedomain.org/news/story2.html"), true /* is_committed */,
       net::EFFECTIVE_CONNECTION_TYPE_4G);
   histogram_tester.ExpectBucketCount(
@@ -378,7 +428,7 @@
   int inflation_percent = 0;
   net::EffectiveConnectionType ect_threshold =
       net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-  EXPECT_TRUE(previews_hints()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
       GURL("https://www.somedomain.org/has_inflation_percent/"),
       PreviewsType::RESOURCE_LOADING_HINTS, &inflation_percent,
       &ect_threshold));
@@ -390,7 +440,7 @@
   inflation_percent = 0;
   ect_threshold =
       net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-  EXPECT_TRUE(previews_hints()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
       GURL("https://www.somedomain.org/has_max_ect_trigger/"),
       PreviewsType::RESOURCE_LOADING_HINTS, &inflation_percent,
       &ect_threshold));
@@ -435,7 +485,7 @@
   int inflation_percent = 0;
   net::EffectiveConnectionType ect_threshold =
       net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-  EXPECT_TRUE(previews_hints()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
       GURL("https://www.somedomain.org/has_noscript/"), PreviewsType::NOSCRIPT,
       &inflation_percent, &ect_threshold));
 }
@@ -455,6 +505,7 @@
   page_hint1->set_max_ect_trigger(
       optimization_guide::proto::EffectiveConnectionType::
           EFFECTIVE_CONNECTION_TYPE_3G);
+
   // First add experimental PageHint optimization.
   optimization_guide::proto::Optimization* experimental_optimization =
       page_hint1->add_whitelisted_optimizations();
@@ -467,6 +518,7 @@
   experimental_resourcehint->set_loading_optimization_type(
       optimization_guide::proto::LOADING_BLOCK_RESOURCE);
   experimental_resourcehint->set_resource_pattern("experimental_resource.js");
+
   // Add a non-experimental PageHint optimization with same resource pattern.
   optimization_guide::proto::Optimization* default_pagehint_optimization =
       page_hint1->add_whitelisted_optimizations();
@@ -479,17 +531,20 @@
       optimization_guide::proto::LOADING_BLOCK_RESOURCE);
   default_resourcehint->set_resource_pattern("default_resource.js");
 
+  base::HistogramTester histogram_tester;
+
+  ParseConfig(config);
+
+  histogram_tester.ExpectUniqueSample(
+      "ResourceLoadingHints.ResourceHints.TotalReceived", 2, 1);
+
   // Test with the experiment disabled.
   {
-    base::HistogramTester histogram_tester;
-
-    ParseConfig(config);
-
     // Verify default resource hint whitelisted (via inflation_percent).
     int inflation_percent = 0;
     net::EffectiveConnectionType ect_threshold =
         net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-    EXPECT_TRUE(previews_hints()->IsWhitelisted(
+    EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
         GURL("https://www.somedomain.org/experimental_preview/"
              "experimental_resource.js"),
         PreviewsType::RESOURCE_LOADING_HINTS, &inflation_percent,
@@ -497,35 +552,26 @@
     EXPECT_EQ(33, inflation_percent);
     EXPECT_EQ(net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G,
               ect_threshold);
+
     std::vector<std::string> patterns_to_block;
     previews_hints()->GetResourceLoadingHints(
         GURL("https://www.somedomain.org/experimental_preview/"),
         &patterns_to_block);
     EXPECT_EQ(1ul, patterns_to_block.size());
     EXPECT_EQ("default_resource.js", patterns_to_block[0]);
-
-    // Verify that the experimental optimization was not added when it was
-    // disabled.
-    histogram_tester.ExpectUniqueSample(
-        "ResourceLoadingHints.ResourceHints.TotalReceived", 1, 1);
   }
 
   // Now enable the experiment and verify experimental resource hint chosen.
   {
-    base::HistogramTester histogram_tester;
-
     base::test::ScopedFeatureList scoped_list2;
     scoped_list2.InitAndEnableFeatureWithParameters(
         features::kOptimizationHintsExperiments,
         {{"experiment_name", "foo_experiment"}});
 
-    // Parse the config again with the experiment enabled.
-    ParseConfig(config);
-
     int inflation_percent = 0;
     net::EffectiveConnectionType ect_threshold =
         net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_2G;
-    EXPECT_TRUE(previews_hints()->IsWhitelisted(
+    EXPECT_TRUE(MaybeLoadHintAndCheckIsWhitelisted(
         GURL("https://www.somedomain.org/experimental_preview/"
              "experimental_resource.js"),
         PreviewsType::RESOURCE_LOADING_HINTS, &inflation_percent,
@@ -533,18 +579,55 @@
     EXPECT_EQ(99, inflation_percent);
     EXPECT_EQ(net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G,
               ect_threshold);
+
     std::vector<std::string> patterns_to_block;
     previews_hints()->GetResourceLoadingHints(
         GURL("https://www.somedomain.org/experimental_preview/"),
         &patterns_to_block);
     EXPECT_EQ(1ul, patterns_to_block.size());
     EXPECT_EQ("experimental_resource.js", patterns_to_block[0]);
+  }
+}
 
-    // Verify that the second optimization was not added when the experimental
-    // optimization was enabled.
+TEST_F(PreviewsHintsTest, ParseDuplicateConfigs) {
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
+
+  optimization_guide::proto::Configuration config;
+  optimization_guide::proto::Hint* hint1 = config.add_hints();
+  hint1->set_key("somedomain.org");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
+  page_hint1->set_page_pattern("/news/");
+  optimization_guide::proto::Optimization* optimization1 =
+      page_hint1->add_whitelisted_optimizations();
+  optimization1->set_optimization_type(
+      optimization_guide::proto::RESOURCE_LOADING);
+  optimization_guide::proto::ResourceLoadingHint* resource_loading_hint1 =
+      optimization1->add_resource_loading_hints();
+  resource_loading_hint1->set_loading_optimization_type(
+      optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+  resource_loading_hint1->set_resource_pattern("news_cruft.js");
+
+  // Test the first time parsing the config.
+  {
+    base::HistogramTester histogram_tester;
+    ParseConfig(config);
+    histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
+                                        1 /* kProcessedPreviewsHints */, 1);
     histogram_tester.ExpectUniqueSample(
         "ResourceLoadingHints.ResourceHints.TotalReceived", 1, 1);
   }
+
+  // Test the second time parsing the config. This will be treated by the cache
+  // as a duplicate version.
+  {
+    base::HistogramTester histogram_tester;
+    ParseConfig(config);
+    histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
+                                       3 /* kSkippedProcessingPreviewsHints */,
+                                       1);
+  }
 }
 
 }  // namespace previews
diff --git a/components/previews/content/previews_hints_util.cc b/components/previews/content/previews_hints_util.cc
new file mode 100644
index 0000000..6b62f95
--- /dev/null
+++ b/components/previews/content/previews_hints_util.cc
@@ -0,0 +1,60 @@
+// 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 "components/previews/content/previews_hints_util.h"
+
+#include <string>
+
+#include "base/metrics/field_trial_params.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/optimization_guide/url_pattern_with_wildcards.h"
+#include "components/previews/core/previews_features.h"
+#include "url/gurl.h"
+
+namespace previews {
+
+bool IsDisabledExperimentalOptimization(
+    const optimization_guide::proto::Optimization& optimization) {
+  // If this optimization has been marked with an experiment name, consider it
+  // disabled unless an experiment with that name is running. Experiment names
+  // are configured with the experiment_name parameter to the
+  // kOptimizationHintsExperiments feature.
+  //
+  // If kOptimizationHintsExperiments is disabled, getting the param value
+  // returns an empty string. Since experiment names are not allowed to be
+  // empty strings, all experiments will be disabled if the feature is
+  // disabled.
+  if (optimization.has_experiment_name() &&
+      !optimization.experiment_name().empty() &&
+      optimization.experiment_name() !=
+          base::GetFieldTrialParamValueByFeature(
+              features::kOptimizationHintsExperiments,
+              features::kOptimizationHintsExperimentNameParam)) {
+    return true;
+  }
+  return false;
+}
+
+const optimization_guide::proto::PageHint* FindPageHintForURL(
+    const GURL& gurl,
+    const optimization_guide::proto::Hint* hint) {
+  if (!hint) {
+    return nullptr;
+  }
+
+  for (const auto& page_hint : hint->page_hints()) {
+    if (page_hint.page_pattern().empty()) {
+      continue;
+    }
+    optimization_guide::URLPatternWithWildcards url_pattern(
+        page_hint.page_pattern());
+    if (url_pattern.Matches(gurl.spec())) {
+      // Return the first matching page hint.
+      return &page_hint;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace previews
diff --git a/components/previews/content/previews_hints_util.h b/components/previews/content/previews_hints_util.h
new file mode 100644
index 0000000..5fab6e7
--- /dev/null
+++ b/components/previews/content/previews_hints_util.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_
+#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_
+
+class GURL;
+namespace optimization_guide {
+namespace proto {
+class Hint;
+class Optimization;
+class PageHint;
+}  // namespace proto
+}  // namespace optimization_guide
+
+namespace previews {
+
+// Returns true if the optimization is part of a disabled experiment.
+bool IsDisabledExperimentalOptimization(
+    const optimization_guide::proto::Optimization& optimization);
+
+// Returns the matching PageHint for |gurl| if found in |hint|.
+const optimization_guide::proto::PageHint* FindPageHintForURL(
+    const GURL& gurl,
+    const optimization_guide::proto::Hint* hint);
+
+}  // namespace previews
+
+#endif  // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_
diff --git a/components/previews/content/previews_hints_util_unittest.cc b/components/previews/content/previews_hints_util_unittest.cc
new file mode 100644
index 0000000..b3e02c25
--- /dev/null
+++ b/components/previews/content/previews_hints_util_unittest.cc
@@ -0,0 +1,65 @@
+// 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 "components/previews/content/previews_hints_util.h"
+
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace previews {
+
+class PreviewsHintsUtilTest : public testing::Test {
+ public:
+  PreviewsHintsUtilTest() {}
+  ~PreviewsHintsUtilTest() override {}
+};
+
+TEST_F(PreviewsHintsUtilTest, FindPageHintForSubstringPagePattern) {
+  optimization_guide::proto::Hint hint1;
+
+  // Page hint for "/one/"
+  optimization_guide::proto::PageHint* page_hint1 = hint1.add_page_hints();
+  page_hint1->set_page_pattern("foo.org/*/one/");
+
+  // Page hint for "two"
+  optimization_guide::proto::PageHint* page_hint2 = hint1.add_page_hints();
+  page_hint2->set_page_pattern("two");
+
+  // Page hint for "three.jpg"
+  optimization_guide::proto::PageHint* page_hint3 = hint1.add_page_hints();
+  page_hint3->set_page_pattern("three.jpg");
+
+  EXPECT_EQ(nullptr, FindPageHintForURL(GURL(""), &hint1));
+  EXPECT_EQ(nullptr, FindPageHintForURL(GURL("https://www.foo.org/"), &hint1));
+  EXPECT_EQ(nullptr,
+            FindPageHintForURL(GURL("https://www.foo.org/one"), &hint1));
+
+  EXPECT_EQ(nullptr,
+            FindPageHintForURL(GURL("https://www.foo.org/one/"), &hint1));
+  EXPECT_EQ(page_hint1,
+            FindPageHintForURL(GURL("https://www.foo.org/pages/one/"), &hint1));
+  EXPECT_EQ(page_hint1,
+            FindPageHintForURL(GURL("https://www.foo.org/pages/subpages/one/"),
+                               &hint1));
+  EXPECT_EQ(page_hint1, FindPageHintForURL(
+                            GURL("https://www.foo.org/pages/one/two"), &hint1));
+  EXPECT_EQ(page_hint1,
+            FindPageHintForURL(
+                GURL("https://www.foo.org/pages/one/two/three.jpg"), &hint1));
+
+  EXPECT_EQ(page_hint2,
+            FindPageHintForURL(
+                GURL("https://www.foo.org/pages/onetwo/three.jpg"), &hint1));
+  EXPECT_EQ(page_hint2,
+            FindPageHintForURL(GURL("https://www.foo.org/one/two/three.jpg"),
+                               &hint1));
+  EXPECT_EQ(page_hint2,
+            FindPageHintForURL(GURL("https://one.two.org"), &hint1));
+
+  EXPECT_EQ(page_hint3, FindPageHintForURL(
+                            GURL("https://www.foo.org/bar/three.jpg"), &hint1));
+}
+
+}  // namespace previews
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc
index ad5e2bb..c872c264 100644
--- a/components/previews/content/previews_optimization_guide.cc
+++ b/components/previews/content/previews_optimization_guide.cc
@@ -7,13 +7,16 @@
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
 #include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/optimization_guide_service.h"
 #include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/content/hint_cache_leveldb_store.h"
 #include "components/previews/content/previews_hints.h"
+#include "components/previews/content/previews_hints_util.h"
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/previews_constants.h"
 #include "components/previews/core/previews_switches.h"
@@ -23,6 +26,27 @@
 
 namespace {
 
+// The component version used with a manual config. This ensures that any hint
+// component received from the OptimizationGuideService on a subsequent startup
+// will have a newer version than it.
+constexpr char kManualConfigComponentVersion[] = "0.0.0";
+
+// Hints are purged during startup if the explicit purge switch exists or if
+// a proto override is being used--in which case the hints need to come from the
+// override instead.
+bool ShouldPurgeHintCacheStoreOnStartup() {
+  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+  return cmd_line->HasSwitch(switches::kHintsProtoOverride) ||
+         cmd_line->HasSwitch(switches::kPurgeHintCacheStore);
+}
+
+// Available hint components are only processed if a proto override isn't being
+// used; otherwise, the hints from the proto override are used instead.
+bool IsHintComponentProcessingDisabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kHintsProtoOverride);
+}
+
 // Attempts to parse a base64 encoded Optimization Guide Configuration proto
 // from the command line. If no proto is given or if it is encoded incorrectly,
 // nullptr is returned.
@@ -56,29 +80,25 @@
 
 PreviewsOptimizationGuide::PreviewsOptimizationGuide(
     optimization_guide::OptimizationGuideService* optimization_guide_service,
-    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+    const base::FilePath& profile_path)
     : optimization_guide_service_(optimization_guide_service),
       ui_task_runner_(ui_task_runner),
       background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
+      hint_cache_(std::make_unique<HintCache>(
+          std::make_unique<HintCacheLevelDBStore>(profile_path,
+                                                  background_task_runner_))),
       ui_weak_ptr_factory_(this) {
   DCHECK(optimization_guide_service_);
-
-  // Check if there is a valid hint proto given on the command line first. We
-  // don't normally expect one, but if one is provided then use that and do not
-  // register as an observer as the opt_guide service.
-  std::unique_ptr<optimization_guide::proto::Configuration> manual_config =
-      ParseHintsProtoFromCommandLine();
-  if (manual_config) {
-    // Allow |UpdateHints| to block startup so that the first navigation gets
-    // the hints.
-    UpdateHints(PreviewsHints::CreateFromHintsConfiguration(*manual_config));
-  } else {
-    optimization_guide_service_->AddObserver(this);
-  }
+  hint_cache_->Initialize(
+      ShouldPurgeHintCacheStoreOnStartup(),
+      base::BindOnce(&PreviewsOptimizationGuide::OnHintCacheInitialized,
+                     ui_weak_ptr_factory_.GetWeakPtr()));
 }
 
 PreviewsOptimizationGuide::~PreviewsOptimizationGuide() {
+  DCHECK(ui_task_runner_->BelongsToCurrentThread());
   optimization_guide_service_->RemoveObserver(this);
 }
 
@@ -88,16 +108,20 @@
     PreviewsType type,
     net::EffectiveConnectionType* out_ect_threshold) const {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
-  if (!hints_)
+  if (!hints_) {
     return false;
+  }
 
   *out_ect_threshold = params::GetECTThresholdForPreview(type);
   int inflation_percent = 0;
-  if (!hints_->IsWhitelisted(url, type, &inflation_percent, out_ect_threshold))
+  if (!hints_->IsWhitelisted(url, type, &inflation_percent,
+                             out_ect_threshold)) {
     return false;
+  }
 
-  if (inflation_percent != 0 && previews_data)
+  if (inflation_percent != 0 && previews_data) {
     previews_data->set_data_savings_inflation_percent(inflation_percent);
+  }
 
   return true;
 }
@@ -105,29 +129,37 @@
 bool PreviewsOptimizationGuide::IsBlacklisted(const GURL& url,
                                               PreviewsType type) const {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
-  if (!hints_)
+  if (!hints_) {
     return false;
+  }
 
   return hints_->IsBlacklisted(url, type);
 }
 
 void PreviewsOptimizationGuide::OnLoadedHint(
+    base::OnceClosure callback,
     const GURL& document_url,
-    const optimization_guide::proto::Hint& loaded_hint) const {
+    const optimization_guide::proto::Hint* loaded_hint) const {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
-  // TODO(dougarnett): Consider UMA to capture here else drop this callback.
+  // Run the callback now that the hint is loaded. This is used as a signal by
+  // tests.
+  std::move(callback).Run();
 }
 
-bool PreviewsOptimizationGuide::MaybeLoadOptimizationHints(const GURL& url) {
+bool PreviewsOptimizationGuide::MaybeLoadOptimizationHints(
+    const GURL& url,
+    base::OnceClosure callback) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
-  if (!hints_)
+  if (!hints_) {
     return false;
+  }
 
   return hints_->MaybeLoadOptimizationHints(
       url, base::BindOnce(&PreviewsOptimizationGuide::OnLoadedHint,
-                          ui_weak_ptr_factory_.GetWeakPtr(), url));
+                          ui_weak_ptr_factory_.GetWeakPtr(),
+                          std::move(callback), url));
 }
 
 bool PreviewsOptimizationGuide::GetResourceLoadingHints(
@@ -145,19 +177,55 @@
     const GURL& url,
     bool is_committed,
     net::EffectiveConnectionType ect) const {
-  if (!hints_)
+  if (!hints_) {
     return;
+  }
 
   hints_->LogHintCacheMatch(url, is_committed, ect);
 }
 
+void PreviewsOptimizationGuide::OnHintCacheInitialized() {
+  DCHECK(ui_task_runner_->BelongsToCurrentThread());
+  // Check if there is a valid hint proto given on the command line first. We
+  // don't normally expect one, but if one is provided then use that and do not
+  // register as an observer as the opt_guide service.
+  std::unique_ptr<optimization_guide::proto::Configuration> manual_config =
+      ParseHintsProtoFromCommandLine();
+  if (manual_config) {
+    // Allow |UpdateHints| to block startup so that the first navigation gets
+    // the hints when a command line hint proto is provided.
+    UpdateHints(PreviewsHints::CreateFromHintsConfiguration(
+        std::move(manual_config),
+        hint_cache_->MaybeCreateComponentUpdateData(
+            base::Version(kManualConfigComponentVersion))));
+  }
+  // Register as an observer regardless of hint proto override usage. This is
+  // needed as a signal during testing.
+  optimization_guide_service_->AddObserver(this);
+}
+
 void PreviewsOptimizationGuide::OnHintsComponentAvailable(
     const optimization_guide::HintsComponentInfo& info) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
+  // Check for if hint component is disabled. This check is needed because the
+  // optimization guide still registers with the service as an observer for
+  // components as a signal during testing.
+  if (IsHintComponentProcessingDisabled()) {
+    return;
+  }
+
+  // Create PreviewsHints from the newly available component on a background
+  // thread, providing a ComponentUpdateData from the hint cache, so that each
+  // hint within the component can be moved into it. In the case where the
+  // component's version is not newer than the hint cache store's component
+  // version, ComponentUpdateData will be a nullptr and hint processing will be
+  // skipped. After PreviewsHints::Create() returns the newly created
+  // PreviewsHints, it is initialized in UpdateHints() on the UI thread.
   base::PostTaskAndReplyWithResult(
       background_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&PreviewsHints::CreateFromHintsComponent, info),
+      base::BindOnce(&PreviewsHints::CreateFromHintsComponent, info,
+                     hint_cache_->MaybeCreateComponentUpdateData(info.version)),
       base::BindOnce(&PreviewsOptimizationGuide::UpdateHints,
                      ui_weak_ptr_factory_.GetWeakPtr()));
 }
@@ -166,7 +234,18 @@
     std::unique_ptr<PreviewsHints> hints) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
   hints_ = std::move(hints);
+  if (hints_) {
+    hints_->Initialize(
+        hint_cache_.get(),
+        base::BindOnce(&PreviewsOptimizationGuide::OnHintsUpdated,
+                       ui_weak_ptr_factory_.GetWeakPtr()));
+  } else {
+    OnHintsUpdated();
+  }
+}
 
+void PreviewsOptimizationGuide::OnHintsUpdated() {
+  DCHECK(ui_task_runner_->BelongsToCurrentThread());
   // Record the result of updating the hints. This is used as a signal for the
   // hints being fully processed in testing.
   LOCAL_HISTOGRAM_BOOLEAN(
diff --git a/components/previews/content/previews_optimization_guide.h b/components/previews/content/previews_optimization_guide.h
index 360b37e..6b300a9b 100644
--- a/components/previews/content/previews_optimization_guide.h
+++ b/components/previews/content/previews_optimization_guide.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_OPTIMIZATION_GUIDE_H_
 #define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_OPTIMIZATION_GUIDE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -14,10 +15,13 @@
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "components/optimization_guide/optimization_guide_service_observer.h"
-#include "components/previews/content/previews_optimization_guide.h"
+#include "components/previews/content/hint_cache.h"
 #include "components/previews/core/previews_experiments.h"
 #include "url/gurl.h"
 
+namespace base {
+class FilePath;
+}  // namespace base
 namespace optimization_guide {
 struct HintsComponentInfo;
 class OptimizationGuideService;
@@ -39,7 +43,8 @@
   // The embedder guarantees |optimization_guide_service| outlives |this|.
   PreviewsOptimizationGuide(
       optimization_guide::OptimizationGuideService* optimization_guide_service,
-      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+      const base::FilePath& profile_path);
 
   ~PreviewsOptimizationGuide() override;
 
@@ -60,8 +65,9 @@
   // Returns whether |request| may have associated optimization hints
   // (specifically, PageHints). If so, but the hints are not available
   // synchronously, this method will request that they be loaded (from disk or
-  // network).
-  bool MaybeLoadOptimizationHints(const GURL& url);
+  // network). The callback is run after the hint is loaded and can be used as
+  // a signal during tests.
+  bool MaybeLoadOptimizationHints(const GURL& url, base::OnceClosure callback);
 
   // Whether |url| has loaded resource loading hints and, if it does, populates
   // |out_resource_patterns_to_block| with the resource patterns to block.
@@ -77,18 +83,30 @@
                          net::EffectiveConnectionType ect) const;
 
   // optimization_guide::OptimizationGuideServiceObserver implementation:
+  // Called by OptimizationGuideService when a new component is available for
+  // processing.
   void OnHintsComponentAvailable(
       const optimization_guide::HintsComponentInfo& info) override;
 
   PreviewsHints* GetHintsForTesting() { return hints_.get(); }
 
  private:
+  // Callback run after the hint cache is fully initialized. At this point, the
+  // PreviewsOptimizationGuide is ready to process components from the
+  // OptimizationGuideService and registers as an observer with it.
+  void OnHintCacheInitialized();
+
   // Updates the hints to the latest hints sent by the Component Updater.
   void UpdateHints(std::unique_ptr<PreviewsHints> hints);
 
+  // Called when the hints have been fully updated with the latest hints from
+  // the Component Updater. This is used as a signal during tests.
+  void OnHintsUpdated();
+
   // Callback when a hint is loaded.
-  void OnLoadedHint(const GURL& document_url,
-                    const optimization_guide::proto::Hint& loaded_hint) const;
+  void OnLoadedHint(base::OnceClosure callback,
+                    const GURL& document_url,
+                    const optimization_guide::proto::Hint* loaded_hint) const;
 
   // The OptimizationGuideService that this guide is listening to. Not owned.
   optimization_guide::OptimizationGuideService* optimization_guide_service_;
@@ -99,6 +117,12 @@
   // Background thread where hints processing should be performed.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
+  // The hint cache used by PreviewsHints. It is owned by
+  // PreviewsOptimizationGuide so that the existing hint cache can be reused on
+  // component updates. Otherwise, a new cache and store would need to be
+  // created during each component update.
+  std::unique_ptr<HintCache> hint_cache_;
+
   // The current hints used for this optimization guide.
   std::unique_ptr<PreviewsHints> hints_;
 
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index ae3754b0..dfba646 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/bloom_filter.h"
+#include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -44,16 +45,24 @@
   explicit TestOptimizationGuideService(
       const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
       : OptimizationGuideService(ui_task_runner),
+        add_observer_called_(false),
         remove_observer_called_(false) {}
 
+  void AddObserver(
+      optimization_guide::OptimizationGuideServiceObserver* observer) override {
+    add_observer_called_ = true;
+  }
+
   void RemoveObserver(
       optimization_guide::OptimizationGuideServiceObserver* observer) override {
     remove_observer_called_ = true;
   }
 
+  bool AddObserverCalled() { return add_observer_called_; }
   bool RemoveObserverCalled() { return remove_observer_called_; }
 
  private:
+  bool add_observer_called_;
   bool remove_observer_called_;
 };
 
@@ -65,12 +74,7 @@
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    optimization_guide_service_ =
-        std::make_unique<TestOptimizationGuideService>(
-            scoped_task_environment_.GetMainThreadTaskRunner());
-    guide_ = std::make_unique<PreviewsOptimizationGuide>(
-        optimization_guide_service_.get(),
-        scoped_task_environment_.GetMainThreadTaskRunner());
+    CreateServiceAndGuide();
   }
 
   // Delete |guide_| if it hasn't been deleted.
@@ -89,13 +93,24 @@
         temp_dir().Append(FILE_PATH_LITERAL("somefile.pb")));
     ASSERT_NO_FATAL_FAILURE(WriteConfigToFile(config, info.path));
     guide_->OnHintsComponentAvailable(info);
+    RunUntilIdle();
   }
 
-  void NewGuide() {
+  void CreateServiceAndGuide() {
+    if (guide_) {
+      ResetGuide();
+    }
+    optimization_guide_service_ =
+        std::make_unique<TestOptimizationGuideService>(
+            scoped_task_environment_.GetMainThreadTaskRunner());
     guide_ = std::make_unique<PreviewsOptimizationGuide>(
         optimization_guide_service_.get(),
-        scoped_task_environment_.GetMainThreadTaskRunner());
-    RunUntilIdle();
+        scoped_task_environment_.GetMainThreadTaskRunner(), temp_dir());
+    // Add observer is called after the HintCache is fully initialized,
+    // indicating that the PreviewsOptimizationGuide is ready to process hints.
+    while (!optimization_guide_service_->AddObserverCalled()) {
+      RunUntilIdle();
+    }
   }
 
   void ResetGuide() {
@@ -133,6 +148,16 @@
   // server blacklist.
   void InitializeWithLitePageRedirectBlacklist();
 
+  // This function guarantees that all of the asynchronous processing required
+  // to load the specified hint has occurred prior to calling IsWhitelisted.
+  // It accomplishes this by calling MaybeLoadOptimizationHints() and waiting
+  // until OnLoadOptimizationHints runs before calling IsWhitelisted().
+  bool MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      PreviewsUserData* previews_data,
+      const GURL& url,
+      PreviewsType type,
+      net::EffectiveConnectionType* out_ect_threshold);
+
  private:
   void WriteConfigToFile(const optimization_guide::proto::Configuration& config,
                          const base::FilePath& filePath) {
@@ -143,12 +168,23 @@
                               serialized_config.length()));
   }
 
+  // Callback used to indicate that the asynchronous call to
+  // MaybeLoadOptimizationHints() has completed its processing.
+  void OnLoadOptimizationHints();
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::ScopedTempDir temp_dir_;
 
   std::unique_ptr<PreviewsOptimizationGuide> guide_;
   std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_;
 
+  // Flag set when the OnLoadOptimizationHints callback runs. This indicates
+  // that MaybeLoadOptimizationHints() has completed its processing.
+  bool requested_hints_loaded_;
+
+  GURL loaded_hints_document_gurl_;
+  std::vector<std::string> loaded_hints_resource_patterns_;
+
   DISALLOW_COPY_AND_ASSIGN(PreviewsOptimizationGuideTest);
 };
 
@@ -189,9 +225,8 @@
   resource_loading_hint3->set_loading_optimization_type(
       optimization_guide::proto::LOADING_BLOCK_RESOURCE);
   resource_loading_hint3->set_resource_pattern("barball_cruft.js");
-  ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
+  ProcessHints(config, "2.0.0");
 }
 
 void PreviewsOptimizationGuideTest::
@@ -226,9 +261,8 @@
   resource_loading_hint2->set_loading_optimization_type(
       optimization_guide::proto::LOADING_BLOCK_RESOURCE);
   resource_loading_hint2->set_resource_pattern("football_cruft.js");
-  ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
+  ProcessHints(config, "2.0.0");
 }
 
 void PreviewsOptimizationGuideTest::InitializeMultipleResourceLoadingHints(
@@ -265,9 +299,8 @@
       resource_loading_hint_2->set_resource_pattern("news_cruft_2.js");
     }
   }
-  ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
+  ProcessHints(config, "2.0.0");
 }
 
 void PreviewsOptimizationGuideTest::InitializeWithLitePageRedirectBlacklist() {
@@ -288,16 +321,41 @@
   bloom_filter_proto->set_data(blacklist_data);
   blacklist_proto->set_allocated_bloom_filter(bloom_filter_proto.release());
   ProcessHints(config, "2.0.0");
+}
 
-  RunUntilIdle();
+bool PreviewsOptimizationGuideTest::
+    MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+        PreviewsUserData* previews_data,
+        const GURL& url,
+        PreviewsType type,
+        net::EffectiveConnectionType* out_ect_threshold) {
+  // Ensure that all asynchronous MaybeLoadOptimizationHints processing
+  // finishes prior to calling IsWhitelisted. This is accomplished by waiting
+  // for the OnLoadOptimizationHints callback to set |requested_hints_loaded_|
+  // to true.
+  requested_hints_loaded_ = false;
+  if (guide()->MaybeLoadOptimizationHints(
+          url, base::BindOnce(
+                   &PreviewsOptimizationGuideTest::OnLoadOptimizationHints,
+                   base::Unretained(this)))) {
+    while (!requested_hints_loaded_) {
+      RunUntilIdle();
+    }
+  }
+
+  return guide()->IsWhitelisted(previews_data, url, type, out_ect_threshold);
+}
+
+void PreviewsOptimizationGuideTest::OnLoadOptimizationHints() {
+  requested_hints_loaded_ = true;
 }
 
 TEST_F(PreviewsOptimizationGuideTest, IsWhitelistedWithoutHints) {
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data,
-                                      GURL("https://m.facebook.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
@@ -333,31 +391,32 @@
   optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
 
   // Verify page matches and ECT thresholds.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://somedomain.org/noscript_default_2g"),
       PreviewsType::NOSCRIPT, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data,
-                                     GURL("https://somedomain.org/noscript_3g"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://somedomain.org/noscript_3g"),
+      PreviewsType::NOSCRIPT, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G, ect_threshold);
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://somedomain.org/no_pattern_match"),
       PreviewsType::NOSCRIPT, &ect_threshold));
 
   // Verify * matches any page.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://anypage.com/noscript_for_all"),
       PreviewsType::NOSCRIPT, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://anypage.com/"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://anypage.com"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://anypage.com/"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://anypage.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
@@ -378,27 +437,112 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kHintsProtoOverride, encoded_config);
-  NewGuide();
+  CreateServiceAndGuide();
 
   // Verify page matches and ECT thresholds.
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
+
   EXPECT_TRUE(guide()->GetHintsForTesting());
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://somedomain.org/noscript_default_2g"),
       PreviewsType::NOSCRIPT, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
+       ProcessHintsWithValidCommandLineOverrideAndPreexistingData) {
+  InitializeFixedCountResourceLoadingHints();
+
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+
+  optimization_guide::proto::Configuration config;
+  optimization_guide::proto::Hint* hint = config.add_hints();
+  hint->set_key("otherdomain.org");
+  hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
+  page_hint->set_page_pattern("noscript_default_2g");
+  optimization_guide::proto::Optimization* optimization =
+      page_hint->add_whitelisted_optimizations();
+  optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  std::string encoded_config;
+  config.SerializeToString(&encoded_config);
+  base::Base64Encode(encoded_config, &encoded_config);
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kHintsProtoOverride, encoded_config);
+  CreateServiceAndGuide();
+
+  // Verify page matches and ECT thresholds.
+  PreviewsUserData user_data(kDefaultPageId);
+  net::EffectiveConnectionType ect_threshold;
+
+  EXPECT_TRUE(guide()->GetHintsForTesting());
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://otherdomain.org/noscript_default_2g"),
+      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
+
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
        ProcessHintsWithInvalidCommandLineOverride) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kHintsProtoOverride, "this-is-not-a-proto");
-  NewGuide();
+  CreateServiceAndGuide();
 
   EXPECT_FALSE(guide()->GetHintsForTesting());
 }
 
+TEST_F(PreviewsOptimizationGuideTest,
+       ProcessHintsWithPurgeHintCacheStoreCommandLineAndNoPreexistingData) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kPurgeHintCacheStore);
+  CreateServiceAndGuide();
+
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+
+  InitializeFixedCountResourceLoadingHints();
+
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       ProcessHintsWithPurgeHintCacheStoreCommandLineAndPreexistingData) {
+  InitializeFixedCountResourceLoadingHints();
+
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kPurgeHintCacheStore);
+  CreateServiceAndGuide();
+
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+
+  InitializeFixedCountResourceLoadingHints();
+
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+}
+
 // Test when resource loading hints are enabled.
 TEST_F(PreviewsOptimizationGuideTest,
        ProcessHintsForResourceLoadingHintsPopulatedCorrectly) {
@@ -448,21 +592,19 @@
 
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
-
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Twitter and Facebook should be whitelisted but not Google.
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                     PreviewsType::RESOURCE_LOADING_HINTS,
-                                     &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://m.twitter.com/example"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data, GURL("https://google.com"),
-                                      PreviewsType::RESOURCE_LOADING_HINTS,
-                                      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://google.com"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
 
 // Test when both NoScript and resource loading hints are enabled.
@@ -513,21 +655,21 @@
 
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Twitter and Facebook should be whitelisted but not Google.
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://m.facebook.com/example.html"),
       PreviewsType::NOSCRIPT, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://m.twitter.com/example"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data, GURL("https://google.com"),
-                                      PreviewsType::RESOURCE_LOADING_HINTS,
-                                      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://google.com"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
@@ -577,21 +719,19 @@
 
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
-
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://3g.com"),
-                                     PreviewsType::RESOURCE_LOADING_HINTS,
-                                     &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://3g.com"), PreviewsType::RESOURCE_LOADING_HINTS,
+      &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G, ect_threshold);
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://4g.com/example"),
-                                     PreviewsType::RESOURCE_LOADING_HINTS,
-                                     &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://4g.com/example"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_4G, ect_threshold);
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://default2g.com"),
-                                     PreviewsType::RESOURCE_LOADING_HINTS,
-                                     &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://default2g.com"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
 }
 
@@ -647,34 +787,33 @@
   optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
-
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Check to ensure the optimization under test (facebook noscript) is either
   // enabled or disabled, depending on what the caller told us to expect.
-  EXPECT_EQ(expect_enabled,
-            guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                   PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_EQ(expect_enabled, MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+                                &user_data, GURL("https://m.facebook.com"),
+                                PreviewsType::NOSCRIPT, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
 
   // RESOURCE_LOADING_HINTS for facebook should always be enabled.
   ect_threshold = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                     PreviewsType::RESOURCE_LOADING_HINTS,
-                                     &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"),
+      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
   // Twitter's NOSCRIPT should always be enabled; RESOURCE_LOADING_HINTS is not
   // configured and should be disabled.
   ect_threshold = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data,
-                                     GURL("https://m.twitter.com/example"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.twitter.com/example"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
   // Google (which is not configured at all) should always have both NOSCRIPT
   // and RESOURCE_LOADING_HINTS disabled.
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data, GURL("https://google.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
@@ -745,13 +884,11 @@
   optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
-
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data,
-                                      GURL("https://m.facebook.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest,
@@ -769,13 +906,11 @@
 
   ProcessHints(config, "2.0.0");
 
-  RunUntilIdle();
-
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data,
-                                      GURL("https://m.facebook.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
 }
 
 TEST_F(PreviewsOptimizationGuideTest, ProcessHintsWithExistingSentinel) {
@@ -799,25 +934,26 @@
 
   // Verify config not processed for version 2.0.0 (same as sentinel).
   ProcessHints(config, "2.0.0");
-  RunUntilIdle();
+
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data,
-                                      GURL("https://m.facebook.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
   EXPECT_TRUE(base::PathExists(sentinel_path));
   histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
-                                      2 /* FAILED_FINISH_PROCESSING */, 1);
+                                      2 /* kFailedFinishProcessing */, 1);
 
   // Now verify config is processed for different version and sentinel cleared.
   ProcessHints(config, "3.0.0");
-  RunUntilIdle();
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
   EXPECT_FALSE(base::PathExists(sentinel_path));
   histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
-                                     1 /* PROCESSED_PREVIEWS_HINTS */, 1);
+                                     1 /* kProcessedPreviewsHints */, 1);
 }
 
 TEST_F(PreviewsOptimizationGuideTest, ProcessHintsWithInvalidSentinelFile) {
@@ -842,24 +978,184 @@
   // Verify config not processed for existing sentinel with bad value but
   // that the existinel sentinel file is deleted.
   ProcessHints(config, "2.0.0");
-  RunUntilIdle();
+
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(&user_data,
-                                      GURL("https://m.facebook.com"),
-                                      PreviewsType::NOSCRIPT, &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
   EXPECT_FALSE(base::PathExists(sentinel_path));
   histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
-                                      2 /* FAILED_FINISH_PROCESSING */, 1);
+                                      2 /* kFailedFinishProcessing */, 1);
 
   // Now verify config is processed with sentinel cleared.
   ProcessHints(config, "2.0.0");
-  RunUntilIdle();
-  EXPECT_TRUE(guide()->IsWhitelisted(&user_data, GURL("https://m.facebook.com"),
-                                     PreviewsType::NOSCRIPT, &ect_threshold));
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
   EXPECT_FALSE(base::PathExists(sentinel_path));
   histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
-                                     1 /* PROCESSED_PREVIEWS_HINTS */, 1);
+                                     1 /* kProcessedPreviewsHints */, 1);
+}
+
+TEST_F(PreviewsOptimizationGuideTest, SkipHintProcessingForSameConfigVersion) {
+  base::HistogramTester histogram_tester;
+
+  optimization_guide::proto::Configuration config1;
+  optimization_guide::proto::Hint* hint1 = config1.add_hints();
+  hint1->set_key("facebook.com");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
+  page_hint1->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization1 =
+      page_hint1->add_whitelisted_optimizations();
+  optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  optimization_guide::proto::Configuration config2;
+  optimization_guide::proto::Hint* hint2 = config2.add_hints();
+  hint2->set_key("google.com");
+  hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
+  page_hint2->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization2 =
+      page_hint2->add_whitelisted_optimizations();
+  optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  PreviewsUserData user_data(kDefaultPageId);
+  net::EffectiveConnectionType ect_threshold;
+
+  // Process the new hints config and verify that they are available.
+  ProcessHints(config1, "2.0.0");
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
+                                      1 /* kProcessedPreviewsHints */, 1);
+
+  // Verify hint config with the same version as the previously processed config
+  // is skipped.
+  ProcessHints(config2, "2.0.0");
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
+                                     3 /* kSkippedProcessingPreviewsHints */,
+                                     1);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       SkipHintProcessingForEarlierConfigVersion) {
+  base::HistogramTester histogram_tester;
+
+  optimization_guide::proto::Configuration config1;
+  optimization_guide::proto::Hint* hint1 = config1.add_hints();
+  hint1->set_key("facebook.com");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
+  page_hint1->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization1 =
+      page_hint1->add_whitelisted_optimizations();
+  optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  optimization_guide::proto::Configuration config2;
+  optimization_guide::proto::Hint* hint2 = config2.add_hints();
+  hint2->set_key("google.com");
+  hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
+  page_hint2->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization2 =
+      page_hint2->add_whitelisted_optimizations();
+  optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  PreviewsUserData user_data(kDefaultPageId);
+  net::EffectiveConnectionType ect_threshold;
+
+  // Process the new hints config and verify that they are available.
+  ProcessHints(config1, "2.0.0");
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
+                                      1 /* kProcessedPreviewsHints */, 1);
+
+  // Verify hint config with an earlier version than the previously processed
+  // one is skipped.
+  ProcessHints(config2, "1.0.0");
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
+                                     3 /* kSkippedProcessingPreviewsHints */,
+                                     1);
+}
+
+TEST_F(PreviewsOptimizationGuideTest, ProcessMultipleNewConfigs) {
+  base::HistogramTester histogram_tester;
+
+  optimization_guide::proto::Configuration config1;
+  optimization_guide::proto::Hint* hint1 = config1.add_hints();
+  hint1->set_key("facebook.com");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
+  page_hint1->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization1 =
+      page_hint1->add_whitelisted_optimizations();
+  optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  optimization_guide::proto::Configuration config2;
+  optimization_guide::proto::Hint* hint2 = config2.add_hints();
+  hint2->set_key("google.com");
+  hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
+  page_hint2->set_page_pattern("*");
+  optimization_guide::proto::Optimization* optimization2 =
+      page_hint2->add_whitelisted_optimizations();
+  optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+
+  PreviewsUserData user_data(kDefaultPageId);
+  net::EffectiveConnectionType ect_threshold;
+
+  // Process the new hints config and verify that they are available.
+  ProcessHints(config1, "2.0.0");
+
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
+                                      1 /* kProcessedPreviewsHints */, 1);
+
+  // Verify hint config with a newer version then the previously processed one
+  // is processed.
+  ProcessHints(config2, "3.0.0");
+
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
+      &ect_threshold));
+  histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
+                                     1 /* kProcessedPreviewsHints */, 2);
 }
 
 TEST_F(PreviewsOptimizationGuideTest, ProcessHintConfigWithNoKeyFailsDcheck) {
@@ -874,7 +1170,6 @@
 
   EXPECT_DCHECK_DEATH({
     ProcessHints(config, "2.0.0");
-    RunUntilIdle();
   });
 }
 
@@ -900,7 +1195,6 @@
 
   EXPECT_DCHECK_DEATH({
     ProcessHints(config, "2.0.0");
-    RunUntilIdle();
   });
 }
 
@@ -911,12 +1205,12 @@
 
   InitializeFixedCountResourceLoadingHints();
 
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://www.somedomain.org/news/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
   RunUntilIdle();
   histogram_tester.ExpectUniqueSample(
@@ -929,15 +1223,15 @@
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Verify whitelisting from loaded page hints.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain.org/unhinted"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
@@ -950,33 +1244,34 @@
 
   InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
 
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
   EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://www.somedomain.org/news/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
   RunUntilIdle();
+
   histogram_tester.ExpectUniqueSample(
       "ResourceLoadingHints.PageHints.ProcessedCount", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived", 0, 1);
+      "ResourceLoadingHints.ResourceHints.TotalReceived", 2, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived", 0, 1);
+      "ResourceLoadingHints.PageHints.TotalReceived", 2, 1);
 
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Verify whitelisting from loaded page hints.
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain.org/unhinted"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
@@ -994,33 +1289,34 @@
 
   InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
 
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://www.somedomain.org/news/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
   RunUntilIdle();
+
   histogram_tester.ExpectUniqueSample(
       "ResourceLoadingHints.PageHints.ProcessedCount", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.ResourceHints.TotalReceived", 2, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.PageHints.TotalReceived", 2, 1);
 
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Verify whitelisting from loaded page hints.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain.org/unhinted"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
@@ -1028,6 +1324,7 @@
 TEST_F(PreviewsOptimizationGuideTest,
        MaybeLoadPageHintsWithSecondExperimentEnabled) {
   base::HistogramTester histogram_tester;
+
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
 
@@ -1038,33 +1335,34 @@
 
   InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
 
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://www.somedomain.org/news/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
   RunUntilIdle();
+
   histogram_tester.ExpectUniqueSample(
       "ResourceLoadingHints.PageHints.ProcessedCount", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.ResourceHints.TotalReceived", 2, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.PageHints.TotalReceived", 2, 1);
 
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Verify whitelisting from loaded page hints.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain.org/unhinted"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
@@ -1083,33 +1381,34 @@
 
   InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
 
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://www.somedomain.org/news/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://somedomain.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
   RunUntilIdle();
+
   histogram_tester.ExpectUniqueSample(
       "ResourceLoadingHints.PageHints.ProcessedCount", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.ResourceHints.TotalReceived", 2, 1);
   histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived", 1, 1);
+      "ResourceLoadingHints.PageHints.TotalReceived", 2, 1);
 
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
   // Verify whitelisting from loaded page hints.
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain.org/unhinted"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 }
@@ -1122,223 +1421,59 @@
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
 
-  const size_t key_count = 20;
+  const size_t key_count = 50;
   const size_t page_patterns_per_key = 25;
 
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
 
-  ASSERT_EQ(previews::params::GetMaxPageHintsInMemoryThreshhold(),
-            key_count * page_patterns_per_key);
-
-  // Count of page patterns is within the threshold.
-  ASSERT_LE(key_count * page_patterns_per_key,
-            previews::params::GetMaxPageHintsInMemoryThreshhold());
-
   InitializeMultipleResourceLoadingHints(key_count, page_patterns_per_key);
 
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain0.org/")));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://somedomain0.org/news0/football")));
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain19.org/")));
+      GURL("https://somedomain0.org/"), base::DoNothing()));
   EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://somedomain19.org/news0/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain20.org/")));
+      GURL("https://somedomain0.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain0.org/news0/football"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain49.org/"), base::DoNothing()));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain49.org/news0/football"), base::DoNothing()));
   EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
-      GURL("https://somedomain20.org/news0/football")));
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
+      GURL("https://somedomain50.org/"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://somedomain50.org/news0/football"), base::DoNothing()));
+  EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.unknown.com"), base::DoNothing()));
 
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain0.org/news0/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain0.org/news24/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data, GURL("https://www.somedomain0.org/news25/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news0/football"),
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain49.org/news0/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news24/football"),
+  EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain49.org/news24/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news25/football"),
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain49.org/news25/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news0/football"),
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain50.org/news0/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news24/football"),
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain50.org/news24/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  RunUntilIdle();
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.ProcessedCount", page_patterns_per_key,
-      key_count);
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived",
-      key_count * page_patterns_per_key * 2, 1);
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived",
-      key_count * page_patterns_per_key, 1);
-}
-
-// Test that only up to GetMaxPageHintsInMemoryThreshhold() page hints
-// are loaded to the memory.
-TEST_F(PreviewsOptimizationGuideTest,
-       LoadTooManyResourceLoadingOptimizationHints) {
-  base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
-
-  const size_t key_count = 21;
-  const size_t page_patterns_per_key = 25;
-
-  PreviewsUserData user_data(kDefaultPageId);
-  net::EffectiveConnectionType ect_threshold;
-
-  ASSERT_EQ(previews::params::GetMaxPageHintsInMemoryThreshhold(),
-            20u * page_patterns_per_key);
-
-  // Provide more page patterns than the threshold.
-  ASSERT_GT(key_count * page_patterns_per_key,
-            previews::params::GetMaxPageHintsInMemoryThreshhold());
-
-  InitializeMultipleResourceLoadingHints(key_count, page_patterns_per_key);
-
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain0.org/")));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news24/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain19.org/")));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news24/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  // The last page pattern should be dropped since it exceeds the threshold
-  // count.
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain20.org/")));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news24/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  RunUntilIdle();
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.ProcessedCount", page_patterns_per_key,
-      key_count);
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.ResourceHints.TotalReceived",
-      key_count * page_patterns_per_key * 2, 1);
-  histogram_tester.ExpectUniqueSample(
-      "ResourceLoadingHints.PageHints.TotalReceived",
-      key_count * page_patterns_per_key, 1);
-}
-
-// Test that only up to GetMaxPageHintsInMemoryThreshhold() page hints
-// are loaded to the memory.
-TEST_F(PreviewsOptimizationGuideTest,
-       LoadTooManyResourceLoadingOptimizationHintsWithPartialPageHint) {
-  base::HistogramTester histogram_tester;
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
-
-  const size_t key_count = 21;
-  const size_t page_patterns_per_key = 26;
-
-  PreviewsUserData user_data(kDefaultPageId);
-  net::EffectiveConnectionType ect_threshold;
-
-  // Provide more page patterns than the threshold.
-  ASSERT_GT(key_count * page_patterns_per_key,
-            previews::params::GetMaxPageHintsInMemoryThreshhold());
-
-  InitializeMultipleResourceLoadingHints(key_count, page_patterns_per_key);
-
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain0.org/")));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain0.org/news26/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  // The third to last page pattern has all of its resource loading hints fall
-  // within the threshold.
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain18.org/")));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain18.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain18.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain18.org/news26/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  // The second to last page pattern had some of its resource loading hints
-  // fall within the threshold.
-  EXPECT_TRUE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain19.org/")));
-  EXPECT_TRUE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain19.org/news26/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-
-  // The last page pattern should be dropped since all of its resource loading
-  // hints exceeds the threshold count.
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://somedomain20.org/")));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news0/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news25/football"),
-      PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
-  EXPECT_FALSE(guide()->IsWhitelisted(
-      &user_data, GURL("https://www.somedomain20.org/news26/football"),
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
+      &user_data, GURL("https://www.somedomain50.org/news25/football"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
 
   RunUntilIdle();
@@ -1362,13 +1497,14 @@
 
   InitializeFixedCountResourceLoadingHints();
 
-  EXPECT_FALSE(
-      guide()->MaybeLoadOptimizationHints(GURL("https://www.somedomain.org")));
+  EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
+      GURL("https://www.somedomain.org"), base::DoNothing()));
 
   RunUntilIdle();
+
   PreviewsUserData user_data(kDefaultPageId);
   net::EffectiveConnectionType ect_threshold;
-  EXPECT_FALSE(guide()->IsWhitelisted(
+  EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
       &user_data,
       GURL("https://www.somedomain.org/news/weather/raininginseattle"),
       PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
diff --git a/components/previews/content/proto/BUILD.gn b/components/previews/content/proto/BUILD.gn
new file mode 100644
index 0000000..8bc279bc
--- /dev/null
+++ b/components/previews/content/proto/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+copy("hints_header_include") {
+  sources = [
+    "hints_header_include.h",
+  ]
+  outputs = [
+    "$root_gen_dir" + "/components/previews/content/proto/hints.pb.h",
+  ]
+  deps = [
+    "//components/optimization_guide/proto:optimization_guide_proto",
+  ]
+}
+
+proto_library("hint_cache_proto") {
+  import_dirs = [ "//components/optimization_guide/proto" ]
+  sources = [
+    "hint_cache.proto",
+  ]
+  deps = [
+    ":hints_header_include",
+    "//components/optimization_guide/proto:optimization_guide_proto",
+  ]
+}
diff --git a/components/previews/content/proto/DEPS b/components/previews/content/proto/DEPS
new file mode 100644
index 0000000..f1d879cf
--- /dev/null
+++ b/components/previews/content/proto/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/optimization_guide/proto",
+]
diff --git a/components/previews/content/proto/hint_cache.proto b/components/previews/content/proto/hint_cache.proto
new file mode 100644
index 0000000..9f795aa8
--- /dev/null
+++ b/components/previews/content/proto/hint_cache.proto
@@ -0,0 +1,22 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package previews.proto;
+
+// Imports //components/optimization_guide/proto/hints.proto but uses local
+// path here with import_dirs in build file.
+import "hints.proto";
+
+message StoreEntry {
+  // Generic version field. For the schema metadata entry, version represents
+  // the schema version; for the component metadata entry, version represents
+  // the component version.
+  optional string version = 1;
+  // The actual hint data.
+  optional optimization_guide.proto.Hint hint = 2;
+}
diff --git a/components/previews/content/proto/hints_header_include.h b/components/previews/content/proto/hints_header_include.h
new file mode 100644
index 0000000..3abb136
--- /dev/null
+++ b/components/previews/content/proto/hints_header_include.h
@@ -0,0 +1,12 @@
+// 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.
+
+#ifndef COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
+#define COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
+
+// This is a bridge to include the generated proto header for the
+// optimization_guide proto.
+#include "components/optimization_guide/proto/hints.pb.h"
+
+#endif  // COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
diff --git a/components/previews/core/previews_switches.cc b/components/previews/core/previews_switches.cc
index 0c0cd25..455d84d 100644
--- a/components/previews/core/previews_switches.cc
+++ b/components/previews/core/previews_switches.cc
@@ -33,5 +33,9 @@
 // valid value to this switch causes Chrome startup to block on hints parsing.
 const char kHintsProtoOverride[] = "optimization_guide_hints_override";
 
+// Purges the hint cache store on startup, so that it's guaranteed to be using
+// fresh data.
+const char kPurgeHintCacheStore[] = "purge_hint_cache_store";
+
 }  // namespace switches
 }  // namespace previews
diff --git a/components/previews/core/previews_switches.h b/components/previews/core/previews_switches.h
index 0fe70a78..b258e7db6 100644
--- a/components/previews/core/previews_switches.h
+++ b/components/previews/core/previews_switches.h
@@ -14,6 +14,7 @@
 extern const char kIgnoreLitePageRedirectOptimizationBlacklist[];
 extern const char kClearLitePageRedirectLocalBlacklist[];
 extern const char kHintsProtoOverride[];
+extern const char kPurgeHintCacheStore[];
 
 }  // namespace switches
 }  // namespace previews
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 748bc943..28e4c1b1 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -670,7 +670,7 @@
                                page_layout.content_height);
 
   blink::WebView* web_view = blink::WebView::Create(
-      /*client=*/nullptr, /*widget_client=*/nullptr,
+      /*client=*/nullptr,
       /*is_hidden=*/false, /*compositing_enabled=*/false, /*opener=*/nullptr);
   web_view->GetSettings()->SetJavaScriptEnabled(true);
 
@@ -922,11 +922,10 @@
   prefs.javascript_enabled = false;
 
   blink::WebView* web_view = blink::WebView::Create(
-      /*client=*/this, /*widget_client=*/this,
+      /*client=*/this,
       /*is_hidden=*/false,
       /*compositing_enabled=*/false,
       /*opener=*/nullptr);
-  owns_web_view_ = true;
   content::RenderView::ApplyWebPreferences(prefs, web_view);
   blink::mojom::DocumentInterfaceBrokerPtrInfo document_interface_broker;
   blink::WebLocalFrame* main_frame = blink::WebLocalFrame::CreateMainFrame(
@@ -936,6 +935,8 @@
   blink::WebFrameWidget::CreateForMainFrame(this, main_frame);
   node_to_print_.Reset();
 
+  owns_web_view_ = true;
+
   // When loading is done this will call didStopLoading() and that will do the
   // actual printing.
   navigation_control_->CommitNavigation(
diff --git a/components/signin/ios/browser/BUILD.gn b/components/signin/ios/browser/BUILD.gn
index 77530c4..088e53e 100644
--- a/components/signin/ios/browser/BUILD.gn
+++ b/components/signin/ios/browser/BUILD.gn
@@ -10,8 +10,6 @@
     "manage_accounts_delegate.h",
     "merge_session_observer_bridge.h",
     "merge_session_observer_bridge.mm",
-    "oauth2_token_service_observer_bridge.h",
-    "oauth2_token_service_observer_bridge.mm",
     "profile_oauth2_token_service_ios_delegate.h",
     "profile_oauth2_token_service_ios_delegate.mm",
     "profile_oauth2_token_service_ios_provider.h",
diff --git a/components/signin/ios/browser/oauth2_token_service_observer_bridge.h b/components/signin/ios/browser/oauth2_token_service_observer_bridge.h
deleted file mode 100644
index a168fac..0000000
--- a/components/signin/ios/browser/oauth2_token_service_observer_bridge.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SIGNIN_IOS_BROWSER_OAUTH2_TOKEN_SERVICE_OBSERVER_BRIDGE_H_
-#define COMPONENTS_SIGNIN_IOS_BROWSER_OAUTH2_TOKEN_SERVICE_OBSERVER_BRIDGE_H_
-
-#import <Foundation/Foundation.h>
-
-#include "base/macros.h"
-#include "google_apis/gaia/oauth2_token_service.h"
-
-@protocol OAuth2TokenServiceObserverBridgeDelegate <NSObject>
-
-@optional
-
-// Informs the delegate that a refresh token is avaible for |account_id|.
-- (void)onRefreshTokenAvailable:(const std::string&)account_id;
-
-// Informs the delegate that the refresh token was revoked for |account_id|.
-- (void)onRefreshTokenRevoked:(const std::string&)account_id;
-
-// Informs the delegate that the token service has finished loading the tokens.
-- (void)onRefreshTokensLoaded;
-
-// Informs the delegate that a batch of refresh token changes will start.
-- (void)onStartBatchChanges;
-
-// Informs the delegate that a batch of refresh token changes has ended.
-- (void)onEndBatchChanges;
-
-@end
-
-// Bridge class that listens for |OAuth2TokenService| notifications and passes
-// them to its Objective-C delegate.
-class OAuth2TokenServiceObserverBridge : public OAuth2TokenService::Observer {
- public:
-  OAuth2TokenServiceObserverBridge(
-      OAuth2TokenService* token_service,
-      id<OAuth2TokenServiceObserverBridgeDelegate> delegate);
-  ~OAuth2TokenServiceObserverBridge() override;
-
-  // OAuth2TokenService::Observer
-  void OnRefreshTokenAvailable(const std::string& account_id) override;
-  void OnRefreshTokenRevoked(const std::string& account_id) override;
-  void OnRefreshTokensLoaded() override;
-  void OnStartBatchChanges() override;
-  void OnEndBatchChanges() override;
-
- private:
-  OAuth2TokenService* token_service_;  // weak
-  __weak id<OAuth2TokenServiceObserverBridgeDelegate> delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceObserverBridge);
-};
-
-#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_OAUTH2_TOKEN_SERVICE_OBSERVER_BRIDGE_H_
diff --git a/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm b/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm
deleted file mode 100644
index ddaddb0..0000000
--- a/components/signin/ios/browser/oauth2_token_service_observer_bridge.mm
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-OAuth2TokenServiceObserverBridge::OAuth2TokenServiceObserverBridge(
-    OAuth2TokenService* token_service,
-    id<OAuth2TokenServiceObserverBridgeDelegate> delegate)
-        : token_service_(token_service),
-          delegate_(delegate) {
-    DCHECK(token_service_);
-    token_service_->AddObserver(this);
-}
-OAuth2TokenServiceObserverBridge::~OAuth2TokenServiceObserverBridge() {
-  token_service_->RemoveObserver(this);
-}
-
-void OAuth2TokenServiceObserverBridge::OnRefreshTokenAvailable(
-    const std::string& account_id) {
-  if ([delegate_ respondsToSelector:@selector(onRefreshTokenAvailable:)]) {
-    [delegate_ onRefreshTokenAvailable:account_id];
-  }
-}
-
-void OAuth2TokenServiceObserverBridge::OnRefreshTokenRevoked(
-    const std::string& account_id) {
-  if ([delegate_ respondsToSelector:@selector(onRefreshTokenRevoked:)]) {
-    [delegate_ onRefreshTokenRevoked:account_id];
-  }
-}
-void OAuth2TokenServiceObserverBridge::OnRefreshTokensLoaded() {
-  if ([delegate_ respondsToSelector:@selector(onRefreshTokensLoaded)]) {
-    [delegate_ onRefreshTokensLoaded];
-  }
-}
-void OAuth2TokenServiceObserverBridge::OnStartBatchChanges() {
-  if ([delegate_ respondsToSelector:@selector(onStartBatchChanges)]) {
-    [delegate_ onStartBatchChanges];
-  }
-
-}
-
-void OAuth2TokenServiceObserverBridge::OnEndBatchChanges() {
-  if ([delegate_ respondsToSelector:@selector(onEndBatchChanges)]) {
-    [delegate_ onEndBatchChanges];
-  }
-}
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 2b5478f57..d1bb6ab4 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -77,6 +77,11 @@
       prefs::kSyncFirstSetupComplete, pref_service_,
       base::BindRepeating(&SyncPrefs::OnFirstSetupCompletePrefChange,
                           base::Unretained(this)));
+  pref_sync_suppressed_.Init(
+      prefs::kSyncSuppressStart, pref_service_,
+      base::BindRepeating(&SyncPrefs::OnSyncSuppressedPrefChange,
+                          base::Unretained(this)));
+
   // Cache the value of the kEnableLocalSyncBackend pref to avoid it flipping
   // during the lifetime of the service.
   local_sync_enabled_ =
@@ -435,6 +440,13 @@
     observer.OnFirstSetupCompletePrefChange(*pref_first_setup_complete_);
 }
 
+void SyncPrefs::OnSyncSuppressedPrefChange() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Note: The pref is inverted for historic reasons; see IsSyncRequested.
+  for (SyncPrefObserver& observer : sync_pref_observers_)
+    observer.OnSyncRequestedPrefChange(!*pref_sync_suppressed_);
+}
+
 void SyncPrefs::SetManagedForTest(bool is_managed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   pref_service_->SetBoolean(prefs::kSyncManaged, is_managed);
diff --git a/components/sync/base/sync_prefs.h b/components/sync/base/sync_prefs.h
index 9145e31..d7d7eddca 100644
--- a/components/sync/base/sync_prefs.h
+++ b/components/sync/base/sync_prefs.h
@@ -34,6 +34,7 @@
  public:
   virtual void OnSyncManagedPrefChange(bool is_sync_managed) = 0;
   virtual void OnFirstSetupCompletePrefChange(bool is_first_setup_complete) = 0;
+  virtual void OnSyncRequestedPrefChange(bool is_sync_requested) = 0;
   virtual void OnPreferredDataTypesPrefChange(
       bool sync_everything,
       syncer::ModelTypeSet preferred_types) = 0;
@@ -228,6 +229,7 @@
 
   void OnSyncManagedPrefChanged();
   void OnFirstSetupCompletePrefChange();
+  void OnSyncSuppressedPrefChange();
 
   // Never null.
   PrefService* const pref_service_;
@@ -240,6 +242,8 @@
 
   BooleanPrefMember pref_first_setup_complete_;
 
+  BooleanPrefMember pref_sync_suppressed_;
+
   bool local_sync_enabled_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/components/sync/base/sync_prefs_unittest.cc b/components/sync/base/sync_prefs_unittest.cc
index 4986462..362998d 100644
--- a/components/sync/base/sync_prefs_unittest.cc
+++ b/components/sync/base/sync_prefs_unittest.cc
@@ -75,6 +75,7 @@
  public:
   MOCK_METHOD1(OnSyncManagedPrefChange, void(bool));
   MOCK_METHOD1(OnFirstSetupCompletePrefChange, void(bool));
+  MOCK_METHOD1(OnSyncRequestedPrefChange, void(bool));
   MOCK_METHOD2(OnPreferredDataTypesPrefChange, void(bool, ModelTypeSet));
 };
 
@@ -85,9 +86,12 @@
   EXPECT_CALL(mock_sync_pref_observer, OnSyncManagedPrefChange(false));
   EXPECT_CALL(mock_sync_pref_observer, OnFirstSetupCompletePrefChange(true));
   EXPECT_CALL(mock_sync_pref_observer, OnFirstSetupCompletePrefChange(false));
+  EXPECT_CALL(mock_sync_pref_observer, OnSyncRequestedPrefChange(false));
+  EXPECT_CALL(mock_sync_pref_observer, OnSyncRequestedPrefChange(true));
 
-  EXPECT_FALSE(sync_prefs_->IsManaged());
-  EXPECT_FALSE(sync_prefs_->IsFirstSetupComplete());
+  ASSERT_FALSE(sync_prefs_->IsManaged());
+  ASSERT_FALSE(sync_prefs_->IsFirstSetupComplete());
+  ASSERT_TRUE(sync_prefs_->IsSyncRequested());
 
   sync_prefs_->AddSyncPrefObserver(&mock_sync_pref_observer);
 
@@ -103,6 +107,11 @@
   sync_prefs_->ClearPreferences();
   EXPECT_FALSE(sync_prefs_->IsFirstSetupComplete());
 
+  sync_prefs_->SetSyncRequested(false);
+  EXPECT_FALSE(sync_prefs_->IsSyncRequested());
+  sync_prefs_->SetSyncRequested(true);
+  EXPECT_TRUE(sync_prefs_->IsSyncRequested());
+
   sync_prefs_->RemoveSyncPrefObserver(&mock_sync_pref_observer);
 }
 
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder.cc b/components/sync/driver/sync_session_durations_metrics_recorder.cc
index b1e36697..6064ac24 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder.cc
+++ b/components/sync/driver/sync_session_durations_metrics_recorder.cc
@@ -146,6 +146,14 @@
   HandleSyncAndAccountChange();
 }
 
+void SyncSessionDurationsMetricsRecorder::
+    OnErrorStateOfRefreshTokenUpdatedForAccount(
+        const AccountInfo& account_info,
+        const GoogleServiceAuthError& error) {
+  DVLOG(1) << __func__;
+  HandleSyncAndAccountChange();
+}
+
 bool SyncSessionDurationsMetricsRecorder::ShouldLogUpdate(
     FeatureState new_sync_status,
     FeatureState new_account_status) {
@@ -168,16 +176,9 @@
 }
 
 void SyncSessionDurationsMetricsRecorder::HandleSyncAndAccountChange() {
-  // If sync is off, we can tell whether the user is signed in by just checking
-  // if the token service has accounts, because the reconcilor will take care of
-  // removing accounts in error state from that list.
-  FeatureState non_sync_account_status =
-      identity_manager_->GetAccountsWithRefreshTokens().empty()
-          ? FeatureState::OFF
-          : FeatureState::ON;
   if (!sync_service_ || !sync_service_->CanSyncFeatureStart()) {
     // Only the account status needs to be updated when sync is off.
-    UpdateSyncAndAccountStatus(FeatureState::OFF, non_sync_account_status);
+    UpdateSyncAndAccountStatus(FeatureState::OFF, DetermineAccountStatus());
     return;
   }
 
@@ -192,9 +193,14 @@
     UpdateSyncAndAccountStatus(FeatureState::ON, FeatureState::ON);
   } else {
     // We don't know yet if sync is going to work.
-    // At least update the signin status, so that if we never learn
-    // what the sync state is, we know the signin state.
-    account_status_ = non_sync_account_status;
+    // At least update the account status, so that if we never learn what the
+    // sync state is, we know the signin state.
+    //
+    // TODO(msarda): The current code uses the account status for all accounts
+    // (i.e. it is not scoped to the sync account). Figure out whether this
+    // should be changed to only capture the status of the sync account when
+    // the user has opted in to sync.
+    account_status_ = DetermineAccountStatus();
   }
 }
 
@@ -251,4 +257,16 @@
   }
 }
 
+SyncSessionDurationsMetricsRecorder::FeatureState
+SyncSessionDurationsMetricsRecorder::DetermineAccountStatus() const {
+  for (const auto& account :
+       identity_manager_->GetAccountsWithRefreshTokens()) {
+    if (!identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
+            account.account_id)) {
+      return SyncSessionDurationsMetricsRecorder::FeatureState::ON;
+    }
+  }
+  return SyncSessionDurationsMetricsRecorder::FeatureState::OFF;
+}
+
 }  // namespace syncer
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder.h b/components/sync/driver/sync_session_durations_metrics_recorder.h
index 457bc02..6c14a6e 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder.h
+++ b/components/sync/driver/sync_session_durations_metrics_recorder.h
@@ -43,6 +43,9 @@
       const AccountInfo& account_info) override;
   void OnRefreshTokenRemovedForAccount(const std::string& account_id) override;
   void OnRefreshTokensLoaded() override;
+  void OnErrorStateOfRefreshTokenUpdatedForAccount(
+      const AccountInfo& account_info,
+      const GoogleServiceAuthError& error) override;
   void OnAccountsInCookieUpdated(
       const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
       const GoogleServiceAuthError& error) override;
@@ -64,6 +67,10 @@
 
   void HandleSyncAndAccountChange();
 
+  // Returns |FeatureState::ON| iff there is at least one account in
+  // |identity_manager| that has a valid refresh token.
+  FeatureState DetermineAccountStatus() const;
+
   SyncService* const sync_service_;
   identity::IdentityManager* const identity_manager_;
 
@@ -81,7 +88,7 @@
   // timer is absent if there's no active session.
   std::unique_ptr<base::ElapsedTimer> signin_session_timer_;
 
-  // Whether or not Chrome curently has an LST for an account.
+  // Whether or not Chrome curently has a valid refresh token for an account.
   FeatureState account_status_ = FeatureState::UNKNOWN;
   // Whether or not sync is currently active.
   FeatureState sync_status_ = FeatureState::UNKNOWN;
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
index 819fe31..f2c169c2f 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
+++ b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
@@ -135,7 +135,8 @@
            "OptedInToSyncWithAccount"});
 }
 
-TEST_F(SyncSessionDurationsMetricsRecorderTest, OptedInToSync_AuthError) {
+TEST_F(SyncSessionDurationsMetricsRecorderTest,
+       OptedInToSync_PrimaryAccountInAuthError) {
   EnableSync();
   SetInvalidCredentialsAuthError();
 
@@ -148,5 +149,44 @@
            "OptedInToSyncWithAccount"});
 }
 
+TEST_F(SyncSessionDurationsMetricsRecorderTest,
+       SyncDisabled_PrimaryAccountInAuthError) {
+  EnableSync();
+  SetInvalidCredentialsAuthError();
+  sync_service_.SetDisableReasons(SyncService::DISABLE_REASON_USER_CHOICE);
+
+  base::HistogramTester ht;
+  StartAndEndSession();
+
+  // If the user opted in to sync, but then disabled sync (e.g. via policy or
+  // from the Android OS settings), then they are counted as having opted out
+  // of sync.
+  // The account is in auth error, so they are also counted as not having any
+  // browser account.
+  ExpectOneSession(ht, {"NotOptedInToSyncWithoutAccount"});
+  ExpectNoSession(ht,
+                  {"NotOptedInToSyncWithAccount", "OptedInToSyncWithoutAccount",
+                   "OptedInToSyncWithAccount"});
+}
+
+TEST_F(SyncSessionDurationsMetricsRecorderTest,
+       NotOptedInToSync_AccountInAuthError) {
+  AccountInfo account =
+      identity_test_env_.MakeAccountAvailable("foo@gmail.com");
+  identity_test_env_.UpdatePersistentErrorOfRefreshTokenForAccount(
+      account.account_id,
+      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+
+  base::HistogramTester ht;
+  StartAndEndSession();
+
+  // The account is in auth error, so they are counted as not having any browser
+  // account.
+  ExpectOneSession(ht, {"NotOptedInToSyncWithoutAccount"});
+  ExpectNoSession(ht,
+                  {"NotOptedInToSyncWithAccount", "OptedInToSyncWithoutAccount",
+                   "OptedInToSyncWithAccount"});
+}
+
 }  // namespace
 }  // namespace syncer
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index 3ca83ac..805d1bf 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -48,6 +48,19 @@
   loopback_server_->set_observer_for_tests(this);
 }
 
+FakeServer::FakeServer(const base::FilePath& user_data_dir)
+    : error_type_(sync_pb::SyncEnums::SUCCESS),
+      alternate_triggered_errors_(false),
+      request_counter_(0),
+      weak_ptr_factory_(this) {
+  base::ThreadRestrictions::SetIOAllowed(true);
+  base::FilePath loopback_server_path =
+      user_data_dir.AppendASCII("FakeSyncServer");
+  loopback_server_ = std::make_unique<syncer::LoopbackServer>(
+      loopback_server_path.AppendASCII("profile.pb"));
+  loopback_server_->set_observer_for_tests(this);
+}
+
 FakeServer::~FakeServer() {}
 
 namespace {
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index 100c80fe..77ac5da 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -55,6 +55,9 @@
   };
 
   FakeServer();
+  // A directory will be created under |user_data_dir| to persist sync server
+  // state. It's necessary for supporting PRE_ tests.
+  explicit FakeServer(const base::FilePath& user_data_dir);
   ~FakeServer() override;
 
   // Handles a /command POST (with the given |request|) to the server.
diff --git a/components/translate/core/browser/translate_infobar_delegate.cc b/components/translate/core/browser/translate_infobar_delegate.cc
index 148300f5..2c643695 100644
--- a/components/translate/core/browser/translate_infobar_delegate.cc
+++ b/components/translate/core/browser/translate_infobar_delegate.cc
@@ -149,12 +149,7 @@
 }
 
 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() {
-  if (ui_delegate_.IsLanguageBlocked()) {
-    ui_delegate_.SetLanguageBlocked(false);
-  } else {
-    ui_delegate_.SetLanguageBlocked(true);
-    infobar()->RemoveSelf();
-  }
+  ui_delegate_.SetLanguageBlocked(!ui_delegate_.IsLanguageBlocked());
 }
 
 bool TranslateInfoBarDelegate::IsSiteBlacklisted() const {
@@ -162,12 +157,7 @@
 }
 
 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
-  if (ui_delegate_.IsSiteBlacklisted()) {
-    ui_delegate_.SetSiteBlacklist(false);
-  } else {
-    ui_delegate_.SetSiteBlacklist(true);
-    infobar()->RemoveSelf();
-  }
+  ui_delegate_.SetSiteBlacklist(!ui_delegate_.IsSiteBlacklisted());
 }
 
 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() const {
diff --git a/components/translate_strings.grdp b/components/translate_strings.grdp
index f1da80e..d23fd78 100644
--- a/components/translate_strings.grdp
+++ b/components/translate_strings.grdp
@@ -2,14 +2,20 @@
 <grit-part>
 
    <!-- Translate Info Bar -->
+  <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_MORE_LANGUAGE" desc="Text for the menu item that lets the user open a dialog to choose another target language for translation, from a list of available languages.">
+    More languages
+  </message>
+  <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_NOT_SOURCE_LANGUAGE" desc="Text for the menu item that lets the user open a dialog to choose another language as the source language for translation, from a list of available languages.">
+    Page is not in <ph name="language">$1<ex>French</ex></ph>?
+  </message>
   <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_LANG" desc="Text for the menu item to never translate the specified language">
-    Never translate <ph name="language">$1<ex>French</ex></ph>
+    Never translate pages in <ph name="language">$1<ex>French</ex></ph>
   </message>
   <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_SITE" desc="Text for the menu item to never translate the current site">
     Never translate this site
   </message>
-  <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS" desc="Text for the menu item to always translate from one language to another">
-    Always translate <ph name="original_language">$1<ex>French</ex></ph> to <ph name="target_language">$2<ex>German</ex></ph>
+  <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS" desc="Text for the menu item to always translate the specified language">
+    Always translate pages in <ph name="original_language">$1<ex>French</ex></ph>
   </message>
   <message name="IDS_TRANSLATE_INFOBAR_OPTIONS_REPORT_ERROR" desc="Text for the menu item for reporting the page's language was not correctly detected">
     Not in <ph name="original_language">$1<ex>French</ex></ph>? Report this error
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index a86f7fa..e128578 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -114,8 +114,6 @@
     "gpu/context_provider.h",
     "gpu/raster_context_provider.cc",
     "gpu/raster_context_provider.h",
-    "gpu/texture_allocation.cc",
-    "gpu/texture_allocation.h",
     "hit_test/aggregated_hit_test_region.h",
     "hit_test/hit_test_region_list.cc",
     "hit_test/hit_test_region_list.h",
diff --git a/components/viz/common/frame_sinks/begin_frame_args.cc b/components/viz/common/frame_sinks/begin_frame_args.cc
index 29d1a3d..3366378 100644
--- a/components/viz/common/frame_sinks/begin_frame_args.cc
+++ b/components/viz/common/frame_sinks/begin_frame_args.cc
@@ -4,6 +4,8 @@
 
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
+#include <utility>
+
 #include "base/trace_event/traced_value.h"
 
 namespace viz {
@@ -16,8 +18,6 @@
       return "NORMAL";
     case BeginFrameArgs::MISSED:
       return "MISSED";
-    case BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX:
-      return "BEGIN_FRAME_ARGS_TYPE_MAX";
   }
   NOTREACHED();
   return "???";
@@ -64,7 +64,6 @@
                                       base::TimeDelta interval,
                                       BeginFrameArgs::BeginFrameArgsType type) {
   DCHECK_NE(type, BeginFrameArgs::INVALID);
-  DCHECK_NE(type, BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX);
 #ifdef NDEBUG
   return BeginFrameArgs(source_id, sequence_number, frame_time, deadline,
                         interval, type);
diff --git a/components/viz/common/frame_sinks/begin_frame_args.h b/components/viz/common/frame_sinks/begin_frame_args.h
index ff33a47..6364247 100644
--- a/components/viz/common/frame_sinks/begin_frame_args.h
+++ b/components/viz/common/frame_sinks/begin_frame_args.h
@@ -44,9 +44,6 @@
     INVALID,
     NORMAL,
     MISSED,
-    // Not a real type, but used by the IPC system. Should always remain the
-    // *last* value in this enum.
-    BEGIN_FRAME_ARGS_TYPE_MAX,
   };
   static const char* TypeToString(BeginFrameArgsType type);
 
diff --git a/components/viz/common/gpu/texture_allocation.cc b/components/viz/common/gpu/texture_allocation.cc
deleted file mode 100644
index 25f4583..0000000
--- a/components/viz/common/gpu/texture_allocation.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/common/gpu/texture_allocation.h"
-
-#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/resource_sizes.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/command_buffer/client/raster_interface.h"
-#include "gpu/command_buffer/common/capabilities.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-// static
-TextureAllocation TextureAllocation::MakeTextureId(
-    gpu::gles2::GLES2Interface* gl,
-    const gpu::Capabilities& caps,
-    ResourceFormat format,
-    bool use_gpu_memory_buffer_resources,
-    bool for_framebuffer_attachment) {
-  uint32_t texture_target = GL_TEXTURE_2D;
-
-  bool overlay_candidate = use_gpu_memory_buffer_resources &&
-                           caps.texture_storage_image &&
-                           IsGpuMemoryBufferFormatSupported(format);
-  if (overlay_candidate) {
-    texture_target = gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
-                                                 BufferFormat(format), caps);
-  }
-
-  uint32_t texture_id;
-  gl->GenTextures(1, &texture_id);
-  gl->BindTexture(texture_target, texture_id);
-  gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-  gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-  gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-  if (for_framebuffer_attachment && caps.texture_usage) {
-    // Set GL_FRAMEBUFFER_ATTACHMENT_ANGLE since we'll be binding these
-    // textures as a framebuffer for drawing directly to them on the gpu.
-    gl->TexParameteri(texture_target, GL_TEXTURE_USAGE_ANGLE,
-                      GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
-  }
-  gl->BindTexture(texture_target, 0);
-  return {texture_id, texture_target, overlay_candidate};
-}
-
-// static
-void TextureAllocation::AllocateStorage(gpu::gles2::GLES2Interface* gl,
-                                        const gpu::Capabilities& caps,
-                                        ResourceFormat format,
-                                        const gfx::Size& size,
-                                        const TextureAllocation& alloc,
-                                        const gfx::ColorSpace& color_space) {
-  gl->BindTexture(alloc.texture_target, alloc.texture_id);
-  // Allocate backing storage for the texture. The best choice is to use
-  // GpuMemoryBuffers if we can use the texture as an overlay and its asked
-  // for by the caller. Else we try to make the texture have immutable
-  // storage, and finally fall back to standard storage if we must.
-  if (alloc.overlay_candidate) {
-    // |overlay_candidate| was only set when these were true, and
-    // |use_gpu_memory_buffer_resources| was specified by the caller.
-    DCHECK(caps.texture_storage_image);
-    DCHECK(IsGpuMemoryBufferFormatSupported(format));
-
-    gl->TexStorage2DImageCHROMIUM(
-        alloc.texture_target, TextureStorageFormat(format), GL_SCANOUT_CHROMIUM,
-        size.width(), size.height());
-    if (color_space.IsValid()) {
-      gl->SetColorSpaceMetadataCHROMIUM(
-          alloc.texture_id, reinterpret_cast<GLColorSpace>(
-                                const_cast<gfx::ColorSpace*>(&color_space)));
-    }
-  } else if (caps.texture_storage) {
-    gl->TexStorage2DEXT(alloc.texture_target, 1, TextureStorageFormat(format),
-                        size.width(), size.height());
-  } else {
-    gl->TexImage2D(alloc.texture_target, 0, GLInternalFormat(format),
-                   size.width(), size.height(), 0, GLDataFormat(format),
-                   GLDataType(format), nullptr);
-  }
-}
-
-}  // namespace viz
diff --git a/components/viz/common/gpu/texture_allocation.h b/components/viz/common/gpu/texture_allocation.h
deleted file mode 100644
index 726744d..0000000
--- a/components/viz/common/gpu/texture_allocation.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_COMMON_GPU_TEXTURE_ALLOCATION_H_
-#define COMPONENTS_VIZ_COMMON_GPU_TEXTURE_ALLOCATION_H_
-
-#include "components/viz/common/resources/resource_format.h"
-#include "components/viz/common/viz_common_export.h"
-#include "third_party/khronos/GLES2/gl2.h"
-
-#include <stdint.h>
-
-namespace gfx {
-class ColorSpace;
-class Size;
-}  // namespace gfx
-
-namespace gpu {
-struct Capabilities;
-namespace gles2 {
-class GLES2Interface;
-}
-}  // namespace gpu
-
-namespace viz {
-
-class VIZ_COMMON_EXPORT TextureAllocation {
- public:
-  GLuint texture_id = 0;
-  GLenum texture_target = 0;
-  bool overlay_candidate = false;
-
-  // Generates a texture id and sets it up for use, but without any storage
-  // allocated pixels.
-  static TextureAllocation MakeTextureId(gpu::gles2::GLES2Interface* gl,
-                                         const gpu::Capabilities& caps,
-                                         ResourceFormat format,
-                                         bool use_gpu_memory_buffer_resources,
-                                         bool for_framebuffer_attachment);
-
-  // Allocates the storage for a texture id previously from MakeTextureId().
-  // Can be called on a different context, if the texture id is mapped to
-  // another context with a mailbox. The |format| should match the one given to
-  // MakeTextureId().
-  static void AllocateStorage(gpu::gles2::GLES2Interface* gl,
-                              const gpu::Capabilities& caps,
-                              ResourceFormat format,
-                              const gfx::Size& size,
-                              const TextureAllocation& alloc,
-                              const gfx::ColorSpace& color_space);
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_COMMON_GPU_TEXTURE_ALLOCATION_H_
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 05deaec..5dd10c8d 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -643,11 +643,21 @@
   for (ResourceId local_id : unused) {
     auto it = resources_.find(local_id);
     CHECK(it != resources_.end());
-
-    resource_sk_image_.erase(local_id);
-
     ChildResource& resource = it->second;
 
+    // TODO(https://crbug.com/922592): Batch deletion for reduced overhead.
+    auto sk_image_it = resource_sk_images_.find(local_id);
+    if (sk_image_it != resource_sk_images_.end()) {
+      ResourceSkImage found(std::move(sk_image_it->second));
+      resource_sk_images_.erase(sk_image_it);
+
+      if (found.destroy_callback.has_value()) {
+        gpu::SyncToken token =
+            found.destroy_callback->Run(std::move(found.image));
+        resource.UpdateSyncToken(token);
+      }
+    }
+
     ResourceId child_id = resource.transferable.id;
     DCHECK(child_info->child_to_parent_map.count(child_id));
 
@@ -842,9 +852,9 @@
   DCHECK(resource);
 
   // Use cached SkImage if possible.
-  auto it = resource_provider_->resource_sk_image_.find(resource_id);
-  if (it != resource_provider_->resource_sk_image_.end()) {
-    sk_image_ = it->second;
+  auto it = resource_provider_->resource_sk_images_.find(resource_id);
+  if (it != resource_provider_->resource_sk_images_.end()) {
+    sk_image_ = it->second.image;
     return;
   }
 
@@ -863,7 +873,7 @@
         ResourceFormatToClosestSkColorType(!resource_provider->IsSoftware(),
                                            resource->transferable.format),
         kPremul_SkAlphaType, nullptr);
-    resource_provider_->resource_sk_image_[resource_id] = sk_image_;
+    resource_provider_->resource_sk_images_[resource_id].image = sk_image_;
     return;
   }
 
@@ -883,7 +893,7 @@
   resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
   sk_bitmap.setImmutable();
   sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
-  resource_provider_->resource_sk_image_[resource_id] = sk_image_;
+  resource_provider_->resource_sk_images_[resource_id].image = sk_image_;
 }
 
 DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
@@ -892,9 +902,11 @@
 
 DisplayResourceProvider::LockSetForExternalUse::LockSetForExternalUse(
     DisplayResourceProvider* resource_provider,
-    const CreateSkImageCallback& callback)
+    const CreateSkImageCallback& create_callback,
+    const DestroySkImageCallback& destroy_callback)
     : resource_provider_(resource_provider),
-      create_sk_image_callback_(callback) {}
+      create_sk_image_callback_(create_callback),
+      destroy_sk_image_callback_(destroy_callback) {}
 
 DisplayResourceProvider::LockSetForExternalUse::~LockSetForExternalUse() {
   DCHECK(resources_.empty());
@@ -911,10 +923,13 @@
 DisplayResourceProvider::LockSetForExternalUse::LockResourceAndCreateSkImage(
     ResourceId id) {
   auto metadata = LockResource(id);
-  auto& sk_image = resource_provider_->resource_sk_image_[id];
-  if (!sk_image)
-    sk_image = create_sk_image_callback_.Run(std::move(metadata));
-  return sk_image;
+  auto& resource_sk_image = resource_provider_->resource_sk_images_[id];
+  if (!resource_sk_image.image) {
+    resource_sk_image.image =
+        create_sk_image_callback_.Run(std::move(metadata));
+    resource_sk_image.destroy_callback = destroy_sk_image_callback_;
+  }
+  return resource_sk_image.image;
 }
 
 void DisplayResourceProvider::LockSetForExternalUse::UnlockResources(
@@ -982,6 +997,11 @@
     default;
 DisplayResourceProvider::ChildResource::~ChildResource() = default;
 
+DisplayResourceProvider::ResourceSkImage::ResourceSkImage() = default;
+DisplayResourceProvider::ResourceSkImage::ResourceSkImage(
+    const ResourceSkImage&) = default;
+DisplayResourceProvider::ResourceSkImage::~ResourceSkImage() = default;
+
 void DisplayResourceProvider::ChildResource::SetLocallyUsed() {
   synchronization_state_ = LOCALLY_USED;
   sync_token_.Clear();
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index eb2f707..6a940c881 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -190,8 +190,13 @@
    public:
     using CreateSkImageCallback =
         base::RepeatingCallback<sk_sp<SkImage>(ResourceMetadata)>;
+    // TODO(https://crbug.com/922595): Remove SyncToken once we always use
+    // SharedImage and can rely on SharedImage ref-counting.
+    using DestroySkImageCallback =
+        base::RepeatingCallback<gpu::SyncToken(sk_sp<SkImage>&&)>;
     LockSetForExternalUse(DisplayResourceProvider* resource_provider,
-                          const CreateSkImageCallback& callback);
+                          const CreateSkImageCallback& create_callback,
+                          const DestroySkImageCallback& destroy_callback);
     ~LockSetForExternalUse();
 
     // Lock a resource for external use.
@@ -209,6 +214,7 @@
    private:
     DisplayResourceProvider* const resource_provider_;
     CreateSkImageCallback create_sk_image_callback_;
+    DestroySkImageCallback destroy_sk_image_callback_;
     std::vector<ResourceId> resources_;
 
     DISALLOW_COPY_AND_ASSIGN(LockSetForExternalUse);
@@ -477,7 +483,16 @@
 
   ResourceMap resources_;
   ChildMap children_;
-  base::flat_map<ResourceId, sk_sp<SkImage>> resource_sk_image_;
+  struct ResourceSkImage {
+    ResourceSkImage();
+    ResourceSkImage(const ResourceSkImage&);
+    ~ResourceSkImage();
+
+    sk_sp<SkImage> image;
+    base::Optional<LockSetForExternalUse::DestroySkImageCallback>
+        destroy_callback;
+  };
+  base::flat_map<ResourceId, ResourceSkImage> resource_sk_images_;
   base::flat_map<int, std::vector<ResourceId>> batched_returning_resources_;
   scoped_refptr<ResourceFence> current_read_lock_fence_;
   // Keep track of whether deleted resources should be batched up or returned
diff --git a/components/viz/service/display/display_resource_provider_unittest.cc b/components/viz/service/display/display_resource_provider_unittest.cc
index 1686ea14..82480d7 100644
--- a/components/viz/service/display/display_resource_provider_unittest.cc
+++ b/components/viz/service/display/display_resource_provider_unittest.cc
@@ -480,7 +480,8 @@
 
   DisplayResourceProvider::LockSetForExternalUse lock_set(
       resource_provider_.get(),
-      DisplayResourceProvider::LockSetForExternalUse::CreateSkImageCallback());
+      DisplayResourceProvider::LockSetForExternalUse::CreateSkImageCallback(),
+      DisplayResourceProvider::LockSetForExternalUse::DestroySkImageCallback());
 
   ResourceMetadata metadata = lock_set.LockResource(parent_id);
   ASSERT_EQ(metadata.mailbox_holder.mailbox, mailbox);
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 6d9eb6c..aadc7a7 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -19,7 +19,6 @@
 #include "cc/test/resource_provider_test_utils.h"
 #include "cc/test/test_in_process_context_provider.h"
 #include "components/viz/client/client_resource_provider.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/quads/picture_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index 2b70985..1f46a73 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -59,6 +59,10 @@
       SkYUVColorSpace yuv_color_space,
       bool has_alpha) = 0;
 
+  // Release SkImage created by MakePromiseSkImage.  |image| may or may not
+  // have been fulfilled.
+  virtual gpu::SyncToken DestroySkImage(sk_sp<SkImage>&& image) = 0;
+
   // Swaps the current backbuffer to the screen.
   virtual void SkiaSwapBuffers(OutputSurfaceFrame frame) = 0;
 
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index cf3b497..ff7a201 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -215,6 +215,8 @@
       lock_set_for_external_use_.emplace(
           resource_provider,
           base::BindRepeating(&SkiaOutputSurface::MakePromiseSkImage,
+                              base::Unretained(skia_output_surface_)),
+          base::BindRepeating(&SkiaOutputSurface::DestroySkImage,
                               base::Unretained(skia_output_surface_)));
       break;
     }
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 3e91d277..c4c88f8 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -635,8 +635,7 @@
         const SharedQuadState* dest_shared_quad_state = CopySharedQuadState(
             quad->shared_quad_state, target_transform, clip_rect, dest_pass);
         last_copied_source_shared_quad_state = quad->shared_quad_state;
-        if (aggregate_only_damaged_ && !has_copy_requests_ &&
-            !has_cached_render_passes_) {
+        if (ignore_undamaged) {
           damage_rect_in_quad_space_valid = CalculateQuadSpaceDamageRect(
               dest_shared_quad_state->quad_to_target_transform,
               dest_pass->transform_to_root_target, root_damage_rect_,
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 857908e..5e1891ad 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -56,6 +56,7 @@
 #include "components/viz/service/display_embedder/software_output_device_ozone.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_window_surface.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 #endif
@@ -267,10 +268,13 @@
 #elif defined(USE_OZONE)
   ui::SurfaceFactoryOzone* factory =
       ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone();
+  std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface =
+      factory->CreatePlatformWindowSurface(surface_handle);
   std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone =
       factory->CreateCanvasForWidget(surface_handle);
   CHECK(surface_ozone);
-  return std::make_unique<SoftwareOutputDeviceOzone>(std::move(surface_ozone));
+  return std::make_unique<SoftwareOutputDeviceOzone>(
+      std::move(platform_window_surface), std::move(surface_ozone));
 #elif defined(USE_X11)
   return std::make_unique<SoftwareOutputDeviceX11>(surface_handle);
 #endif
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index de5f5ba..74b778f 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -143,7 +143,6 @@
     auto* helper = static_cast<PromiseTextureHelper*>(texture_context);
     if (helper->shared_image_) {
       helper->shared_image_->EndReadAccess();
-      helper->shared_image_.reset();
     }
   }
 
@@ -458,6 +457,23 @@
       this, yuv_color_space, std::move(metadatas), has_alpha);
 }
 
+gpu::SyncToken SkiaOutputSurfaceImpl::DestroySkImage(sk_sp<SkImage>&& image) {
+  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
+                            impl_on_gpu_->command_buffer_id(),
+                            ++sync_fence_release_);
+  sync_token.SetVerifyFlush();
+
+  auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
+  // impl_on_gpu_ is released on the GPU thread by a posted task from
+  // SkiaOutputSurfaceImpl::dtor. So it is safe to use base::Unretained.
+  auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::DestroySkImage,
+                                 base::Unretained(impl_on_gpu_.get()),
+                                 std::move(image), sync_fence_release_);
+  gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
+      sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
+  return sync_token;
+}
+
 void SkiaOutputSurfaceImpl::SkiaSwapBuffers(OutputSurfaceFrame frame) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!recorder_);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 31a0a65..0355077 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -79,6 +79,7 @@
       std::vector<ResourceMetadata> metadatas,
       SkYUVColorSpace yuv_color_space,
       bool has_alpha) override;
+  gpu::SyncToken DestroySkImage(sk_sp<SkImage>&& image) override;
   void SkiaSwapBuffers(OutputSurfaceFrame frame) override;
   SkCanvas* BeginPaintRenderPass(const RenderPassId& id,
                                  const gfx::Size& surface_size,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 53ebbf0..6c1b7275 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -420,7 +420,16 @@
     std::unique_ptr<gpu::SharedImageRepresentationSkia>* shared_image_out,
     GrBackendTexture* backend_texture) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!*shared_image_out);
+  if (*shared_image_out) {
+    DCHECK(shared_image_representation_factory_->IsSharedImage(
+        mailbox_holder.mailbox));
+    bool result = (*shared_image_out)
+                      ->BeginReadAccess(sk_surface_.get(), backend_texture);
+    ALLOW_UNUSED_LOCAL(result);
+    DLOG_IF(ERROR, !result)
+        << "Failed to begin read access for SharedImageRepresentationSkia";
+    return;
+  }
   if (shared_image_representation_factory_->IsSharedImage(
           mailbox_holder.mailbox)) {
     std::unique_ptr<gpu::SharedImageRepresentationSkia> shared_image =
@@ -475,6 +484,13 @@
   return gr_context()->threadSafeProxy();
 }
 
+void SkiaOutputSurfaceImplOnGpu::DestroySkImage(sk_sp<SkImage>&& image,
+                                                uint64_t sync_fence_release) {
+  MakeCurrent();
+  image.reset();
+  sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
+}
+
 #if defined(OS_WIN)
 void SkiaOutputSurfaceImplOnGpu::DidCreateAcceleratedSurfaceChildWindow(
     gpu::SurfaceHandle parent_window,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 3b9422a..6f3632a2 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -125,6 +125,8 @@
   sk_sp<GrContextThreadSafeProxy> GetGrContextThreadSafeProxy();
   const gl::GLVersionInfo* gl_version_info() const { return gl_version_info_; }
 
+  void DestroySkImage(sk_sp<SkImage>&& image, uint64_t sync_fence_release);
+
  private:
 // gpu::ImageTransportSurfaceDelegate implementation:
 #if defined(OS_WIN)
diff --git a/components/viz/service/display_embedder/software_output_device_ozone.cc b/components/viz/service/display_embedder/software_output_device_ozone.cc
index ea3c84f..ef13d97 100644
--- a/components/viz/service/display_embedder/software_output_device_ozone.cc
+++ b/components/viz/service/display_embedder/software_output_device_ozone.cc
@@ -8,13 +8,16 @@
 
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/public/platform_window_surface.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 
 namespace viz {
 
 SoftwareOutputDeviceOzone::SoftwareOutputDeviceOzone(
+    std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface,
     std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone)
-    : surface_ozone_(std::move(surface_ozone)) {
+    : platform_window_surface_(std::move(platform_window_surface)),
+      surface_ozone_(std::move(surface_ozone)) {
   vsync_provider_ = surface_ozone_->CreateVSyncProvider();
 }
 
diff --git a/components/viz/service/display_embedder/software_output_device_ozone.h b/components/viz/service/display_embedder/software_output_device_ozone.h
index 6565811..e930ece 100644
--- a/components/viz/service/display_embedder/software_output_device_ozone.h
+++ b/components/viz/service/display_embedder/software_output_device_ozone.h
@@ -13,6 +13,7 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace ui {
+class PlatformWindowSurface;
 class SurfaceOzoneCanvas;
 }
 
@@ -24,7 +25,8 @@
 class VIZ_SERVICE_EXPORT SoftwareOutputDeviceOzone
     : public SoftwareOutputDevice {
  public:
-  explicit SoftwareOutputDeviceOzone(
+  SoftwareOutputDeviceOzone(
+      std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface,
       std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone);
   ~SoftwareOutputDeviceOzone() override;
 
@@ -34,6 +36,10 @@
   void EndPaint() override;
 
  private:
+  // This object should outlive |surface_ozone_|. Ending its lifetime may
+  // cause the platform to tear down surface resources.
+  std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface_;
+
   std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone_;
 
   DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceOzone);
diff --git a/components/viz/service/display_embedder/software_output_device_ozone_unittest.cc b/components/viz/service/display_embedder/software_output_device_ozone_unittest.cc
index 41432e47..7b6e3df 100644
--- a/components/viz/service/display_embedder/software_output_device_ozone_unittest.cc
+++ b/components/viz/service/display_embedder/software_output_device_ozone_unittest.cc
@@ -19,6 +19,7 @@
 #include "ui/gfx/vsync_provider.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/platform_window_surface.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 #include "ui/platform_window/platform_window.h"
@@ -95,13 +96,15 @@
 
   ui::SurfaceFactoryOzone* factory =
       ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone();
+  std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface =
+      factory->CreatePlatformWindowSurface(compositor_->widget());
   std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone =
       factory->CreateCanvasForWidget(compositor_->widget());
   if (!surface_ozone) {
     LOG(ERROR) << "SurfaceOzoneCanvas not constructible on this platform";
   } else {
-    output_device_ =
-        std::make_unique<SoftwareOutputDeviceOzone>(std::move(surface_ozone));
+    output_device_ = std::make_unique<SoftwareOutputDeviceOzone>(
+        std::move(platform_window_surface), std::move(surface_ozone));
   }
   if (output_device_)
     output_device_->Resize(size, 1.f);
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index ba02e1f..a2cd11ef 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -186,6 +186,9 @@
 
   media_gpu_channel_manager_.reset();
   gpu_channel_manager_.reset();
+
+  // Scheduler must be destroyed before sync point manager is destroyed.
+  scheduler_.reset();
   owned_sync_point_manager_.reset();
 
   // Signal this event before destroying the child process. That way all
diff --git a/components/viz/service/main/viz_compositor_thread_runner.cc b/components/viz/service/main/viz_compositor_thread_runner.cc
index 70b6b86..f40cceba 100644
--- a/components/viz/service/main/viz_compositor_thread_runner.cc
+++ b/components/viz/service/main/viz_compositor_thread_runner.cc
@@ -29,6 +29,10 @@
 #include "components/ui_devtools/viz/overlay_agent_viz.h"
 #endif
 
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 namespace viz {
 namespace {
 
@@ -45,10 +49,14 @@
 
   base::Thread::Options thread_options;
 #if defined(OS_WIN)
-  // Windows needs a UI message loop for child HWND. Other platforms can use the
-  // default message loop type.
+  // Windows needs a UI message loop for child HWND.
   thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
-#elif defined(OS_CHROMEOS)
+#elif defined(USE_OZONE)
+  // We may need a non-default message loop type for the platform surface.
+  thread_options.message_loop_type =
+      ui::OzonePlatform::GetInstance()->GetMessageLoopTypeForGpu();
+#endif
+#if defined(OS_CHROMEOS)
   thread_options.priority = base::ThreadPriority::DISPLAY;
 #endif
 
diff --git a/components/viz/test/test_gles2_interface.cc b/components/viz/test/test_gles2_interface.cc
index b6c094dd..e4290d8 100644
--- a/components/viz/test/test_gles2_interface.cc
+++ b/components/viz/test/test_gles2_interface.cc
@@ -597,6 +597,13 @@
   return texture_id;
 }
 
+GLuint TestGLES2Interface::CreateAndTexStorage2DSharedImageCHROMIUM(
+    const GLbyte* mailbox) {
+  GLuint texture_id;
+  GenTextures(1, &texture_id);
+  return texture_id;
+}
+
 void TestGLES2Interface::ResizeCHROMIUM(GLuint width,
                                         GLuint height,
                                         float device_scale,
diff --git a/components/viz/test/test_gles2_interface.h b/components/viz/test/test_gles2_interface.h
index e8370d5..2c25d28 100644
--- a/components/viz/test/test_gles2_interface.h
+++ b/components/viz/test/test_gles2_interface.h
@@ -166,6 +166,8 @@
                              const GLenum* attachments) override;
   void ProduceTextureDirectCHROMIUM(GLuint texture, GLbyte* mailbox) override;
   GLuint CreateAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override;
+  GLuint CreateAndTexStorage2DSharedImageCHROMIUM(
+      const GLbyte* mailbox) override;
 
   void ResizeCHROMIUM(GLuint width,
                       GLuint height,
diff --git a/components/zucchini/arm_utils.cc b/components/zucchini/arm_utils.cc
index 4665281..25630bd 100644
--- a/components/zucchini/arm_utils.cc
+++ b/components/zucchini/arm_utils.cc
@@ -194,7 +194,7 @@
 }
 
 // static
-ArmAlign Arm32Rel32Translator::DecodeT21(uint32_t code32, arm_disp_t* disp) {
+ArmAlign Arm32Rel32Translator::DecodeT20(uint32_t code32, arm_disp_t* disp) {
   if ((code32 & 0xF800D000) == 0xF0008000 &&
       (code32 & 0x03C00000) != 0x03C00000) {
     // B encoding T3. Note the reversal of "(J1)" and "(J2)".
@@ -214,7 +214,7 @@
 }
 
 // static
-bool Arm32Rel32Translator::EncodeT21(arm_disp_t disp, uint32_t* code32) {
+bool Arm32Rel32Translator::EncodeT20(arm_disp_t disp, uint32_t* code32) {
   uint32_t t = *code32;
   if ((t & 0xF800D000) == 0xF0008000 && (t & 0x03C00000) != 0x03C00000) {
     if (disp % 2)  // Require 2-byte alignment.
@@ -235,11 +235,11 @@
 }
 
 // static
-bool Arm32Rel32Translator::ReadT21(rva_t instr_rva,
+bool Arm32Rel32Translator::ReadT20(rva_t instr_rva,
                                    uint32_t code32,
                                    rva_t* target_rva) {
   arm_disp_t disp;
-  ArmAlign align = DecodeT21(code32, &disp);
+  ArmAlign align = DecodeT20(code32, &disp);
   if (align == kArmAlignFail)
     return false;
   *target_rva = GetThumb2TargetRvaFromDisp(instr_rva, disp, align);
@@ -247,12 +247,12 @@
 }
 
 // static
-bool Arm32Rel32Translator::WriteT21(rva_t instr_rva,
+bool Arm32Rel32Translator::WriteT20(rva_t instr_rva,
                                     rva_t target_rva,
                                     uint32_t* code32) {
   arm_disp_t disp =
       GetThumb2DispFromTargetRva(instr_rva, target_rva, kArmAlign2);
-  return EncodeT21(disp, code32);
+  return EncodeT20(disp, code32);
 }
 
 // static
diff --git a/components/zucchini/arm_utils.h b/components/zucchini/arm_utils.h
index 03eb9f4..7d95400 100644
--- a/components/zucchini/arm_utils.h
+++ b/components/zucchini/arm_utils.h
@@ -132,7 +132,7 @@
     ADDR_A24 = 0,
     ADDR_T8,
     ADDR_T11,
-    ADDR_T21,
+    ADDR_T20,
     ADDR_T24,
     NUM_ADDR_TYPE
   };
@@ -211,10 +211,10 @@
   static bool ReadT11(rva_t instr_rva, uint16_t code16, rva_t* target_rva);
   static bool WriteT11(rva_t instr_rva, rva_t target_rva, uint16_t* code16);
 
-  static ArmAlign DecodeT21(uint32_t code32, arm_disp_t* disp);
-  static bool EncodeT21(arm_disp_t disp, uint32_t* code32);
-  static bool ReadT21(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
-  static bool WriteT21(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
+  static ArmAlign DecodeT20(uint32_t code32, arm_disp_t* disp);
+  static bool EncodeT20(arm_disp_t disp, uint32_t* code32);
+  static bool ReadT20(rva_t instr_rva, uint32_t code32, rva_t* target_rva);
+  static bool WriteT20(rva_t instr_rva, rva_t target_rva, uint32_t* code32);
 
   static ArmAlign DecodeT24(uint32_t code32, arm_disp_t* disp);
   static bool EncodeT24(arm_disp_t disp, uint32_t* code32);
@@ -293,15 +293,15 @@
                                        EncodeT11,
                                        ReadT11,
                                        WriteT11>;
-  using AddrTraits_T21 = ArmAddrTraits<AddrType,
-                                       ADDR_T21,
+  using AddrTraits_T20 = ArmAddrTraits<AddrType,
+                                       ADDR_T20,
                                        uint32_t,
                                        FetchThumb2Code32,
                                        StoreThumb2Code32,
-                                       DecodeT21,
-                                       EncodeT21,
-                                       ReadT21,
-                                       WriteT21>;
+                                       DecodeT20,
+                                       EncodeT20,
+                                       ReadT20,
+                                       WriteT20>;
   using AddrTraits_T24 = ArmAddrTraits<AddrType,
                                        ADDR_T24,
                                        uint32_t,
diff --git a/components/zucchini/arm_utils_unittest.cc b/components/zucchini/arm_utils_unittest.cc
index c6d513df..1b328ee 100644
--- a/components/zucchini/arm_utils_unittest.cc
+++ b/components/zucchini/arm_utils_unittest.cc
@@ -29,7 +29,7 @@
 uint32_t kCleanSlateBLX_A2 = 0xFA000000;  // A24.
 uint16_t kCleanSlateB_T1 = 0xD000;        // T8.
 uint16_t kCleanSlateB_T2 = 0xE000;        // T11.
-uint32_t kCleanSlateB_T3 = 0xF0008000;    // T21.
+uint32_t kCleanSlateB_T3 = 0xF0008000;    // T20.
 // For T4 encodings, |disp| = 0 means J1 = J2 = 1, so include 0x00002800.
 uint32_t kCleanSlateB_T4 = 0xF0009000 | 0x00002800;    // T24.
 uint32_t kCleanSlateBL_T1 = 0xF000D000 | 0x00002800;   // T24.
@@ -334,20 +334,20 @@
                {0x07FE, -0x0800, 0, 2, -2, 4, 0x40, 0x42},
                {1, -1, 0x41, 0x43, 0x0800, -0x0802});
 
-  // T21 tests.
-  ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T21> test_T21;
+  // T20 tests.
+  ArmTranslatorEncodeDecodeTest<Arm32Rel32Translator::AddrTraits_T20> test_T20;
   for (int cond = 0; cond <= 0x0E; ++cond) {
     ArmRelInstruction<uint32_t> B_T3_cond(
         "11110Scc cciiiiii 10(J1)0(J2)jjj jjjjjjjj",
         kCleanSlateB_T3 | (cond << 22));
-    test_T21.Run("SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0",
+    test_T20.Run("SSSSSSSS SSSS(J2)(J1)ii iiiijjjj jjjjjjj0",
                  {"S", "J2", "J1", "i", "j"}, {B_T3_cond},
                  {0x000FFFFE, -0x00100000, 0, 2, -2, 4, 0x40, 0x42},
                  {1, -1, 0x41, 0x43, 0x00100000, -0x00100002});
   }
   ArmRelInstruction<uint32_t> B_T3_invalid(
       "11110.11 11...... 10.0.... ........", kCleanSlateB_T3 | (0x0F << 22));
-  test_T21.Run("........ ........ ........ ........",
+  test_T20.Run("........ ........ ........ ........",
                std::vector<std::string>(), {B_T3_invalid},
                std::vector<arm_disp_t>(),
                {0x000FFFFE, -0x00100000, 0, 2, 4, 0x40, 0x42, 1, 0x41, 0x43,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 02b749a..a31025a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -226,7 +226,6 @@
     "//ipc",
     "//media/mojo/interfaces:remoting",
     "//third_party/blink/public:embedded_frame_sink_mojo_bindings",
-    "//third_party/blink/public:media_devices_mojo_bindings",
     "//third_party/leveldatabase",
   ]
 
@@ -1004,8 +1003,6 @@
     "indexed_db/indexed_db_tracing.h",
     "indexed_db/indexed_db_transaction.cc",
     "indexed_db/indexed_db_transaction.h",
-    "indexed_db/indexed_db_transaction_coordinator.cc",
-    "indexed_db/indexed_db_transaction_coordinator.h",
     "indexed_db/indexed_db_value.cc",
     "indexed_db/indexed_db_value.h",
     "indexed_db/leveldb/leveldb_comparator.cc",
@@ -2246,6 +2243,8 @@
       "android/javascript_injector.cc",
       "android/javascript_injector.h",
       "android/load_url_params.cc",
+      "android/navigation_handle_proxy.cc",
+      "android/navigation_handle_proxy.h",
       "android/nfc_host.cc",
       "android/nfc_host.h",
       "android/overscroll_controller_android.cc",
diff --git a/content/browser/OWNERS b/content/browser/OWNERS
index e561d38..6e40c8f 100644
--- a/content/browser/OWNERS
+++ b/content/browser/OWNERS
@@ -28,7 +28,5 @@
 per-file sandbox_mac_unittest.*=file://sandbox/mac/OWNERS
 
 # Linux sandboxing.
-per-file sandbox_host_linux.*=jln@chromium.org
-per-file sandbox_host_linux.*=jorgelo@chromium.org
-per-file sandbox_ipc_linux.*=jln@chromium.org
-per-file sandbox_ipc_linux.*=jorgelo@chromium.org
+per-file sandbox_host_linux.*=file://sandbox/linux/OWNERS
+per-file sandbox_ipc_linux.*=file://sandbox/linux/OWNERS
diff --git a/content/browser/accessibility/accessibility_action_browsertest.cc b/content/browser/accessibility/accessibility_action_browsertest.cc
index e435cc4..e669d27 100644
--- a/content/browser/accessibility/accessibility_action_browsertest.cc
+++ b/content/browser/accessibility/accessibility_action_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -352,4 +353,113 @@
   EXPECT_EQ(focus->GetId(), target->GetId());
 }
 
+IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, InputSetValue) {
+  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+
+  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
+                                         ui::kAXModeComplete,
+                                         ax::mojom::Event::kLoadComplete);
+  GURL url(
+      "data:text/html,"
+      "<input aria-label='Answer' value='Before'>");
+  NavigateToURL(shell(), url);
+  waiter.WaitForNotification();
+
+  BrowserAccessibility* target =
+      FindNode(ax::mojom::Role::kTextField, "Answer");
+  ASSERT_NE(nullptr, target);
+  EXPECT_EQ("Before",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+
+  AccessibilityNotificationWaiter waiter2(shell()->web_contents(),
+                                          ui::kAXModeComplete,
+                                          ax::mojom::Event::kValueChanged);
+  GetManager()->SetValue(*target, "After");
+  waiter2.WaitForNotification();
+
+  EXPECT_EQ("After",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, TextareaSetValue) {
+  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+
+  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
+                                         ui::kAXModeComplete,
+                                         ax::mojom::Event::kLoadComplete);
+  GURL url(
+      "data:text/html,"
+      "<textarea aria-label='Answer'>Before</textarea>");
+  NavigateToURL(shell(), url);
+  waiter.WaitForNotification();
+
+  BrowserAccessibility* target =
+      FindNode(ax::mojom::Role::kTextField, "Answer");
+  ASSERT_NE(nullptr, target);
+  EXPECT_EQ("Before",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+
+  AccessibilityNotificationWaiter waiter2(shell()->web_contents(),
+                                          ui::kAXModeComplete,
+                                          ax::mojom::Event::kValueChanged);
+  GetManager()->SetValue(*target, "Line1\nLine2");
+  waiter2.WaitForNotification();
+
+  EXPECT_EQ("Line1\nLine2",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+
+  // TODO(dmazzoni): On Android we use an ifdef to disable inline text boxes,
+  // which contain all of the line break information.
+  //
+  // We should do it with accessibility flags instead. http://crbug.com/672205
+#if !defined(OS_ANDROID)
+  // Check that it really does contain two lines.
+  auto start_pos = target->CreatePositionAt(0);
+  auto end_of_line_1 = start_pos->CreateNextLineEndPosition(
+      ui::AXBoundaryBehavior::CrossBoundary);
+  EXPECT_EQ(5, end_of_line_1->text_offset());
+#endif
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest,
+                       ContenteditableSetValue) {
+  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+
+  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
+                                         ui::kAXModeComplete,
+                                         ax::mojom::Event::kLoadComplete);
+  GURL url(
+      "data:text/html,"
+      "<div contenteditable aria-label='Answer'>Before</div>");
+  NavigateToURL(shell(), url);
+  waiter.WaitForNotification();
+
+  BrowserAccessibility* target =
+      FindNode(ax::mojom::Role::kGenericContainer, "Answer");
+  ASSERT_NE(nullptr, target);
+  EXPECT_EQ("Before",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+
+  AccessibilityNotificationWaiter waiter2(shell()->web_contents(),
+                                          ui::kAXModeComplete,
+                                          ax::mojom::Event::kValueChanged);
+  GetManager()->SetValue(*target, "Line1\nLine2");
+  waiter2.WaitForNotification();
+
+  EXPECT_EQ("Line1\nLine2",
+            target->GetStringAttribute(ax::mojom::StringAttribute::kValue));
+
+  // TODO(dmazzoni): On Android we use an ifdef to disable inline text boxes,
+  // which contain all of the line break information.
+  //
+  // We should do it with accessibility flags instead. http://crbug.com/672205
+#if !defined(OS_ANDROID)
+  // Check that it really does contain two lines.
+  auto start_pos = target->CreatePositionAt(0);
+  auto end_of_line_1 = start_pos->CreateNextLineEndPosition(
+      ui::AXBoundaryBehavior::CrossBoundary);
+  EXPECT_EQ(5, end_of_line_1->text_offset());
+#endif
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index a1c6e35..1b7a74b 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -221,6 +221,10 @@
 }
 
 bool BrowserAccessibilityAndroid::IsClickable() const {
+  // Explicitly disabled form controls shouldn't be clickable.
+  if (!IsEnabled())
+    return false;
+
   // If it has a custom default action verb except for
   // ax::mojom::DefaultActionVerb::kClickAncestor, it's definitely clickable.
   // ax::mojom::DefaultActionVerb::kClickAncestor is used when an element with a
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 1fcc3e7..5162eaacb 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -158,9 +158,9 @@
     const ui::AXTreeUpdate& initial_tree) {
   if (!tree_->Unserialize(initial_tree)) {
     static auto* ax_tree_error = base::debug::AllocateCrashKeyString(
-        "ax_tree_error", base::debug::CrashKeySize::Size32);
+        "ax_tree_error", base::debug::CrashKeySize::Size64);
     static auto* ax_tree_update = base::debug::AllocateCrashKeyString(
-        "ax_tree_update", base::debug::CrashKeySize::Size64);
+        "ax_tree_update", base::debug::CrashKeySize::Size256);
     // Temporarily log some additional crash keys so we can try to
     // figure out why we're getting bad accessibility trees here.
     // http://crbug.com/765490
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 92d36f482..55fa5f9 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -17,8 +17,8 @@
     const ui::AXTreeUpdate& initial_tree,
     BrowserAccessibilityDelegate* delegate,
     BrowserAccessibilityFactory* factory) {
-  return new BrowserAccessibilityManagerAuraLinux(nullptr, initial_tree,
-                                                  delegate, factory);
+  return new BrowserAccessibilityManagerAuraLinux(initial_tree, delegate,
+                                                  factory);
 }
 
 BrowserAccessibilityManagerAuraLinux*
@@ -27,12 +27,10 @@
 }
 
 BrowserAccessibilityManagerAuraLinux::BrowserAccessibilityManagerAuraLinux(
-    AtkObject* parent_object,
     const ui::AXTreeUpdate& initial_tree,
     BrowserAccessibilityDelegate* delegate,
     BrowserAccessibilityFactory* factory)
-    : BrowserAccessibilityManager(delegate, factory),
-      parent_object_(parent_object) {
+    : BrowserAccessibilityManager(delegate, factory) {
   Initialize(initial_tree);
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.h b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
index 29ce37d..47589ce 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.h
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
@@ -18,7 +18,6 @@
     : public BrowserAccessibilityManager {
  public:
   BrowserAccessibilityManagerAuraLinux(
-      AtkObject* parent_object,
       const ui::AXTreeUpdate& initial_tree,
       BrowserAccessibilityDelegate* delegate,
       BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory());
@@ -38,8 +37,6 @@
   void FireExpandedEvent(BrowserAccessibility* node, bool is_expanded);
   void FireLoadingEvent(BrowserAccessibility* node, bool is_loading);
 
-  AtkObject* parent_object() { return parent_object_; }
-
  protected:
   // AXTreeObserver methods.
   void OnAtomicUpdateFinished(
diff --git a/content/browser/accessibility/browser_accessibility_position.h b/content/browser/accessibility/browser_accessibility_position.h
index d5a7a68..536e8d7 100644
--- a/content/browser/accessibility/browser_accessibility_position.h
+++ b/content/browser/accessibility/browser_accessibility_position.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
+#include "content/common/content_export.h"
 #include "ui/accessibility/ax_position.h"
 #include "ui/accessibility/ax_tree_id_registry.h"
 
@@ -19,7 +20,7 @@
 
 using AXTreeID = ui::AXTreeID;
 
-class BrowserAccessibilityPosition
+class CONTENT_EXPORT BrowserAccessibilityPosition
     : public ui::AXPosition<BrowserAccessibilityPosition,
                             BrowserAccessibility> {
  public:
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index a612e5b..ca1c1192 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1157,6 +1157,10 @@
   RunHtmlTest(FILE_PATH_LITERAL("dialog.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDisabled) {
+  RunHtmlTest(FILE_PATH_LITERAL("disabled.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDiv) {
   RunHtmlTest(FILE_PATH_LITERAL("div.html"));
 }
@@ -1952,4 +1956,9 @@
   RunLanguageDetectionTest(FILE_PATH_LITERAL("lang-attribute-nested.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       LanguageDetectionLangAttributeSwitching) {
+  RunLanguageDetectionTest(FILE_PATH_LITERAL("lang-attribute-switching.html"));
+}
+
 }  // namespace content
diff --git a/content/browser/android/navigation_handle_proxy.cc b/content/browser/android/navigation_handle_proxy.cc
new file mode 100644
index 0000000..ec48e73e6
--- /dev/null
+++ b/content/browser/android/navigation_handle_proxy.cc
@@ -0,0 +1,32 @@
+// 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 "content/browser/android/navigation_handle_proxy.h"
+
+#include "content/public/browser/navigation_handle.h"
+#include "jni/NavigationHandleProxy_jni.h"
+
+namespace content {
+
+// Called from Java.
+void NavigationHandleProxy::SetRequestHeader(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jstring>& name,
+    const base::android::JavaParamRef<jstring>& value) {
+  navigation_handle_->SetRequestHeader(
+      base::android::ConvertJavaStringToUTF8(name),
+      base::android::ConvertJavaStringToUTF8(value));
+}
+
+// Called from Java.
+void NavigationHandleProxy::RemoveRequestHeader(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jstring>& name) {
+  navigation_handle_->RemoveRequestHeader(
+      base::android::ConvertJavaStringToUTF8(name));
+}
+
+}  // namespace content
diff --git a/content/browser/android/navigation_handle_proxy.h b/content/browser/android/navigation_handle_proxy.h
new file mode 100644
index 0000000..ec2e4a0
--- /dev/null
+++ b/content/browser/android/navigation_handle_proxy.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_ANDROID_NAVIGATION_HANDLE_PROXY_H_
+#define CONTENT_BROWSER_ANDROID_NAVIGATION_HANDLE_PROXY_H_
+
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "net/http/http_request_headers.h"
+
+#include <string>
+#include <vector>
+
+namespace content {
+
+class NavigationHandle;
+
+// JNI bridge for using a content::NavigationHandle from Java.
+class NavigationHandleProxy {
+ public:
+  explicit NavigationHandleProxy(content::NavigationHandle* navigation_handle)
+      : navigation_handle_(navigation_handle) {}
+
+  // Called from Java.
+  void SetRequestHeader(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj,
+                        const base::android::JavaParamRef<jstring>& name,
+                        const base::android::JavaParamRef<jstring>& value);
+
+  // Called from Java.
+  void RemoveRequestHeader(JNIEnv* env,
+                           const base::android::JavaParamRef<jobject>& obj,
+                           const base::android::JavaParamRef<jstring>& name);
+
+  jlong JavaThis() const { return reinterpret_cast<jlong>(this); }
+
+ private:
+  content::NavigationHandle* navigation_handle_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ANDROID_NAVIGATION_HANDLE_PROXY_H_
diff --git a/content/browser/android/web_contents_observer_proxy.cc b/content/browser/android/web_contents_observer_proxy.cc
index 2ecfc7473..1bca9e7 100644
--- a/content/browser/android/web_contents_observer_proxy.cc
+++ b/content/browser/android/web_contents_observer_proxy.cc
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
+#include "content/browser/android/navigation_handle_proxy.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/navigation_details.h"
@@ -129,9 +130,21 @@
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jstring> jstring_url(
       ConvertUTF8ToJavaString(env, navigation_handle->GetURL().spec()));
+  NavigationHandleProxy navigation_handle_proxy(navigation_handle);
   Java_WebContentsObserverProxy_didStartNavigation(
       env, java_observer_, jstring_url, navigation_handle->IsInMainFrame(),
-      navigation_handle->IsSameDocument(), navigation_handle->IsErrorPage());
+      navigation_handle->IsSameDocument(), navigation_handle_proxy.JavaThis());
+}
+
+void WebContentsObserverProxy::DidRedirectNavigation(
+    NavigationHandle* navigation_handle) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jstring_url(
+      ConvertUTF8ToJavaString(env, navigation_handle->GetURL().spec()));
+  NavigationHandleProxy navigation_handle_proxy(navigation_handle);
+  Java_WebContentsObserverProxy_didRedirectNavigation(
+      env, java_observer_, jstring_url, navigation_handle->IsInMainFrame(),
+      navigation_handle_proxy.JavaThis());
 }
 
 void WebContentsObserverProxy::DidFinishNavigation(
diff --git a/content/browser/android/web_contents_observer_proxy.h b/content/browser/android/web_contents_observer_proxy.h
index ccc54e0..e558c5c4 100644
--- a/content/browser/android/web_contents_observer_proxy.h
+++ b/content/browser/android/web_contents_observer_proxy.h
@@ -44,6 +44,7 @@
   void TitleWasSet(NavigationEntry* entry) override;
 
   void DidStartNavigation(NavigationHandle* navigation_handle) override;
+  void DidRedirectNavigation(NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
 
   void DidFinishLoad(RenderFrameHost* render_frame_host,
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
index 8632f222..affed98 100644
--- a/content/browser/appcache/appcache_backend_impl.cc
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -92,8 +92,7 @@
   if (!host)
     return false;
 
-  host->GetStatusWithCallback(std::move(*callback));
-  return true;
+  return host->GetStatusWithCallback(std::move(*callback));
 }
 
 bool AppCacheBackendImpl::StartUpdateWithCallback(
@@ -103,8 +102,7 @@
   if (!host)
     return false;
 
-  host->StartUpdateWithCallback(std::move(*callback));
-  return true;
+  return host->StartUpdateWithCallback(std::move(*callback));
 }
 
 bool AppCacheBackendImpl::SwapCacheWithCallback(int host_id,
@@ -113,8 +111,7 @@
   if (!host)
     return false;
 
-  host->SwapCacheWithCallback(std::move(*callback));
-  return true;
+  return host->SwapCacheWithCallback(std::move(*callback));
 }
 
 void AppCacheBackendImpl::GetResourceList(
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index f3ef446..30eb806 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -46,7 +46,10 @@
 
   // The xxxWithCallback functions take ownership of the callback iff the host
   // is found (and the return value is true). If the result is false, the
-  // callback is still available to the caller of these methods.
+  // callback might still be available to the caller of these methods.
+  // TODO(mek): Just pass callbacks unconditionally. That is possible if the
+  // caller is changed to call BindingSet::ReportBadMessage rather than calling
+  // the global mojo::ReportBadMessage.
   bool GetStatusWithCallback(int host_id, GetStatusCallback* callback);
   bool StartUpdateWithCallback(int host_id, StartUpdateCallback* callback);
   bool SwapCacheWithCallback(int host_id, SwapCacheCallback* callback);
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc
index 0603473..07792ff0 100644
--- a/content/browser/appcache/appcache_dispatcher_host.cc
+++ b/content/browser/appcache/appcache_dispatcher_host.cc
@@ -142,8 +142,10 @@
       mojo::ReportBadMessage("ACDH_GET_STATUS");
     }
   }
-  std::move(callback).Run(
-      blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
+  if (callback) {
+    std::move(callback).Run(
+        blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
+  }
 }
 
 void AppCacheDispatcherHost::StartUpdate(int32_t host_id,
@@ -155,7 +157,8 @@
       mojo::ReportBadMessage("ACDH_START_UPDATE");
     }
   }
-  std::move(callback).Run(false);
+  if (callback)
+    std::move(callback).Run(false);
 }
 
 void AppCacheDispatcherHost::SwapCache(int32_t host_id,
@@ -167,7 +170,8 @@
       mojo::ReportBadMessage("ACDH_SWAP_CACHE");
     }
   }
-  std::move(callback).Run(false);
+  if (callback)
+    std::move(callback).Run(false);
 }
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index c5cb081..f308528 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -211,16 +211,21 @@
   return true;
 }
 
-void AppCacheHost::GetStatusWithCallback(GetStatusCallback callback) {
-  DCHECK(pending_start_update_callback_.is_null() &&
-         pending_swap_cache_callback_.is_null() &&
-         pending_get_status_callback_.is_null());
+bool AppCacheHost::GetStatusWithCallback(GetStatusCallback callback) {
+  if (!pending_start_update_callback_.is_null() ||
+      !pending_swap_cache_callback_.is_null() ||
+      !pending_get_status_callback_.is_null()) {
+    std::move(callback).Run(
+        blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
+    return false;
+  }
 
   pending_get_status_callback_ = std::move(callback);
   if (is_selection_pending())
-    return;
+    return true;
 
   DoPendingGetStatus();
+  return true;
 }
 
 void AppCacheHost::DoPendingGetStatus() {
@@ -229,16 +234,20 @@
   std::move(pending_get_status_callback_).Run(GetStatus());
 }
 
-void AppCacheHost::StartUpdateWithCallback(StartUpdateCallback callback) {
-  DCHECK(pending_start_update_callback_.is_null() &&
-         pending_swap_cache_callback_.is_null() &&
-         pending_get_status_callback_.is_null());
+bool AppCacheHost::StartUpdateWithCallback(StartUpdateCallback callback) {
+  if (!pending_start_update_callback_.is_null() ||
+      !pending_swap_cache_callback_.is_null() ||
+      !pending_get_status_callback_.is_null()) {
+    std::move(callback).Run(false);
+    return false;
+  }
 
   pending_start_update_callback_ = std::move(callback);
   if (is_selection_pending())
-    return;
+    return true;
 
   DoPendingStartUpdate();
+  return true;
 }
 
 void AppCacheHost::DoPendingStartUpdate() {
@@ -257,17 +266,21 @@
   std::move(pending_start_update_callback_).Run(success);
 }
 
-void AppCacheHost::SwapCacheWithCallback(SwapCacheCallback callback) {
-  DCHECK(pending_start_update_callback_.is_null() &&
-         pending_swap_cache_callback_.is_null() &&
-         pending_get_status_callback_.is_null());
+bool AppCacheHost::SwapCacheWithCallback(SwapCacheCallback callback) {
+  if (!pending_start_update_callback_.is_null() ||
+      !pending_swap_cache_callback_.is_null() ||
+      !pending_get_status_callback_.is_null()) {
+    std::move(callback).Run(false);
+    return false;
+  }
 
   pending_swap_cache_callback_ = std::move(callback);
 
   if (is_selection_pending())
-    return;
+    return true;
 
   DoPendingSwapCache();
+  return true;
 }
 
 void AppCacheHost::DoPendingSwapCache() {
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index e21a3341..74426e8 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/gtest_prod_util.h"
@@ -62,7 +63,6 @@
       public AppCacheGroup::UpdateObserver,
       public AppCacheServiceImpl::Observer {
  public:
-
   class CONTENT_EXPORT Observer {
    public:
     // Called just after the cache selection algorithm completes.
@@ -83,16 +83,19 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // Support for cache selection and scriptable method calls.
+  // Support for cache selection and scriptable method calls. These methods
+  // return false if their preconditions have been broken.
+  // TODO(mek): WARN_UNUSED_RESULT to make sure we're actually checking the
+  // return values.
   bool SelectCache(const GURL& document_url,
                    const int64_t cache_document_was_loaded_from,
                    const GURL& manifest_url);
   bool SelectCacheForSharedWorker(int64_t appcache_id);
   bool MarkAsForeignEntry(const GURL& document_url,
                           int64_t cache_document_was_loaded_from);
-  void GetStatusWithCallback(GetStatusCallback callback);
-  void StartUpdateWithCallback(StartUpdateCallback callback);
-  void SwapCacheWithCallback(SwapCacheCallback callback);
+  bool GetStatusWithCallback(GetStatusCallback callback);
+  bool StartUpdateWithCallback(StartUpdateCallback callback);
+  bool SwapCacheWithCallback(SwapCacheCallback callback);
 
   // Called prior to the main resource load. When the system contains multiple
   // candidates for a main resource load, the appcache preferred by the host
@@ -185,6 +188,8 @@
            !pending_selected_manifest_url_.is_empty();
   }
 
+  const url::Origin& origin_in_use() { return origin_in_use_; }
+
   const GURL& first_party_url() const { return first_party_url_; }
   void SetFirstPartyUrlForTesting(const GURL& url) {
     first_party_url_ = url;
diff --git a/content/browser/appcache/appcache_response.cc b/content/browser/appcache/appcache_response.cc
index 663b8f1f..c54d4e1 100644
--- a/content/browser/appcache/appcache_response.cc
+++ b/content/browser/appcache/appcache_response.cc
@@ -238,11 +238,11 @@
 }
 
 void AppCacheResponseReader::ContinueReadData() {
-  if (read_position_ + buffer_len_ > range_length_) {
-    // TODO(michaeln): What about integer overflows?
-    DCHECK(range_length_ >= read_position_);
+  // Since every read reads at most (range_length_ - read_position_) bytes,
+  // read_position_ can never become larger than range_length_.
+  DCHECK_GE(range_length_, read_position_);
+  if (range_length_ - read_position_ < buffer_len_)
     buffer_len_ = range_length_ - read_position_;
-  }
   ReadRaw(kResponseContentIndex,
           range_offset_ + read_position_,
           buffer_.get(),
diff --git a/content/browser/appcache/appcache_subresource_url_factory.cc b/content/browser/appcache/appcache_subresource_url_factory.cc
index b4d0722..7a7ecf0f 100644
--- a/content/browser/appcache/appcache_subresource_url_factory.cc
+++ b/content/browser/appcache/appcache_subresource_url_factory.cc
@@ -4,6 +4,9 @@
 
 #include "content/browser/appcache/appcache_subresource_url_factory.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "content/browser/appcache/appcache_host.h"
@@ -11,9 +14,12 @@
 #include "content/browser/appcache/appcache_url_loader_job.h"
 #include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -358,6 +364,21 @@
     network::mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (request.request_initiator.has_value() &&
+      !request.request_initiator.value().opaque() &&
+      request.request_initiator.value() != appcache_host_->origin_in_use()) {
+    const char* scheme_exception =
+        GetContentClient()
+            ->browser()
+            ->GetInitiatorSchemeBypassingDocumentBlocking();
+    if (!scheme_exception ||
+        request.request_initiator.value().scheme() != scheme_exception) {
+      mojo::ReportBadMessage(
+          "APP_CACHE_SUBRESOURCE_URL_FACTORY_INVALID_INITIATOR");
+      return;
+    }
+  }
+
   new SubresourceLoader(std::move(url_loader_request), routing_id, request_id,
                         options, request, std::move(client), traffic_annotation,
                         appcache_host_, network_loader_factory_);
diff --git a/content/browser/background_fetch/background_fetch.proto b/content/browser/background_fetch/background_fetch.proto
index 94117bd..a644ed3 100644
--- a/content/browser/background_fetch/background_fetch.proto
+++ b/content/browser/background_fetch/background_fetch.proto
@@ -85,6 +85,26 @@
   optional uint64 download_total = 3;
 }
 
+// Keeps track of the version of the persisted data. If a breaking change
+// needs to be made, increment `CURRENT`, and replace it with a descriptive
+// name of the change. Don't directly check against `SV_CURRENT` as it can be
+// changed.
+// For example, if you want to enable a new key format for the SWDB, perform
+// the following steps:
+// 1. Add a new enum value (SV_ENABLE_NEW_KEY_FORMAT) and increment
+//    `SV_CURRENT`
+// 2. Migrate the old formats on database load (GetInitializationDataTask).
+// 3. Add a TODO with a bug to clean this up after 2 milestones or so.
+//
+// Next Tag: 2
+enum BackgroundFetchStorageVersion {
+  SV_UNINITIALIZED = 0;
+  // Add new versions here.
+
+  // Must be last.
+  SV_CURRENT = 1;
+}
+
 // Stores information about the background fetch that will be persisted
 // in memory. This information should be everything needed to reconstruct
 // the state of an interrupted background fetch.
diff --git a/content/browser/background_fetch/background_fetch_data_manager.cc b/content/browser/background_fetch/background_fetch_data_manager.cc
index a276f287..4283f10fc 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager.cc
@@ -248,4 +248,9 @@
   return this;
 }
 
+base::WeakPtr<background_fetch::DatabaseTaskHost>
+BackgroundFetchDataManager::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_data_manager.h b/content/browser/background_fetch/background_fetch_data_manager.h
index 7c0ac92..6932733 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.h
+++ b/content/browser/background_fetch/background_fetch_data_manager.h
@@ -200,6 +200,7 @@
   // DatabaseTaskHost implementation.
   void OnTaskFinished(background_fetch::DatabaseTask* task) override;
   BackgroundFetchDataManager* data_manager() override;
+  base::WeakPtr<background_fetch::DatabaseTaskHost> GetWeakPtr() override;
 
   void Cleanup();
 
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index b22cdfe..b011a2ff 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -990,6 +990,31 @@
                                                   kAlternativeDeveloperId));
 }
 
+TEST_F(BackgroundFetchDataManagerTest, StorageVersionIsPersisted) {
+  int64_t sw_id = RegisterServiceWorker();
+  ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
+
+  {
+    // Create a single registration.
+    BackgroundFetchRegistrationId registration_id(
+        sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
+    EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
+
+    std::vector<blink::mojom::FetchAPIRequestPtr> requests =
+        CreateValidRequests(origin(), 2u);
+    auto options = blink::mojom::BackgroundFetchOptions::New();
+    blink::mojom::BackgroundFetchError error;
+    CreateRegistration(registration_id, CloneRequestVector(requests),
+                       options.Clone(), SkBitmap(), &error);
+    ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+  }
+
+  auto storage_versions = GetRegistrationUserDataByKeyPrefix(
+      sw_id, background_fetch::StorageVersionKey(kExampleUniqueId));
+  ASSERT_EQ(storage_versions.size(), 1u);
+  EXPECT_EQ(storage_versions[0], base::NumberToString(proto::SV_CURRENT));
+}
+
 TEST_F(BackgroundFetchDataManagerTest, GetRegistration) {
   int64_t sw_id = RegisterServiceWorker();
   ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
@@ -1962,7 +1987,8 @@
 
   RestartDataManagerFromPersistentStorage();
 
-  EXPECT_EQ(4u,  // Metadata proto + UI options + remaining pending fetches.
+  // Metadata proto + UI options + storage version + remaining pending fetches.
+  EXPECT_EQ(5u,
             GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
 
   // Cleanup should delete the registration.
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index 510b1b4..3c2204e 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -28,6 +28,7 @@
 #include "content/common/service_worker/service_worker_types.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
@@ -64,29 +65,16 @@
                       });
 }
 
-class BadMessageObserver {
+class FakeMojoMessageDispatchContext {
  public:
-  BadMessageObserver()
-      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {
-    mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
-        &BadMessageObserver::ReportBadMessage, base::Unretained(this)));
-  }
-
-  ~BadMessageObserver() {
-    mojo::core::SetDefaultProcessErrorCallback(
-        mojo::core::ProcessErrorCallback());
-  }
-
-  const std::string& last_error() const { return last_error_; }
+  FakeMojoMessageDispatchContext()
+      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {}
 
  private:
-  void ReportBadMessage(const std::string& error) { last_error_ = error; }
-
   mojo::Message dummy_message_;
   mojo::internal::MessageDispatchContext context_;
-  std::string last_error_;
 
-  DISALLOW_COPY_AND_ASSIGN(BadMessageObserver);
+  DISALLOW_COPY_AND_ASSIGN(FakeMojoMessageDispatchContext);
 };
 
 std::vector<blink::mojom::FetchAPIRequestPtr> CloneRequestVector(
@@ -437,7 +425,8 @@
 
   // The |developer_id| must be a non-empty string.
   {
-    BadMessageObserver bad_message_observer;
+    FakeMojoMessageDispatchContext fake_dispatch_context;
+    mojo::test::BadMessageObserver bad_message_observer;
     std::vector<blink::mojom::FetchAPIRequestPtr> requests;
     requests.push_back(CreateDefaultRequest());
 
@@ -448,12 +437,13 @@
           std::move(requests), options.Clone(), SkBitmap(), &error,
           &registration);
     ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ARGUMENT);
-    EXPECT_EQ("Invalid developer_id", bad_message_observer.last_error());
+    EXPECT_EQ("Invalid developer_id", bad_message_observer.WaitForBadMessage());
   }
 
   // At least a single blink::mojom::FetchAPIRequestPtr must be given.
   {
-    BadMessageObserver bad_message_observer;
+    FakeMojoMessageDispatchContext fake_dispatch_context;
+    mojo::test::BadMessageObserver bad_message_observer;
     std::vector<blink::mojom::FetchAPIRequestPtr> requests;
     // |requests| has deliberately been left empty.
 
@@ -464,7 +454,7 @@
           std::move(requests), std::move(options), SkBitmap(), &error,
           &registration);
     ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ARGUMENT);
-    EXPECT_EQ("Invalid requests", bad_message_observer.last_error());
+    EXPECT_EQ("Invalid requests", bad_message_observer.WaitForBadMessage());
   }
 }
 
@@ -857,12 +847,13 @@
   // return INVALID_ARGUMENT when an invalid |developer_id| is sent over the
   // Mojo channel.
 
-  BadMessageObserver bad_message_observer;
+  FakeMojoMessageDispatchContext fake_dispatch_context;
+  mojo::test::BadMessageObserver bad_message_observer;
   blink::mojom::BackgroundFetchError error;
   Abort(/* service_worker_registration_id= */ 42, /* developer_id= */ "",
         kExampleUniqueId, &error);
   ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ARGUMENT);
-  EXPECT_EQ("Invalid developer_id", bad_message_observer.last_error());
+  EXPECT_EQ("Invalid developer_id", bad_message_observer.WaitForBadMessage());
 }
 
 TEST_F(BackgroundFetchServiceTest, AbortInvalidUniqueIdArgument) {
@@ -870,12 +861,13 @@
   // return INVALID_ARGUMENT when an invalid |unique_id| is sent over the Mojo
   // channel.
 
-  BadMessageObserver bad_message_observer;
+  FakeMojoMessageDispatchContext fake_dispatch_context;
+  mojo::test::BadMessageObserver bad_message_observer;
   blink::mojom::BackgroundFetchError error;
   Abort(/* service_worker_registration_id= */ 42, kExampleDeveloperId,
         /* unique_id= */ "not a GUID", &error);
   ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ARGUMENT);
-  EXPECT_EQ("Invalid unique_id", bad_message_observer.last_error());
+  EXPECT_EQ("Invalid unique_id", bad_message_observer.WaitForBadMessage());
 }
 
 TEST_F(BackgroundFetchServiceTest, AbortUnknownUniqueId) {
diff --git a/content/browser/background_fetch/storage/README.md b/content/browser/background_fetch/storage/README.md
index ccaaa2e..72ad448 100644
--- a/content/browser/background_fetch/storage/README.md
+++ b/content/browser/background_fetch/storage/README.md
@@ -45,6 +45,11 @@
 value: "<serialized content::proto::BackgroundFetchCompletedRequest>"
 ```
 
+```
+key: "bgfetch_storage_version_<unique_id>"
+value: "<content::proto::BackgroundFetchStorageVersion>"
+```
+
 ## Cache Storage UserData schema
 
 The downloaded responses of every fetch will be stored in their own private cache.
diff --git a/content/browser/background_fetch/storage/create_metadata_task.cc b/content/browser/background_fetch/storage/create_metadata_task.cc
index 3e7fbf6b..d28d6ca 100644
--- a/content/browser/background_fetch/storage/create_metadata_task.cc
+++ b/content/browser/background_fetch/storage/create_metadata_task.cc
@@ -296,7 +296,8 @@
   // - DeveloperId -> UniqueID
   // - BackgroundFetchMetadata
   // - BackgroundFetchUIOptions
-  entries.reserve(requests_.size() + 3);
+  // - BackgroundFetchStorageVersion
+  entries.reserve(requests_.size() + 4u);
 
   std::string serialized_metadata_proto;
 
@@ -325,6 +326,9 @@
                        std::move(serialized_metadata_proto));
   entries.emplace_back(UIOptionsKey(registration_id_.unique_id()),
                        serialized_ui_options_proto);
+  entries.emplace_back(
+      StorageVersionKey(registration_id_.unique_id()),
+      base::NumberToString(proto::BackgroundFetchStorageVersion::SV_CURRENT));
 
   // Signed integers are used for request indexes to avoid unsigned gotchas.
   for (int i = 0; i < base::checked_cast<int>(requests_.size()); i++) {
diff --git a/content/browser/background_fetch/storage/database_helpers.cc b/content/browser/background_fetch/storage/database_helpers.cc
index 3b76eca..41ed22f4 100644
--- a/content/browser/background_fetch/storage/database_helpers.cc
+++ b/content/browser/background_fetch/storage/database_helpers.cc
@@ -53,6 +53,10 @@
   return CompletedRequestKeyPrefix(unique_id) + std::to_string(request_index);
 }
 
+std::string StorageVersionKey(const std::string& unique_id) {
+  return kStorageVersionKeyPrefix + unique_id;
+}
+
 DatabaseStatus ToDatabaseStatus(blink::ServiceWorkerStatusCode status) {
   switch (status) {
     case blink::ServiceWorkerStatusCode::kOk:
diff --git a/content/browser/background_fetch/storage/database_helpers.h b/content/browser/background_fetch/storage/database_helpers.h
index 668d5f22..4a43dc7 100644
--- a/content/browser/background_fetch/storage/database_helpers.h
+++ b/content/browser/background_fetch/storage/database_helpers.h
@@ -32,6 +32,7 @@
 const char kPendingRequestKeyPrefix[] = "bgfetch_pending_request_";
 const char kActiveRequestKeyPrefix[] = "bgfetch_active_request_";
 const char kCompletedRequestKeyPrefix[] = "bgfetch_completed_request_";
+const char kStorageVersionKeyPrefix[] = "bgfetch_storage_version_";
 
 // Database Keys.
 CONTENT_EXPORT std::string ActiveRegistrationUniqueIdKey(
@@ -54,6 +55,8 @@
 std::string CompletedRequestKey(const std::string& unique_id,
                                 int request_index);
 
+CONTENT_EXPORT std::string StorageVersionKey(const std::string& unique_id);
+
 // Database status.
 enum class DatabaseStatus { kOk, kFailed, kNotFound };
 
diff --git a/content/browser/background_fetch/storage/database_task.cc b/content/browser/background_fetch/storage/database_task.cc
index 3199e0b2..8907d0b 100644
--- a/content/browser/background_fetch/storage/database_task.cc
+++ b/content/browser/background_fetch/storage/database_task.cc
@@ -4,7 +4,9 @@
 
 #include "content/browser/background_fetch/storage/database_task.h"
 
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/metrics/histogram_functions.h"
 #include "content/browser/background_fetch/background_fetch_data_manager.h"
@@ -33,15 +35,12 @@
 
 }  // namespace
 
-DatabaseTaskHost::DatabaseTaskHost() : weak_factory_(this) {}
+DatabaseTaskHost::DatabaseTaskHost() = default;
 
 DatabaseTaskHost::~DatabaseTaskHost() = default;
 
-base::WeakPtr<DatabaseTaskHost> DatabaseTaskHost::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-DatabaseTask::DatabaseTask(DatabaseTaskHost* host) : host_(host) {
+DatabaseTask::DatabaseTask(DatabaseTaskHost* host)
+    : host_(host), weak_ptr_factory_(this) {
   DCHECK(host_);
   // Hold a reference to the CacheStorageManager.
   cache_manager_ = data_manager()->cache_manager();
@@ -49,6 +48,10 @@
 
 DatabaseTask::~DatabaseTask() = default;
 
+base::WeakPtr<DatabaseTaskHost> DatabaseTask::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 void DatabaseTask::Finished() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Post the OnTaskFinished callback to the same thread, to allow the the
@@ -90,6 +93,40 @@
       base::BindOnce(&DidGetUsageAndQuota, std::move(callback), size));
 }
 
+void DatabaseTask::GetStorageVersion(int64_t service_worker_registration_id,
+                                     const std::string& unique_id,
+                                     StorageVersionCallback callback) {
+  service_worker_context()->GetRegistrationUserData(
+      service_worker_registration_id, {StorageVersionKey(unique_id)},
+      base::BindOnce(&DatabaseTask::DidGetStorageVersion,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DatabaseTask::DidGetStorageVersion(StorageVersionCallback callback,
+                                        const std::vector<std::string>& data,
+                                        blink::ServiceWorkerStatusCode status) {
+  switch (ToDatabaseStatus(status)) {
+    case DatabaseStatus::kNotFound:
+    case DatabaseStatus::kFailed:
+      // This shouldn't happen.
+      std::move(callback).Run(proto::SV_UNINITIALIZED);
+      return;
+    case DatabaseStatus::kOk:
+      break;
+  }
+
+  DCHECK_EQ(data.size(), 1u);
+  int storage_version = proto::SV_UNINITIALIZED;
+
+  if (!base::StringToInt(data[0], &storage_version) ||
+      !proto::BackgroundFetchStorageVersion_IsValid(storage_version)) {
+    storage_version = proto::SV_UNINITIALIZED;
+  }
+
+  std::move(callback).Run(
+      static_cast<proto::BackgroundFetchStorageVersion>(storage_version));
+}
+
 void DatabaseTask::SetStorageError(BackgroundFetchStorageError error) {
   DCHECK_NE(BackgroundFetchStorageError::kNone, error);
   switch (storage_error_) {
diff --git a/content/browser/background_fetch/storage/database_task.h b/content/browser/background_fetch/storage/database_task.h
index 3453bee..9109d89 100644
--- a/content/browser/background_fetch/storage/database_task.h
+++ b/content/browser/background_fetch/storage/database_task.h
@@ -12,8 +12,10 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "content/browser/background_fetch/background_fetch.pb.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
+#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
 
 namespace storage {
@@ -48,13 +50,10 @@
   virtual BackgroundFetchDataManager* data_manager() = 0;
   virtual ~DatabaseTaskHost();
 
-  base::WeakPtr<DatabaseTaskHost> GetWeakPtr();
+  virtual base::WeakPtr<DatabaseTaskHost> GetWeakPtr() = 0;
 
  protected:
   DatabaseTaskHost();
-
- private:
-  base::WeakPtrFactory<DatabaseTaskHost> weak_factory_;
 };
 
 // A DatabaseTask is an asynchronous "transaction" that needs to read/write the
@@ -70,8 +69,9 @@
 // as they are added, and cannot outlive the parent DatabaseTask.
 class DatabaseTask : public DatabaseTaskHost {
  public:
-  using IsQuotaAvailableCallback =
-      base::OnceCallback<void(bool /* is_available */)>;
+  using IsQuotaAvailableCallback = base::OnceCallback<void(bool is_available)>;
+  using StorageVersionCallback =
+      base::OnceCallback<void(proto::BackgroundFetchStorageVersion)>;
 
   ~DatabaseTask() override;
 
@@ -122,6 +122,10 @@
                         int64_t size,
                         IsQuotaAvailableCallback callback);
 
+  void GetStorageVersion(int64_t service_worker_registration_id,
+                         const std::string& unique_id,
+                         StorageVersionCallback callback);
+
   CacheStorageHandle GetOrOpenCacheStorage(
       const BackgroundFetchRegistrationId& registration_id);
   CacheStorageHandle GetOrOpenCacheStorage(const url::Origin& origin,
@@ -141,6 +145,12 @@
   // The Histogram name to report with the Error.
   virtual std::string HistogramName() const;
 
+  void DidGetStorageVersion(StorageVersionCallback callback,
+                            const std::vector<std::string>& data,
+                            blink::ServiceWorkerStatusCode status);
+
+  base::WeakPtr<DatabaseTaskHost> GetWeakPtr() override;
+
   DatabaseTaskHost* host_;
 
   // Owns a reference to the CacheStorageManager in case Shutdown was
@@ -154,6 +164,8 @@
   BackgroundFetchStorageError storage_error_ =
       BackgroundFetchStorageError::kNone;
 
+  base::WeakPtrFactory<DatabaseTask> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(DatabaseTask);
 };
 
diff --git a/content/browser/background_fetch/storage/delete_registration_task.cc b/content/browser/background_fetch/storage/delete_registration_task.cc
index 3a807a8f..0aa71fa 100644
--- a/content/browser/background_fetch/storage/delete_registration_task.cc
+++ b/content/browser/background_fetch/storage/delete_registration_task.cc
@@ -105,9 +105,9 @@
 #endif  // DCHECK_IS_ON()
 
   std::vector<std::string> deletion_key_prefixes{
-      RegistrationKey(unique_id_), UIOptionsKey(unique_id_),
-      PendingRequestKeyPrefix(unique_id_), ActiveRequestKeyPrefix(unique_id_),
-      CompletedRequestKeyPrefix(unique_id_)};
+      RegistrationKey(unique_id_),           UIOptionsKey(unique_id_),
+      PendingRequestKeyPrefix(unique_id_),   ActiveRequestKeyPrefix(unique_id_),
+      CompletedRequestKeyPrefix(unique_id_), StorageVersionKey(unique_id_)};
 
   service_worker_context()->ClearRegistrationUserDataByKeyPrefixes(
       service_worker_registration_id_, std::move(deletion_key_prefixes),
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc
index 31bad2c..944324e 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.cc
+++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -271,7 +271,7 @@
   DCHECK(blob_context);
   for (size_t i = 0; i < body->elements()->size(); ++i) {
     const network::DataElement& element = (*body->elements())[i];
-    if (element.type() != network::DataElement::TYPE_BLOB)
+    if (element.type() != network::mojom::DataElementType::kBlob)
       continue;
     std::unique_ptr<storage::BlobDataHandle> handle =
         blob_context->GetBlobDataFromUUID(element.blob_uuid());
diff --git a/content/browser/bluetooth/bluetooth_allowed_devices.cc b/content/browser/bluetooth/bluetooth_allowed_devices.cc
index a3c2607..dda74a0 100644
--- a/content/browser/bluetooth/bluetooth_allowed_devices.cc
+++ b/content/browser/bluetooth/bluetooth_allowed_devices.cc
@@ -26,15 +26,24 @@
 const WebBluetoothDeviceId& BluetoothAllowedDevices::AddDevice(
     const std::string& device_address,
     const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
+  auto& device_id = AddDevice(device_address);
+  AddUnionOfServicesTo(options, &device_id_to_services_map_[device_id]);
+
+  // Currently, devices that are added with WebBluetoothRequestDeviceOptionsPtr
+  // |options| come from RequestDevice() and therefore have the ablity to be
+  // connected to.
+  device_id_to_connectable_map_[device_id] = true;
+
+  return device_id;
+}
+
+const WebBluetoothDeviceId& BluetoothAllowedDevices::AddDevice(
+    const std::string& device_address) {
   DVLOG(1) << "Adding a device to Map of Allowed Devices.";
 
   auto id_iter = device_address_to_id_map_.find(device_address);
   if (id_iter != device_address_to_id_map_.end()) {
     DVLOG(1) << "Device already in map of allowed devices.";
-    const auto& device_id = id_iter->second;
-
-    AddUnionOfServicesTo(options, &device_id_to_services_map_[device_id]);
-
     return device_address_to_id_map_[device_address];
   }
   const WebBluetoothDeviceId device_id = GenerateUniqueDeviceId();
@@ -42,7 +51,6 @@
 
   device_address_to_id_map_[device_address] = device_id;
   device_id_to_address_map_[device_id] = device_address;
-  AddUnionOfServicesTo(options, &device_id_to_services_map_[device_id]);
 
   CHECK(device_id_set_.insert(device_id).second);
 
@@ -62,6 +70,9 @@
   CHECK(device_id_to_address_map_.erase(device_id));
   CHECK(device_id_to_services_map_.erase(device_id));
 
+  // Not all devices are connectable.
+  device_id_to_connectable_map_.erase(device_id);
+
   // 2. Remove from set of ids.
   CHECK(device_id_set_.erase(device_id));
 }
@@ -105,6 +116,14 @@
              : base::ContainsKey(id_iter->second, service_uuid);
 }
 
+bool BluetoothAllowedDevices::IsAllowedToGATTConnect(
+    const WebBluetoothDeviceId& device_id) const {
+  auto id_iter = device_id_to_connectable_map_.find(device_id);
+  if (id_iter == device_id_to_connectable_map_.end())
+    return false;
+  return id_iter->second;
+}
+
 WebBluetoothDeviceId BluetoothAllowedDevices::GenerateUniqueDeviceId() {
   WebBluetoothDeviceId device_id = WebBluetoothDeviceId::Create();
   while (base::ContainsKey(device_id_set_, device_id)) {
diff --git a/content/browser/bluetooth/bluetooth_allowed_devices.h b/content/browser/bluetooth/bluetooth_allowed_devices.h
index dc5223c2..ce9d66d 100644
--- a/content/browser/bluetooth/bluetooth_allowed_devices.h
+++ b/content/browser/bluetooth/bluetooth_allowed_devices.h
@@ -40,6 +40,10 @@
       const std::string& device_address,
       const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options);
 
+  // Same as the above version of |AddDevice| but does not add any services to
+  // the device id -> services map.
+  const WebBluetoothDeviceId& AddDevice(const std::string& device_address);
+
   // Removes the Bluetooth Device with |device_address| from the map of allowed
   // devices.
   void RemoveDevice(const std::string& device_address);
@@ -62,6 +66,8 @@
       const WebBluetoothDeviceId& device_id,
       const device::BluetoothUUID& service_uuid) const;
 
+  bool IsAllowedToGATTConnect(const WebBluetoothDeviceId& device_id) const;
+
  private:
   typedef std::unordered_map<std::string, WebBluetoothDeviceId>
       DeviceAddressToIdMap;
@@ -73,6 +79,9 @@
       std::unordered_set<device::BluetoothUUID, device::BluetoothUUIDHash>,
       WebBluetoothDeviceIdHash>
       DeviceIdToServicesMap;
+  typedef std::
+      unordered_map<WebBluetoothDeviceId, bool, WebBluetoothDeviceIdHash>
+          DeviceIdToConnectableMap;
 
   // Returns an id guaranteed to be unique for the map. The id is randomly
   // generated so that an origin can't guess the id used in another origin.
@@ -85,6 +94,7 @@
   DeviceAddressToIdMap device_address_to_id_map_;
   DeviceIdToAddressMap device_id_to_address_map_;
   DeviceIdToServicesMap device_id_to_services_map_;
+  DeviceIdToConnectableMap device_id_to_connectable_map_;
 
   // Keep track of all device_ids in the map.
   std::unordered_set<WebBluetoothDeviceId, WebBluetoothDeviceIdHash>
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index 0ec5c2e..4728c4f0 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -365,7 +365,7 @@
 }
 
 void WebBluetoothServiceImpl::DeviceAdvertisementReceived(
-    const std::string& device_id,
+    const std::string& device_address,
     const base::Optional<std::string>& device_name,
     const base::Optional<std::string>& advertisement_name,
     base::Optional<int8_t> rssi,
@@ -382,15 +382,7 @@
   auto client = scanning_clients_.begin();
   while (client != scanning_clients_.end()) {
     auto device = blink::mojom::WebBluetoothDevice::New();
-    // TODO(dougt):
-    // Currently, Web Bluetooth (GATT Connections) obfuscate the device
-    // address, here called the |device->id|. However, for scanning, it
-    // may be desirable to expose the actual MAC.
-    //
-    // What needs to happen next is for us to figure out if we can expose
-    // the MAC address or if we need to obfuscate like we do with GATT
-    // Connections.
-    device->id = WebBluetoothDeviceId(device_id, /*is_mac_address=*/true);
+    device->id = allowed_devices().AddDevice(device_address);
     device->name = device_name;
 
     auto result = blink::mojom::WebBluetoothScanResult::New();
@@ -520,6 +512,12 @@
     RemoteServerConnectCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  if (!allowed_devices().IsAllowedToGATTConnect(device_id)) {
+    std::move(callback).Run(
+        blink::mojom::WebBluetoothResult::GATT_NOT_AUTHORIZED);
+    return;
+  }
+
   const CacheQueryResult query_result = QueryCacheForDevice(device_id);
 
   if (query_result.outcome != CacheQueryOutcome::SUCCESS) {
diff --git a/content/browser/broadcast_channel/broadcast_channel_provider.cc b/content/browser/broadcast_channel/broadcast_channel_provider.cc
index 17f996c1..8e7ddea5 100644
--- a/content/browser/broadcast_channel/broadcast_channel_provider.cc
+++ b/content/browser/broadcast_channel/broadcast_channel_provider.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/stl_util.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -65,9 +66,10 @@
 
 BroadcastChannelProvider::BroadcastChannelProvider() {}
 
-void BroadcastChannelProvider::Connect(
+mojo::BindingId BroadcastChannelProvider::Connect(
+    RenderProcessHostId render_process_host_id,
     blink::mojom::BroadcastChannelProviderRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+  return bindings_.AddBinding(this, std::move(request), render_process_host_id);
 }
 
 void BroadcastChannelProvider::ConnectToChannel(
@@ -75,6 +77,13 @@
     const std::string& name,
     blink::mojom::BroadcastChannelClientAssociatedPtrInfo client,
     blink::mojom::BroadcastChannelClientAssociatedRequest connection) {
+  RenderProcessHostId process_id = bindings_.dispatch_context();
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  if (!policy->CanAccessDataForOrigin(process_id, origin.GetURL())) {
+    mojo::ReportBadMessage("BROADCAST_CHANNEL_INVALID_ORIGIN");
+    return;
+  }
+
   std::unique_ptr<Connection> c(new Connection(origin, name, std::move(client),
                                                std::move(connection), this));
   c->set_connection_error_handler(
diff --git a/content/browser/broadcast_channel/broadcast_channel_provider.h b/content/browser/broadcast_channel/broadcast_channel_provider.h
index 7b8dea3..adf47ca3 100644
--- a/content/browser/broadcast_channel/broadcast_channel_provider.h
+++ b/content/browser/broadcast_channel/broadcast_channel_provider.h
@@ -8,18 +8,23 @@
 #include <map>
 
 #include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom.h"
 #include "url/origin.h"
 
 namespace content {
 
-class BroadcastChannelProvider
+class CONTENT_EXPORT BroadcastChannelProvider
     : public base::RefCountedThreadSafe<BroadcastChannelProvider>,
-  public blink::mojom::BroadcastChannelProvider {
+      public blink::mojom::BroadcastChannelProvider {
  public:
   BroadcastChannelProvider();
-  void Connect(blink::mojom::BroadcastChannelProviderRequest request);
+
+  using RenderProcessHostId = int;
+  mojo::BindingId Connect(
+      RenderProcessHostId render_process_host_id,
+      blink::mojom::BroadcastChannelProviderRequest request);
 
   void ConnectToChannel(
       const url::Origin& origin,
@@ -28,6 +33,8 @@
       blink::mojom::BroadcastChannelClientAssociatedRequest connection)
       override;
 
+  auto& bindings_for_testing() { return bindings_; }
+
  private:
   friend class base::RefCountedThreadSafe<BroadcastChannelProvider>;
   class Connection;
@@ -38,7 +45,8 @@
   void ReceivedMessageOnConnection(Connection*,
                                    const blink::CloneableMessage& message);
 
-  mojo::BindingSet<blink::mojom::BroadcastChannelProvider> bindings_;
+  mojo::BindingSet<blink::mojom::BroadcastChannelProvider, RenderProcessHostId>
+      bindings_;
   std::map<url::Origin, std::multimap<std::string, std::unique_ptr<Connection>>>
       connections_;
 };
diff --git a/content/browser/browser_main_runner_impl.cc b/content/browser/browser_main_runner_impl.cc
index f7794627..5e6c0fff 100644
--- a/content/browser/browser_main_runner_impl.cc
+++ b/content/browser/browser_main_runner_impl.cc
@@ -49,8 +49,8 @@
 }  // namespace
 
 // static
-BrowserMainRunnerImpl* BrowserMainRunnerImpl::Create() {
-  return new BrowserMainRunnerImpl();
+std::unique_ptr<BrowserMainRunnerImpl> BrowserMainRunnerImpl::Create() {
+  return std::make_unique<BrowserMainRunnerImpl>();
 }
 
 BrowserMainRunnerImpl::BrowserMainRunnerImpl()
@@ -240,7 +240,7 @@
 }
 
 // static
-BrowserMainRunner* BrowserMainRunner::Create() {
+std::unique_ptr<BrowserMainRunner> BrowserMainRunner::Create() {
   return BrowserMainRunnerImpl::Create();
 }
 
diff --git a/content/browser/browser_main_runner_impl.h b/content/browser/browser_main_runner_impl.h
index 7ab5d1c..77c2861 100644
--- a/content/browser/browser_main_runner_impl.h
+++ b/content/browser/browser_main_runner_impl.h
@@ -25,7 +25,7 @@
 
 class BrowserMainRunnerImpl : public BrowserMainRunner {
  public:
-  static BrowserMainRunnerImpl* Create();
+  static std::unique_ptr<BrowserMainRunnerImpl> Create();
 
   BrowserMainRunnerImpl();
   ~BrowserMainRunnerImpl() override;
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
index ec2bd3aa..3c0ec0d 100644
--- a/content/browser/cache_storage/cache_storage_dispatcher_host.cc
+++ b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
@@ -405,13 +405,4 @@
       origin, CacheStorageOwner::kCacheAPI);
 }
 
-bool CacheStorageDispatcherHost::ValidState() {
-  // cache_manager() can return nullptr when process is shutting down.
-  if (!(context_ && context_->cache_manager())) {
-    bindings_.CloseAllBindings();
-    return false;
-  }
-  return true;
-}
-
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.h b/content/browser/cache_storage/cache_storage_dispatcher_host.h
index 1f07b73..47e1460 100644
--- a/content/browser/cache_storage/cache_storage_dispatcher_host.h
+++ b/content/browser/cache_storage/cache_storage_dispatcher_host.h
@@ -70,11 +70,6 @@
   // Called by Init() on IO thread.
   void CreateCacheListener(CacheStorageContextImpl* context);
 
-  // Validate the current state of required members, returns false if they
-  // aren't valid and also close |bindings_|, so it's safe to not run
-  // mojo callbacks.
-  bool ValidState();
-
   scoped_refptr<CacheStorageContextImpl> context_;
 
   mojo::StrongBindingSet<blink::mojom::CacheStorage> bindings_;
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 1280f5e..2b832c85 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -483,8 +483,33 @@
 }
 
 void ChildProcessSecurityPolicyImpl::Remove(int child_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::AutoLock lock(lock_);
+
+  auto state = security_state_.find(child_id);
+  if (state == security_state_.end())
+    return;
+
+  // Moving the existing SecurityState object into a pending map so
+  // that we can preserve permission state and avoid mutations to this
+  // state after Remove() has been called.
+  pending_remove_state_[child_id] = std::move(state->second);
   security_state_.erase(child_id);
+
+  // |child_id| could be inside tasks that are on the UI thread and IO thread
+  // task queues. We need to keep the |pending_remove_state_| entry around
+  // until we have successfully executed a task on the IO thread followed by
+  // a task on the UI thread. This should ensure that any pending tasks on
+  // either thread will have completed before we remove the entry.
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE, {BrowserThread::IO}, base::DoNothing(),
+      base::BindOnce(
+          [](ChildProcessSecurityPolicyImpl* policy, int child_id) {
+            DCHECK_CURRENTLY_ON(BrowserThread::UI);
+            base::AutoLock lock(policy->lock_);
+            policy->pending_remove_state_.erase(child_id);
+          },
+          base::Unretained(this), child_id));
 }
 
 void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme(
@@ -966,25 +991,25 @@
 
   for (const network::DataElement& element : *body->elements()) {
     switch (element.type()) {
-      case network::DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         if (!CanReadFile(child_id, element.path()))
           return false;
         break;
 
-      case network::DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         // Data is self-contained within |body| - no need to check access.
         break;
 
-      case network::DataElement::TYPE_BLOB:
+      case network::mojom::DataElementType::kBlob:
         // No need to validate - the unguessability of the uuid of the blob is a
         // sufficient defense against access from an unrelated renderer.
         break;
 
-      case network::DataElement::TYPE_DATA_PIPE:
+      case network::mojom::DataElementType::kDataPipe:
         // Data is self-contained within |body| - no need to check access.
         break;
 
-      case network::DataElement::TYPE_UNKNOWN:
+      case network::mojom::DataElementType::kUnknown:
       default:
         // Fail safe - deny access.
         NOTREACHED();
@@ -1187,30 +1212,30 @@
   DCHECK(IsRunningOnExpectedThread());
 
   base::AutoLock lock(lock_);
-  auto state = security_state_.find(child_id);
-  if (state == security_state_.end()) {
-    // TODO(nick): Returning true instead of false here is a temporary
-    // workaround for https://crbug.com/600441
-    return true;
-  }
+  SecurityState* security_state = GetSecurityState(child_id);
 
   // Determine the BrowsingInstance ID for calculating the expected process
   // lock URL.
-  BrowsingInstanceId browsing_instance_id =
-      state->second->lowest_browsing_instance_id();
+  BrowsingInstanceId browsing_instance_id;
+
+  if (security_state)
+    browsing_instance_id = security_state->lowest_browsing_instance_id();
 
   GURL expected_process_lock = SiteInstanceImpl::DetermineProcessLockURL(
       nullptr, IsolationContext(browsing_instance_id), url);
 
-  bool can_access =
-      state->second->CanAccessDataForOrigin(expected_process_lock);
+  bool can_access = security_state && security_state->CanAccessDataForOrigin(
+                                          expected_process_lock);
   if (!can_access) {
     // Returning false here will result in a renderer kill.  Set some crash
     // keys that will help understand the circumstances of that kill.
     base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(),
                                    expected_process_lock.spec());
+
     base::debug::SetCrashKeyString(bad_message::GetKilledProcessOriginLockKey(),
-                                   state->second->origin_lock().spec());
+                                   security_state
+                                       ? security_state->origin_lock().spec()
+                                       : "(child id not found)");
 
     static auto* requested_origin_key = base::debug::AllocateCrashKeyString(
         "requested_origin", base::debug::CrashKeySize::Size64);
@@ -1471,4 +1496,20 @@
     isolated_origins_.erase(key);
 }
 
+ChildProcessSecurityPolicyImpl::SecurityState*
+ChildProcessSecurityPolicyImpl::GetSecurityState(int child_id) {
+  auto itr = security_state_.find(child_id);
+  if (itr != security_state_.end())
+    return itr->second.get();
+
+  // Check to see if |child_id| is in the pending removal map since this
+  // may be a call that was already on the IO or UI thread's task queue when the
+  // Remove() call occurred.
+  itr = pending_remove_state_.find(child_id);
+  if (itr != pending_remove_state_.end())
+    return itr->second.get();
+
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index e89f227..fa3c693 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -170,6 +170,13 @@
 
   // Upon destruction, child processess should unregister themselves by caling
   // this method exactly once.
+  //
+  // Note: Pre-Remove() permissions remain in effect until the task posted
+  // to the IO thread by this call runs AND the task posted, by that IO thread
+  // task, to the UI thread removes the entry from |pending_remove_state_|.
+  // This UI -> IO -> UI task sequence ensures that any pending tasks on either
+  // thread for this |child_id| are allowed to run before access is completely
+  // revoked.
   void Remove(int child_id);
 
   // Whenever the browser processes commands the child process to commit a URL,
@@ -433,6 +440,11 @@
       const std::string& filesystem_id,
       int permission);
 
+  // Gets the SecurityState object associated with |child_id|.
+  // Note: Returned object is only valid for the duration the caller holds
+  // |lock_|.
+  SecurityState* GetSecurityState(int child_id) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
   // You must acquire this lock before reading or writing any members of this
   // class, except for isolated_origins_ which uses its own lock.  You must not
   // block while holding this lock.
@@ -455,6 +467,15 @@
   // not escape this class.
   SecurityStateMap security_state_ GUARDED_BY(lock_);
 
+  // This map holds the SecurityState for a child process after Remove()
+  // is called on the UI thread. An entry stays in this map until a task has
+  // run on the IO thread and then a task posted from there runs on the UI
+  // thread. This is necessary to provide consistent security decisions and
+  // avoid races between the UI & IO threads during child process shutdown.
+  // This separate map is used to preserve SecurityState info AND
+  // preventing mutation of that state after Remove() is called.
+  SecurityStateMap pending_remove_state_ GUARDED_BY(lock_);
+
   FileSystemPermissionPolicyMap file_system_policy_map_ GUARDED_BY(lock_);
 
   // You must acquire this lock before reading or writing isolated_origins_.
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 6b9be49..e2961b9 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -7,6 +7,8 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/mock_log.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/site_instance_impl.h"
@@ -60,7 +62,9 @@
 
 class ChildProcessSecurityPolicyTest : public testing::Test {
  public:
-  ChildProcessSecurityPolicyTest() : old_browser_client_(nullptr) {}
+  ChildProcessSecurityPolicyTest()
+      : thread_bundle_(TestBrowserThreadBundle::REAL_IO_THREAD),
+        old_browser_client_(nullptr) {}
 
   void SetUp() override {
     old_browser_client_ = SetBrowserClientForTesting(&test_browser_client_);
@@ -1045,6 +1049,170 @@
   EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 }
 
+// Tests behavior of CanAccessDataForOrigin() during race conditions that
+// can occur during Remove(). It verifies that permissions for a child ID are
+// preserved after a Remove() call until the task, that Remove() has posted to
+// the IO thread, has run AND the task posted back to the UI thread has also
+// run.
+//
+// We use a combination of waitable events and extra tasks posted to the
+// threads to capture permission state from the UI & IO threads during the
+// removal process. It is intended to simulate pending tasks that could be
+// run on each thread during removal.
+TEST_F(ChildProcessSecurityPolicyTest, RemoveRace_CanAccessDataForOrigin) {
+  ChildProcessSecurityPolicyImpl* p =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+
+  GURL url("file:///etc/passwd");
+
+  p->Add(kRendererID);
+
+  base::WaitableEvent ready_for_remove_event;
+  base::WaitableEvent remove_called_event;
+  base::WaitableEvent pending_remove_complete_event;
+
+  // Keep track of the return value for CanAccessDataForOrigin at various
+  // points in time during the test.
+  bool io_before_remove = false;
+  bool io_while_io_task_pending = false;
+  bool io_after_io_task_completed = false;
+  bool ui_before_remove = false;
+  bool ui_while_io_task_pending = false;
+  bool ui_after_io_task_completed = false;
+
+  // Post a task that will run on the IO thread before the task that
+  // Remove() will post to the IO thread.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        // Capture state on the IO thread before Remove() is called.
+        io_before_remove = p->CanAccessDataForOrigin(kRendererID, url);
+
+        // Tell the UI thread we are ready for Remove() to be called.
+        ready_for_remove_event.Signal();
+
+        // Wait for Remove() to be called on the UI thread.
+        remove_called_event.Wait();
+
+        // Capture state after Remove() is called, but before its task on
+        // the IO thread runs.
+        io_while_io_task_pending = p->CanAccessDataForOrigin(kRendererID, url);
+      }));
+
+  ready_for_remove_event.Wait();
+
+  ui_before_remove = p->CanAccessDataForOrigin(kRendererID, url);
+
+  p->Remove(kRendererID);
+
+  // Post a task to run after the task Remove() posted on the IO thread.
+  base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
+                           base::BindLambdaForTesting([&]() {
+                             io_after_io_task_completed =
+                                 p->CanAccessDataForOrigin(kRendererID, url);
+
+                             // Tell the UI thread that the task from Remove()
+                             // has completed on the IO thread.
+                             pending_remove_complete_event.Signal();
+                           }));
+
+  // Capture state after Remove() has been called, but before its IO thread
+  // task has run. We know the IO thread task hasn't run yet because the
+  // task we posted before the Remove() call is waiting for us to signal
+  // |remove_called_event|.
+  ui_while_io_task_pending = p->CanAccessDataForOrigin(kRendererID, url);
+
+  // Unblock the IO thread so the pending remove events can run.
+  remove_called_event.Signal();
+
+  pending_remove_complete_event.Wait();
+
+  // Capture state after IO thread task has run, but before the task it posted
+  // to the UI thread has run.
+  ui_after_io_task_completed = p->CanAccessDataForOrigin(kRendererID, url);
+
+  // Run pending UI thread tasks.
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+
+  bool ui_after_remove_complete = p->CanAccessDataForOrigin(kRendererID, url);
+  bool io_after_remove_complete = false;
+  base::WaitableEvent after_remove_complete_event;
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        io_after_remove_complete = p->CanAccessDataForOrigin(kRendererID, url);
+
+        // Tell the UI thread that this task has
+        // has completed on the IO thread.
+        after_remove_complete_event.Signal();
+      }));
+
+  // Wait for the task we just posted to the IO thread to complete.
+  after_remove_complete_event.Wait();
+
+  // Verify expected states at various parts of the removal.
+  // Note: UI & IO threads are expected to keep pre-Remove() permissions until
+  // the task Remove() posted runs on the IO thread and the task posted from
+  // the IO thread runs on the UI thread.
+  EXPECT_TRUE(io_before_remove);
+  EXPECT_TRUE(io_while_io_task_pending);
+  EXPECT_TRUE(io_after_io_task_completed);
+
+  EXPECT_TRUE(ui_before_remove);
+  EXPECT_TRUE(ui_while_io_task_pending);
+  EXPECT_TRUE(ui_after_io_task_completed);
+
+  EXPECT_FALSE(ui_after_remove_complete);
+  EXPECT_FALSE(io_after_remove_complete);
+}
+
+TEST_F(ChildProcessSecurityPolicyTest, CanAccessDataForOrigin) {
+  ChildProcessSecurityPolicyImpl* p =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+
+  GURL file_url("file:///etc/passwd");
+  GURL http_url("http://foo.com/index.html");
+  GURL http2_url("http://bar.com/index.html");
+
+  // Test invalid ID case.
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, file_url));
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http_url));
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http2_url));
+
+  TestBrowserContext browser_context;
+  p->Add(kRendererID);
+
+  // Verify unlocked origin permissions.
+  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, file_url));
+  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http_url));
+  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http2_url));
+
+  // Lock process to |http_url| origin.
+  scoped_refptr<SiteInstanceImpl> foo_instance =
+      SiteInstanceImpl::CreateForURL(&browser_context, http_url);
+  p->LockToOrigin(foo_instance->GetIsolationContext(), kRendererID,
+                  foo_instance->GetSiteURL());
+
+  // Verify that file access is no longer allowed.
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, file_url));
+  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, http_url));
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http2_url));
+
+  p->Remove(kRendererID);
+
+  // Post a task to the IO loop that then posts a task to the UI loop.
+  // This should cause the |run_loop| to return after the removal has completed.
+  base::RunLoop run_loop;
+  base::PostTaskWithTraitsAndReply(FROM_HERE, {BrowserThread::IO},
+                                   base::DoNothing(), run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Verify invalid ID is rejected now that Remove() has complted.
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, file_url));
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http_url));
+  EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, http2_url));
+}
+
 // Test the granting of origin permissions, and their interactions with
 // granting scheme permissions.
 TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) {
@@ -1204,8 +1372,14 @@
   p->RemoveIsolatedOriginForTesting(baz_http);
 }
 
+// Fails on Fuchsia x64. crbug.com/922732
+#if defined(OS_FUCHSIA)
+#define MAYBE_DynamicIsolatedOrigins DISABLED_DynamicIsolatedOrigins
+#else
+#define MAYBE_DynamicIsolatedOrigins DynamicIsolatedOrigins
+#endif
 // Verifies that isolated origins only apply to future BrowsingInstances.
-TEST_F(ChildProcessSecurityPolicyTest, DynamicIsolatedOrigins) {
+TEST_F(ChildProcessSecurityPolicyTest, MAYBE_DynamicIsolatedOrigins) {
   url::Origin foo = url::Origin::Create(GURL("https://foo.com/"));
   url::Origin bar = url::Origin::Create(GURL("https://bar.com/"));
   url::Origin baz = url::Origin::Create(GURL("https://baz.com/"));
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 9d96054..5aeda7b 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -96,6 +96,7 @@
 #include "ui/ozone/public/overlay_manager_ozone.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/ozone_switches.h"
+#include "ui/ozone/public/platform_window_surface.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 #elif defined(USE_X11)
@@ -236,11 +237,13 @@
 #elif defined(USE_OZONE)
   ui::SurfaceFactoryOzone* factory =
       ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone();
+  std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface =
+      factory->CreatePlatformWindowSurface(widget);
   std::unique_ptr<ui::SurfaceOzoneCanvas> surface_ozone =
       factory->CreateCanvasForWidget(widget);
   CHECK(surface_ozone);
   return std::make_unique<viz::SoftwareOutputDeviceOzone>(
-      std::move(surface_ozone));
+      std::move(platform_window_surface), std::move(surface_ozone));
 #elif defined(USE_X11)
   return std::make_unique<viz::SoftwareOutputDeviceX11>(widget);
 #elif defined(OS_MACOSX)
diff --git a/content/browser/cookie_store/cookie_store_context.cc b/content/browser/cookie_store/cookie_store_context.cc
index 84be9578..87b45691 100644
--- a/content/browser/cookie_store/cookie_store_context.cc
+++ b/content/browser/cookie_store/cookie_store_context.cc
@@ -25,7 +25,7 @@
 
 void CookieStoreContext::Initialize(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
-    base::OnceCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> success_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 #if DCHECK_IS_ON()
   DCHECK(!initialize_called_) << __func__ << " called twice";
@@ -43,12 +43,13 @@
                 task_runner->PostTask(
                     FROM_HERE, base::BindOnce(std::move(callback), result));
               },
-              base::SequencedTaskRunnerHandle::Get(), std::move(callback))));
+              base::SequencedTaskRunnerHandle::Get(),
+              std::move(success_callback))));
 }
 
 void CookieStoreContext::ListenToCookieChanges(
     ::network::mojom::NetworkContext* network_context,
-    base::OnceCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> success_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 #if DCHECK_IS_ON()
   DCHECK(initialize_called_) << __func__ << " called before Initialize()";
@@ -69,7 +70,8 @@
                 task_runner->PostTask(
                     FROM_HERE, base::BindOnce(std::move(callback), result));
               },
-              base::SequencedTaskRunnerHandle::Get(), std::move(callback))));
+              base::SequencedTaskRunnerHandle::Get(),
+              std::move(success_callback))));
 }
 
 void CookieStoreContext::CreateService(blink::mojom::CookieStoreRequest request,
@@ -87,24 +89,24 @@
 
 void CookieStoreContext::InitializeOnIOThread(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
-    base::OnceCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> success_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!cookie_store_manager_) << __func__ << " called more than once";
 
   cookie_store_manager_ =
       std::make_unique<CookieStoreManager>(std::move(service_worker_context));
-  cookie_store_manager_->LoadAllSubscriptions(std::move(callback));
+  cookie_store_manager_->LoadAllSubscriptions(std::move(success_callback));
 }
 
 void CookieStoreContext::ListenToCookieChangesOnIOThread(
     ::network::mojom::CookieManagerPtrInfo cookie_manager_ptr_info,
-    base::OnceCallback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> success_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(cookie_store_manager_);
 
   cookie_store_manager_->ListenToCookieChanges(
       ::network::mojom::CookieManagerPtr(std::move(cookie_manager_ptr_info)),
-      std::move(callback));
+      std::move(success_callback));
 }
 
 void CookieStoreContext::CreateServiceOnIOThread(
diff --git a/content/browser/cookie_store/cookie_store_context.h b/content/browser/cookie_store/cookie_store_context.h
index 0317b9f0..f2cd5af 100644
--- a/content/browser/cookie_store/cookie_store_context.h
+++ b/content/browser/cookie_store/cookie_store_context.h
@@ -52,13 +52,13 @@
   // errors.
   void Initialize(
       scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
-      base::OnceCallback<void(bool)> callback);
+      base::OnceCallback<void(bool)> success_callback);
 
   // Starts listening to cookie changes from a network service instance.
   //
   // The callback is called with the (success / failure) result of subscribing.
   void ListenToCookieChanges(::network::mojom::NetworkContext* network_context,
-                             base::OnceCallback<void(bool)> callback);
+                             base::OnceCallback<void(bool)> success_callback);
 
   // Routes a mojo request to the CookieStoreManager on the IO thread.
   void CreateService(blink::mojom::CookieStoreRequest request,
@@ -71,11 +71,11 @@
 
   void InitializeOnIOThread(
       scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
-      base::OnceCallback<void(bool)> callback);
+      base::OnceCallback<void(bool)> success_callback);
 
   void ListenToCookieChangesOnIOThread(
       ::network::mojom::CookieManagerPtrInfo cookie_manager_ptr_info,
-      base::OnceCallback<void(bool)> callback);
+      base::OnceCallback<void(bool)> success_callback);
 
   void CreateServiceOnIOThread(blink::mojom::CookieStoreRequest request,
                                const url::Origin& origin);
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index f51eba7..d82370f5 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -729,7 +729,7 @@
   if (elements->empty())
     return false;
   for (const auto& element : *elements) {
-    if (element.type() != network::DataElement::TYPE_BYTES)
+    if (element.type() != network::mojom::DataElementType::kBytes)
       return false;
     result->append(element.bytes(), element.length());
   }
diff --git a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
index c701b61..ebf479c 100644
--- a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/services/leveldb/public/cpp/util.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/dom_storage/session_storage_database.h"
 #include "content/browser/dom_storage/test/fake_leveldb_database_error_on_write.h"
 #include "content/browser/dom_storage/test/fake_leveldb_service.h"
@@ -62,9 +63,13 @@
     features_.InitAndEnableFeature(blink::features::kOnionSoupDOMStorage);
     mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
         &SessionStorageContextMojoTest::OnBadMessage, base::Unretained(this)));
+
+    ChildProcessSecurityPolicyImpl::GetInstance()->Add(kTestProcessId);
   }
 
   void TearDown() override {
+    ChildProcessSecurityPolicyImpl::GetInstance()->Remove(kTestProcessId);
+
     mojo::core::SetDefaultProcessErrorCallback(
         mojo::core::ProcessErrorCallback());
   }
diff --git a/content/browser/dom_storage/test/mojo_test_with_file_service.h b/content/browser/dom_storage/test/mojo_test_with_file_service.h
index 77d6eae..8ae2bbb 100644
--- a/content/browser/dom_storage/test/mojo_test_with_file_service.h
+++ b/content/browser/dom_storage/test/mojo_test_with_file_service.h
@@ -10,7 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
-#include "base/test/scoped_task_environment.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "services/file/file_service.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,10 +34,10 @@
     return test_connector_factory_.GetDefaultConnector();
   }
 
-  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+  void RunUntilIdle() { thread_bundle_.RunUntilIdle(); }
 
  private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestBrowserThreadBundle thread_bundle_;
   service_manager::TestConnectorFactory test_connector_factory_;
   file::FileService file_service_;
   base::ScopedTempDir temp_path_;
diff --git a/content/browser/fileapi/browser_file_system_helper_unittest.cc b/content/browser/fileapi/browser_file_system_helper_unittest.cc
index 1b0cff4..67e528b5 100644
--- a/content/browser/fileapi/browser_file_system_helper_unittest.cc
+++ b/content/browser/fileapi/browser_file_system_helper_unittest.cc
@@ -13,6 +13,7 @@
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/fileapi/browser_file_system_helper.h"
 #include "content/public/common/drop_data.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/filename_util.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "storage/browser/fileapi/file_system_options.h"
@@ -33,6 +34,7 @@
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
+  TestBrowserThreadBundle thread_bundle;
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
   p->Add(kRendererID);
@@ -136,6 +138,7 @@
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
+  TestBrowserThreadBundle thread_bundle;
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
   p->Add(kRendererID);
diff --git a/content/browser/fileapi/file_system_manager_impl.cc b/content/browser/fileapi/file_system_manager_impl.cc
index 45fbd64..d525f15 100644
--- a/content/browser/fileapi/file_system_manager_impl.cc
+++ b/content/browser/fileapi/file_system_manager_impl.cc
@@ -574,6 +574,11 @@
                                          CreateWriterCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
+    bindings_.ReportBadMessage("FileSystemManager.CreateWriter");
+    return;
+  }
+
   FileSystemURL url(context_->CrackURL(file_path));
   base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
   if (opt_error) {
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 592ed75..00165111 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -102,6 +102,9 @@
   const GURL& GetPreviousURL() override;
   net::HostPortPair GetSocketAddress() override;
   const net::HttpRequestHeaders& GetRequestHeaders() override;
+  void RemoveRequestHeader(const std::string& header_name) override;
+  void SetRequestHeader(const std::string& header_name,
+                        const std::string& header_value) override;
   const net::HttpResponseHeaders* GetResponseHeaders() override;
   net::HttpResponseInfo::ConnectionInfo GetConnectionInfo() override;
   const net::SSLInfo& GetSSLInfo() override;
@@ -348,17 +351,6 @@
   // url we're navigating to.
   void SetExpectedProcess(RenderProcessHost* expected_process);
 
-  // Remove a request's header. If the header is not present, it has no effect.
-  // Must be called during a redirect.
-  void RemoveRequestHeader(const std::string& header_name);
-
-  // Set a request's header. If the header is already present, its value is
-  // overwritten. When modified during a navigation start, the headers will be
-  // applied to the initial network request. When modified during a redirect,
-  // the headers will be applied to the redirected request.
-  void SetRequestHeader(const std::string& header_name,
-                        const std::string& header_value);
-
   NavigationThrottle* GetDeferringThrottleForTesting() const {
     return GetDeferringThrottle();
   }
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index a76dcf7c..2f69e44 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1864,17 +1864,22 @@
   if (delegate_->DidAddMessageToConsole(level, message, line_no, source_id))
     return;
 
-  // Pass through log level only on WebUI pages to limit console spew.
-  const bool is_web_ui =
-      HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL());
-  const int32_t resolved_level = is_web_ui ? level : ::logging::LOG_INFO;
+  // Pass through log level only on builtin components pages to limit console
+  // spew.
+  const bool is_builtin_component =
+      HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL()) ||
+      GetContentClient()->browser()->IsBuiltinComponent(
+          GetProcess()->GetBrowserContext(), GetLastCommittedOrigin());
+  const int32_t resolved_level =
+      is_builtin_component ? level : ::logging::LOG_INFO;
 
   // LogMessages can be persisted so this shouldn't be logged in incognito mode.
-  // This rule is not applied to WebUI pages, because source code of WebUI is a
-  // part of Chrome source code, and we want to treat messages from WebUI the
-  // same way as we treat log messages from native code.
+  // This rule is not applied to WebUI pages or other builtin components,
+  // because WebUI and builtin components source code is a part of Chrome source
+  // code, and we want to treat messages from WebUI and other builtin components
+  // the same way as we treat log messages from native code.
   if (::logging::GetMinLogLevel() <= resolved_level &&
-      (is_web_ui ||
+      (is_builtin_component ||
        !GetSiteInstance()->GetBrowserContext()->IsOffTheRecord())) {
     logging::LogMessage("CONSOLE", line_no, resolved_level).stream()
         << "\"" << message << "\", source: " << source_id << " (" << line_no
@@ -4616,6 +4621,17 @@
       // If the caller has supplied a factory for AppCache, use it.
       subresource_loader_factories->appcache_factory_info() =
           std::move(subresource_loader_params->appcache_loader_factory_info);
+
+      // Inject test intermediary if needed.
+      if (!g_create_network_factory_callback_for_test.Get().is_null()) {
+        network::mojom::URLLoaderFactoryPtrInfo original_factory =
+            std::move(subresource_loader_factories->appcache_factory_info());
+        network::mojom::URLLoaderFactoryRequest new_request = mojo::MakeRequest(
+            &subresource_loader_factories->appcache_factory_info());
+        g_create_network_factory_callback_for_test.Get().Run(
+            std::move(new_request), GetProcess()->GetID(),
+            std::move(original_factory));
+      }
     }
 
     // Set up the default factory.
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index f558948d..6488ff4 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -190,17 +190,15 @@
 void RenderWidgetHostViewGuest::PreProcessMouseEvent(
     const blink::WebMouseEvent& event) {
   if (event.GetType() == blink::WebInputEvent::kMouseDown) {
-    DCHECK(guest_->GetOwnerRenderWidgetHostView());
-    RenderWidgetHost* embedder =
-        guest_->GetOwnerRenderWidgetHostView()->GetRenderWidgetHost();
-    if (!embedder->GetView()->HasFocus())
-      embedder->GetView()->Focus();
+    RenderWidgetHostViewBase* owner_view = GetOwnerRenderWidgetHostView();
+    if (!owner_view->HasFocus())
+      owner_view->Focus();
 
     // With direct routing, the embedder would not know to focus the guest on
     // click. Sends a synthetic event for the focusing side effect.
     // TODO(wjmaclean): When we remove BrowserPlugin, delete this code.
     // http://crbug.com/533069
-    MaybeSendSyntheticTapGesture(event.PositionInWidget(),
+    MaybeSendSyntheticTapGesture(owner_view, event.PositionInWidget(),
                                  event.PositionInScreen());
   }
 }
@@ -208,17 +206,16 @@
 void RenderWidgetHostViewGuest::PreProcessTouchEvent(
     const blink::WebTouchEvent& event) {
   if (event.GetType() == blink::WebInputEvent::kTouchStart) {
-    DCHECK(guest_->GetOwnerRenderWidgetHostView());
-    RenderWidgetHost* embedder =
-        guest_->GetOwnerRenderWidgetHostView()->GetRenderWidgetHost();
-    if (!embedder->GetView()->HasFocus())
-      embedder->GetView()->Focus();
+    RenderWidgetHostViewBase* owner_view = GetOwnerRenderWidgetHostView();
+    if (!owner_view->HasFocus())
+      owner_view->Focus();
 
     // With direct routing, the embedder would not know to focus the guest on
     // touch. Sends a synthetic event for the focusing side effect.
     // TODO(wjmaclean): When we remove BrowserPlugin, delete this code.
     // http://crbug.com/533069
-    MaybeSendSyntheticTapGesture(event.touches[0].PositionInWidget(),
+    MaybeSendSyntheticTapGesture(owner_view,
+                                 event.touches[0].PositionInWidget(),
                                  event.touches[0].PositionInScreen());
   }
 }
@@ -586,42 +583,43 @@
 void RenderWidgetHostViewGuest::MaybeSendSyntheticTapGestureForTest(
     const blink::WebFloatPoint& position,
     const blink::WebFloatPoint& screen_position) const {
-  MaybeSendSyntheticTapGesture(position, screen_position);
+  MaybeSendSyntheticTapGesture(GetOwnerRenderWidgetHostView(), position,
+                               screen_position);
 }
 
 // TODO(wjmaclean): When we remove BrowserPlugin, delete this code.
 // http://crbug.com/533069
 void RenderWidgetHostViewGuest::MaybeSendSyntheticTapGesture(
+    RenderWidgetHostViewBase* owner_view,
     const blink::WebFloatPoint& position,
     const blink::WebFloatPoint& screen_position) const {
+  DCHECK(owner_view);
   if (!HasFocus()) {
-    // We need to a account for the position of the guest view within the
-    // embedder, as well as the fact that the embedder's host will add its
-    // offset in screen coordinates before sending the event (with the latter
-    // component just serving to confuse the renderer, hence why it should be
-    // removed).
-    gfx::Vector2d offset =
-        GetViewBounds().origin() -
-        GetOwnerRenderWidgetHostView()->GetBoundsInRootWindow().origin();
+    // We need to convert the position of the event into the coordinate frame
+    // of the embedder in order to be sure we hit the BrowserPlugin element.
+    gfx::PointF point_in_owner;
+    if (!owner_view->TransformPointToLocalCoordSpace(
+            position, GetCurrentSurfaceId(), &point_in_owner)) {
+      LOG(ERROR) << "Unable to convert gesture location to owner coordinates.";
+      return;
+    }
     blink::WebGestureEvent gesture_tap_event(
         blink::WebGestureEvent::kGestureTapDown,
         blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow(),
         blink::kWebGestureDeviceTouchscreen);
-    gesture_tap_event.SetPositionInWidget(
-        blink::WebFloatPoint(position.x + offset.x(), position.y + offset.y()));
+    gesture_tap_event.SetPositionInWidget(point_in_owner);
     gesture_tap_event.SetPositionInScreen(screen_position);
     // The touch action may not be set yet because this is still at the
     // Pre-processing stage of a mouse or a touch event. In this case, set the
     // touch action to Auto to prevent crashing.
-    static_cast<RenderWidgetHostImpl*>(
-        GetOwnerRenderWidgetHostView()->GetRenderWidgetHost())
+    static_cast<RenderWidgetHostImpl*>(owner_view->GetRenderWidgetHost())
         ->input_router()
         ->ForceSetTouchActionAuto();
-    GetOwnerRenderWidgetHostView()->ProcessGestureEvent(
+    owner_view->ProcessGestureEvent(
         gesture_tap_event, ui::LatencyInfo(ui::SourceEventType::TOUCH));
 
     gesture_tap_event.SetType(blink::WebGestureEvent::kGestureTapCancel);
-    GetOwnerRenderWidgetHostView()->ProcessGestureEvent(
+    owner_view->ProcessGestureEvent(
         gesture_tap_event, ui::LatencyInfo(ui::SourceEventType::TOUCH));
   }
 }
diff --git a/content/browser/frame_host/render_widget_host_view_guest.h b/content/browser/frame_host/render_widget_host_view_guest.h
index 1221cc9..d8bf59d 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.h
+++ b/content/browser/frame_host/render_widget_host_view_guest.h
@@ -177,6 +177,7 @@
   // TODO(wjmaclean): When we remove BrowserPlugin, delete this code.
   // http://crbug.com/533069
   void MaybeSendSyntheticTapGesture(
+      RenderWidgetHostViewBase* owner_view,
       const blink::WebFloatPoint& position,
       const blink::WebFloatPoint& screen_position) const;
 
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index 6214a9b..f69b3d4 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -564,8 +564,12 @@
   if (connection_->GetTransaction(transaction_id))
     return;
 
-  connection_->database()->CreateTransaction(transaction_id, connection_.get(),
-                                             object_store_ids, mode);
+  IndexedDBTransaction* transaction = connection_->CreateTransaction(
+      transaction_id,
+      std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode,
+      new IndexedDBBackingStore::Transaction(
+          connection_->database()->backing_store()));
+  connection_->database()->RegisterAndScheduleTransaction(transaction);
 }
 
 void DatabaseImpl::IDBSequenceHelper::Close() {
diff --git a/content/browser/indexed_db/indexed_db_class_factory.cc b/content/browser/indexed_db/indexed_db_class_factory.cc
index dfae601..0adcb53 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/indexed_db_class_factory.cc
@@ -35,9 +35,11 @@
     scoped_refptr<IndexedDBBackingStore> backing_store,
     scoped_refptr<IndexedDBFactory> factory,
     std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-    const IndexedDBDatabase::Identifier& unique_identifier) {
+    const IndexedDBDatabase::Identifier& unique_identifier,
+    ScopesLockManager* transaction_lock_manager) {
   return new IndexedDBDatabase(name, backing_store, factory,
-                               std::move(metadata_coding), unique_identifier);
+                               std::move(metadata_coding), unique_identifier,
+                               transaction_lock_manager);
 }
 
 std::unique_ptr<IndexedDBTransaction>
diff --git a/content/browser/indexed_db/indexed_db_class_factory.h b/content/browser/indexed_db/indexed_db_class_factory.h
index 6082a95f..afd9f76d 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.h
+++ b/content/browser/indexed_db/indexed_db_class_factory.h
@@ -14,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/scopes/scopes_lock_manager.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
@@ -35,6 +36,8 @@
 
 // Use this factory to create some IndexedDB objects. Exists solely to
 // facilitate tests which sometimes need to inject mock objects into the system.
+// TODO(dmurph): Remove th8s class in favor of dependency injection. This makes
+// it really hard to iterate on the system.
 class CONTENT_EXPORT IndexedDBClassFactory {
  public:
   typedef IndexedDBClassFactory* GetterCallback();
@@ -48,7 +51,8 @@
       scoped_refptr<IndexedDBBackingStore> backing_store,
       scoped_refptr<IndexedDBFactory> factory,
       std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-      const IndexedDBDatabase::Identifier& unique_identifier);
+      const IndexedDBDatabase::Identifier& unique_identifier,
+      ScopesLockManager* transaction_lock_manager);
 
   virtual std::unique_ptr<IndexedDBTransaction> CreateIndexedDBTransaction(
       int64_t id,
diff --git a/content/browser/indexed_db/indexed_db_connection.h b/content/browser/indexed_db/indexed_db_connection.h
index 66633368..b65fca774 100644
--- a/content/browser/indexed_db/indexed_db_connection.h
+++ b/content/browser/indexed_db/indexed_db_connection.h
@@ -81,6 +81,11 @@
   // TODO(dmurph): Change that so this doesn't need to ignore unknown ids.
   void RemoveTransaction(int64_t id);
 
+  const std::unordered_map<int64_t, std::unique_ptr<IndexedDBTransaction>>&
+  transactions() const {
+    return transactions_;
+  }
+
  private:
   const int32_t id_;
 
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 7cf3cb14c..d70d360 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -165,33 +165,37 @@
     info->Set("paths", std::move(paths));
     info->SetDouble("connection_count", GetConnectionCount(origin));
 
-    // This ends up being O(n^2) since we iterate over all open databases
-    // to extract just those in the origin, and we're iterating over all
-    // origins in the outer loop.
+    // This ends up being O(NlogN), where N = number of open databases. We
+    // iterate over all open databases to extract just those in the origin, and
+    // we're iterating over all origins in the outer loop.
 
-    if (indexeddb_factory_.get()) {
-      std::pair<IndexedDBFactory::OriginDBMapIterator,
-                IndexedDBFactory::OriginDBMapIterator>
-          range = indexeddb_factory_->GetOpenDatabasesForOrigin(origin);
-      // TODO(jsbell): Sort by name?
-      std::unique_ptr<base::ListValue> database_list(
+    if (!indexeddb_factory_.get()) {
+      list->Append(std::move(info));
+      continue;
+    }
+    std::pair<IndexedDBFactory::OriginDBMapIterator,
+              IndexedDBFactory::OriginDBMapIterator>
+        range = indexeddb_factory_->GetOpenDatabasesForOrigin(origin);
+    // TODO(jsbell): Sort by name?
+    std::unique_ptr<base::ListValue> database_list(
+        std::make_unique<base::ListValue>());
+
+    for (auto it = range.first; it != range.second; ++it) {
+      const IndexedDBDatabase* db = it->second;
+      std::unique_ptr<base::DictionaryValue> db_info(
+          std::make_unique<base::DictionaryValue>());
+
+      db_info->SetString("name", db->name());
+      db_info->SetDouble("connection_count", db->ConnectionCount());
+      db_info->SetDouble("active_open_delete", db->ActiveOpenDeleteCount());
+      db_info->SetDouble("pending_open_delete", db->PendingOpenDeleteCount());
+
+      std::unique_ptr<base::ListValue> transaction_list(
           std::make_unique<base::ListValue>());
 
-      for (auto it = range.first; it != range.second; ++it) {
-        const IndexedDBDatabase* db = it->second;
-        std::unique_ptr<base::DictionaryValue> db_info(
-            std::make_unique<base::DictionaryValue>());
-
-        db_info->SetString("name", db->name());
-        db_info->SetDouble("connection_count", db->ConnectionCount());
-        db_info->SetDouble("active_open_delete", db->ActiveOpenDeleteCount());
-        db_info->SetDouble("pending_open_delete", db->PendingOpenDeleteCount());
-
-        std::unique_ptr<base::ListValue> transaction_list(
-            std::make_unique<base::ListValue>());
-        std::vector<const IndexedDBTransaction*> transactions =
-            db->transaction_coordinator().GetTransactions();
-        for (const auto* transaction : transactions) {
+      for (IndexedDBConnection* connection : db->connections()) {
+        for (const auto& transaction_id_pair : connection->transactions()) {
+          const auto* transaction = transaction_id_pair.second.get();
           std::unique_ptr<base::DictionaryValue> transaction_info(
               std::make_unique<base::DictionaryValue>());
 
@@ -252,13 +256,12 @@
           transaction_info->Set("scope", std::move(scope));
           transaction_list->Append(std::move(transaction_info));
         }
-        db_info->Set("transactions", std::move(transaction_list));
-
-        database_list->Append(std::move(db_info));
       }
-      info->Set("databases", std::move(database_list));
-    }
+      db_info->Set("transactions", std::move(transaction_list));
 
+      database_list->Append(std::move(db_info));
+    }
+    info->Set("databases", std::move(database_list));
     list->Append(std::move(info));
   }
   return list.release();
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index daf241ca..c29430fa 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -246,11 +246,14 @@
     DCHECK_EQ(db_->connections_.count(connection_.get()), 1UL);
 
     std::vector<int64_t> object_store_ids;
-    IndexedDBTransaction* transaction = db_->CreateTransaction(
-        pending_->transaction_id, connection_.get(), object_store_ids,
-        blink::mojom::IDBTransactionMode::VersionChange);
 
-    DCHECK(db_->transaction_coordinator_.IsRunningVersionChangeTransaction());
+    IndexedDBTransaction* transaction = connection_->CreateTransaction(
+        pending_->transaction_id,
+        std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
+        blink::mojom::IDBTransactionMode::VersionChange,
+        new IndexedDBBackingStore::Transaction(db_->backing_store()));
+    db_->RegisterAndScheduleTransaction(transaction);
+
     transaction->ScheduleTask(
         base::BindOnce(&IndexedDBDatabase::VersionChangeOperation, db_,
                        pending_->version, pending_->callbacks));
@@ -379,11 +382,12 @@
     scoped_refptr<IndexedDBBackingStore> backing_store,
     scoped_refptr<IndexedDBFactory> factory,
     std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-    const Identifier& unique_identifier) {
+    const Identifier& unique_identifier,
+    ScopesLockManager* transaction_lock_manager) {
   scoped_refptr<IndexedDBDatabase> database =
       IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
           name, backing_store, factory, std::move(metadata_coding),
-          unique_identifier);
+          unique_identifier, transaction_lock_manager);
   Status s = database->OpenInternal();
   if (!s.ok())
     database = nullptr;
@@ -395,7 +399,8 @@
     scoped_refptr<IndexedDBBackingStore> backing_store,
     scoped_refptr<IndexedDBFactory> factory,
     std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-    const Identifier& unique_identifier)
+    const Identifier& unique_identifier,
+    ScopesLockManager* transaction_lock_manager)
     : backing_store_(backing_store),
       metadata_(name,
                 kInvalidId,
@@ -403,7 +408,8 @@
                 kInvalidId),
       identifier_(unique_identifier),
       factory_(factory),
-      metadata_coding_(std::move(metadata_coding)) {
+      metadata_coding_(std::move(metadata_coding)),
+      lock_manager_(transaction_lock_manager) {
   DCHECK(factory != nullptr);
 }
 
@@ -1816,29 +1822,34 @@
   } while (!active_request_ && !pending_requests_.empty());
 }
 
-IndexedDBTransaction* IndexedDBDatabase::CreateTransaction(
-    int64_t transaction_id,
-    IndexedDBConnection* connection,
-    const std::vector<int64_t>& object_store_ids,
-    blink::mojom::IDBTransactionMode mode) {
-  IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
-  DCHECK(connections_.count(connection));
+void IndexedDBDatabase::RegisterAndScheduleTransaction(
+    IndexedDBTransaction* transaction) {
+  IDB_TRACE1("IndexedDBDatabase::RegisterAndScheduleTransaction", "txn.id",
+             transaction->id());
 
   UMA_HISTOGRAM_COUNTS_1000(
       "WebCore.IndexedDB.Database.OutstandingTransactionCount",
       transaction_count_);
-
-  IndexedDBTransaction* transaction = connection->CreateTransaction(
-      transaction_id,
-      std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode,
-      new IndexedDBBackingStore::Transaction(backing_store_.get()));
-  TransactionCreated(transaction);
-  return transaction;
-}
-
-void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
   transaction_count_++;
-  transaction_coordinator_.DidCreateTransaction(transaction);
+  std::vector<ScopesLockManager::ScopeLockRequest> lock_requests;
+  lock_requests.reserve(1 + transaction->scope().size());
+  lock_requests.emplace_back(
+      kDatabaseRangeLockLevel, GetDatabaseLockRange(id()),
+      transaction->mode() == blink::mojom::IDBTransactionMode::VersionChange
+          ? ScopesLockManager::LockType::kExclusive
+          : ScopesLockManager::LockType::kShared);
+  ScopesLockManager::LockType lock_type =
+      transaction->mode() == blink::mojom::IDBTransactionMode::ReadOnly
+          ? ScopesLockManager::LockType::kShared
+          : ScopesLockManager::LockType::kExclusive;
+  for (int64_t object_store : transaction->scope()) {
+    lock_requests.emplace_back(kObjectStoreRangeLockLevel,
+                               GetObjectStoreLockRange(id(), object_store),
+                               lock_type);
+  }
+  lock_manager_->AcquireLocks(
+      std::move(lock_requests),
+      base::BindOnce(&IndexedDBTransaction::Start, transaction->AsWeakPtr()));
 }
 
 void IndexedDBDatabase::OpenConnection(
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index fe252040..8f0de49 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -25,8 +25,8 @@
 #include "content/browser/indexed_db/indexed_db_callbacks.h"
 #include "content/browser/indexed_db/indexed_db_observer.h"
 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
-#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
 #include "content/browser/indexed_db/list_set.h"
+#include "content/browser/indexed_db/scopes/scopes_lock_manager.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
@@ -67,7 +67,8 @@
       scoped_refptr<IndexedDBBackingStore> backing_store,
       scoped_refptr<IndexedDBFactory> factory,
       std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-      const Identifier& unique_identifier);
+      const Identifier& unique_identifier,
+      ScopesLockManager* lock_manager);
 
   const Identifier& identifier() const { return identifier_; }
   IndexedDBBackingStore* backing_store() { return backing_store_.get(); }
@@ -103,12 +104,8 @@
                          const base::string16& new_name);
 
   // Returns a pointer to a newly created transaction. The object is owned
-  // by |transaction_coordinator_|.
-  IndexedDBTransaction* CreateTransaction(
-      int64_t transaction_id,
-      IndexedDBConnection* connection,
-      const std::vector<int64_t>& object_store_ids,
-      blink::mojom::IDBTransactionMode mode);
+  // by the connection.
+  void RegisterAndScheduleTransaction(IndexedDBTransaction* transaction);
   void Close(IndexedDBConnection* connection, bool forced);
   void ForceClose();
 
@@ -134,14 +131,11 @@
                    int64_t index_id,
                    const base::string16& new_name);
 
-  IndexedDBTransactionCoordinator& transaction_coordinator() {
-    return transaction_coordinator_;
-  }
-  const IndexedDBTransactionCoordinator& transaction_coordinator() const {
-    return transaction_coordinator_;
+  ScopesLockManager* transaction_lock_manager() { return lock_manager_; }
+  const ScopesLockManager* transaction_lock_manager() const {
+    return lock_manager_;
   }
 
-  void TransactionCreated(IndexedDBTransaction* transaction);
   void TransactionFinished(IndexedDBTransaction* transaction, bool committed);
 
   void AbortAllTransactionsForConnections();
@@ -286,6 +280,10 @@
 
   IndexedDBFactory* factory() const { return factory_.get(); }
 
+  const list_set<IndexedDBConnection*>& connections() const {
+    return connections_;
+  }
+
  protected:
   friend class IndexedDBTransaction;
 
@@ -293,7 +291,8 @@
                     scoped_refptr<IndexedDBBackingStore> backing_store,
                     scoped_refptr<IndexedDBFactory> factory,
                     std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-                    const Identifier& unique_identifier);
+                    const Identifier& unique_identifier,
+                    ScopesLockManager* transaction_lock_manager);
   virtual ~IndexedDBDatabase();
 
   // May be overridden in tests.
@@ -344,7 +343,7 @@
   scoped_refptr<IndexedDBFactory> factory_;
   std::unique_ptr<IndexedDBMetadataCoding> metadata_coding_;
 
-  IndexedDBTransactionCoordinator transaction_coordinator_;
+  ScopesLockManager* lock_manager_;
   int64_t transaction_count_ = 0;
 
   list_set<IndexedDBConnection*> connections_;
diff --git a/content/browser/indexed_db/indexed_db_database_unittest.cc b/content/browser/indexed_db/indexed_db_database_unittest.cc
index acbd582..37a644b 100644
--- a/content/browser/indexed_db/indexed_db_database_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -23,11 +23,13 @@
 #include "content/browser/indexed_db/indexed_db_connection.h"
 #include "content/browser/indexed_db/indexed_db_cursor.h"
 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
+#include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,6 +47,8 @@
 
 class IndexedDBDatabaseTest : public ::testing::Test {
  public:
+  IndexedDBDatabaseTest() : lock_manager_(kIndexedDBLockLevelCount) {}
+
   void SetUp() override {
     backing_store_ = new IndexedDBFakeBackingStore();
     factory_ = new MockIndexedDBFactory();
@@ -56,7 +60,8 @@
 
     std::tie(db_, s) = IndexedDBDatabase::Create(
         ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
-        std::move(metadata_coding), IndexedDBDatabase::Identifier());
+        std::move(metadata_coding), IndexedDBDatabase::Identifier(),
+        &lock_manager_);
     ASSERT_TRUE(s.ok());
     EXPECT_FALSE(backing_store_->HasOneRef());  // local and db
   }
@@ -69,6 +74,7 @@
 
  private:
   TestBrowserThreadBundle thread_bundle_;
+  DisjointRangeLockManager lock_manager_;
 };
 
 TEST_F(IndexedDBDatabaseTest, BackingStoreRetention) {
@@ -130,8 +136,11 @@
 
   const int64_t transaction_id = 123;
   const std::vector<int64_t> scope;
-  db_->CreateTransaction(transaction_id, request->connection(), scope,
-                         blink::mojom::IDBTransactionMode::ReadOnly);
+  IndexedDBTransaction* transaction = request->connection()->CreateTransaction(
+      transaction_id, std::set<int64_t>(scope.begin(), scope.end()),
+      blink::mojom::IDBTransactionMode::ReadOnly,
+      new IndexedDBBackingStore::Transaction(backing_store_.get()));
+  db_->RegisterAndScheduleTransaction(transaction);
 
   request->connection()->ForceClose();
 
@@ -312,7 +321,8 @@
 class IndexedDBDatabaseOperationTest : public testing::Test {
  public:
   IndexedDBDatabaseOperationTest()
-      : commit_success_(leveldb::Status::OK()),
+      : lock_manager_(kIndexedDBLockLevelCount),
+        commit_success_(leveldb::Status::OK()),
         factory_(new MockIndexedDBFactory()) {}
 
   void SetUp() override {
@@ -323,7 +333,8 @@
     leveldb::Status s;
     std::tie(db_, s) = IndexedDBDatabase::Create(
         ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
-        std::move(metadata_coding), IndexedDBDatabase::Identifier());
+        std::move(metadata_coding), IndexedDBDatabase::Identifier(),
+        &lock_manager_);
     ASSERT_TRUE(s.ok());
 
     request_ = new MockIndexedDBCallbacks();
@@ -342,7 +353,7 @@
         transaction_id, std::set<int64_t>() /*scope*/,
         blink::mojom::IDBTransactionMode::VersionChange,
         new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
-    db_->TransactionCreated(transaction_);
+    db_->RegisterAndScheduleTransaction(transaction_);
 
     // Add a dummy task which takes the place of the VersionChangeOperation
     // which kicks off the upgrade. This ensures that the transaction has
@@ -364,6 +375,7 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
   IndexedDBTransaction* transaction_;
   std::unique_ptr<IndexedDBConnection> connection_;
+  DisjointRangeLockManager lock_manager_;
 
   leveldb::Status commit_success_;
 
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index 8d8baba..29ea6f37 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -27,7 +27,6 @@
 #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h"
 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
@@ -125,6 +124,7 @@
     base::Clock* clock)
     : context_(context),
       leveldb_factory_(leveldb_factory),
+      lock_manager_(kIndexedDBLockLevelCount),
       clock_(clock),
       earliest_sweep_(GenerateNextGlobalSweepTime(clock_->Now())) {}
 
@@ -548,9 +548,10 @@
   }
 
   scoped_refptr<IndexedDBDatabase> database;
-  std::tie(database, s) = IndexedDBDatabase::Create(
-      name, backing_store.get(), this,
-      std::make_unique<IndexedDBMetadataCoding>(), unique_identifier);
+  std::tie(database, s) =
+      IndexedDBDatabase::Create(name, backing_store.get(), this,
+                                std::make_unique<IndexedDBMetadataCoding>(),
+                                unique_identifier, &lock_manager_);
   if (!database.get()) {
     IndexedDBDatabaseError error(
         blink::kWebIDBDatabaseExceptionUnknownError,
@@ -785,9 +786,10 @@
   }
 
   scoped_refptr<IndexedDBDatabase> database;
-  std::tie(database, s) = IndexedDBDatabase::Create(
-      name, backing_store.get(), this,
-      std::make_unique<IndexedDBMetadataCoding>(), unique_identifier);
+  std::tie(database, s) =
+      IndexedDBDatabase::Create(name, backing_store.get(), this,
+                                std::make_unique<IndexedDBMetadataCoding>(),
+                                unique_identifier, &lock_manager_);
   if (!database.get()) {
     DLOG(ERROR) << "Unable to create the database";
     IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
index 7ec91479..5ff8710 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.h
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "content/browser/indexed_db/indexed_db_factory.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 
 namespace base {
 struct Feature;
@@ -193,6 +194,7 @@
       backing_stores_with_active_blobs_;
   std::set<url::Origin> backends_opened_since_startup_;
 
+  DisjointRangeLockManager lock_manager_;
   base::Clock* clock_;
   base::Time earliest_sweep_;
 
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
index bf1b0c9..43c5c26 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_coding.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
@@ -1004,6 +1004,27 @@
   return Compare(a, b, true /*index_keys*/);
 }
 
+ScopeLockRange GetDatabaseLockRange(int64_t database_id) {
+  uint64_t first[1] = {
+      base::ByteSwapToLE64(static_cast<uint64_t>(database_id))};
+  uint64_t next[1] = {
+      base::ByteSwapToLE64(static_cast<uint64_t>(database_id + 1))};
+  return {std::string(reinterpret_cast<char*>(&first), sizeof(first)),
+          std::string(reinterpret_cast<char*>(&next), sizeof(next))};
+}
+
+ScopeLockRange GetObjectStoreLockRange(int64_t database_id,
+                                       int64_t object_store_id) {
+  uint64_t first[2] = {
+      base::ByteSwapToLE64(static_cast<uint64_t>(database_id)),
+      base::ByteSwapToLE64(static_cast<uint64_t>(object_store_id))};
+  uint64_t next[2] = {
+      base::ByteSwapToLE64(static_cast<uint64_t>(database_id)),
+      base::ByteSwapToLE64(static_cast<uint64_t>(object_store_id + 1))};
+  return {std::string(reinterpret_cast<char*>(&first), sizeof(first)),
+          std::string(reinterpret_cast<char*>(&next), sizeof(next))};
+}
+
 KeyPrefix::KeyPrefix()
     : database_id_(INVALID_TYPE),
       object_store_id_(INVALID_TYPE),
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.h b/content/browser/indexed_db/indexed_db_leveldb_coding.h
index fdc4698..afa5ce76 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_coding.h
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.h
@@ -18,6 +18,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
+#include "content/browser/indexed_db/scopes/scope_lock_range.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
@@ -107,6 +108,17 @@
 CONTENT_EXPORT int CompareIndexKeys(const base::StringPiece& a,
                                     const base::StringPiece& b);
 
+const constexpr int kDatabaseRangeLockLevel = 0;
+const constexpr int kObjectStoreRangeLockLevel = 1;
+const constexpr int kIndexedDBLockLevelCount = 2;
+
+ScopeLockRange GetDatabaseLockRange(int64_t database_id);
+ScopeLockRange GetObjectStoreLockRange(int64_t database_id,
+                                       int64_t object_store_id);
+
+// TODO(dmurph): Modify all decoding methods to return something more sensible,
+// as it is not obvious that they modify the input slice to remove the decoded
+// bit. https://crbug.com/922225
 class KeyPrefix {
  public:
   // These are serialized to disk; any new items must be appended, and none can
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 5c44332..c9aa87e4 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -19,7 +19,6 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
@@ -232,10 +231,7 @@
   // Transactions must also be marked as completed before the
   // front-end is notified, as the transaction completion unblocks
   // operations like closing connections.
-  database_->transaction_coordinator().DidFinishTransaction(this);
-#ifndef NDEBUG
-  DCHECK(!database_->transaction_coordinator().IsActive(this));
-#endif
+  locks_.clear();
 
   if (callbacks_.get())
     callbacks_->OnAbort(*this, error);
@@ -265,10 +261,11 @@
   open_cursors_.erase(cursor);
 }
 
-void IndexedDBTransaction::Start() {
+void IndexedDBTransaction::Start(std::vector<ScopeLock> locks) {
   // TransactionCoordinator has started this transaction.
   DCHECK_EQ(CREATED, state_);
   state_ = STARTED;
+  locks_ = std::move(locks);
   diagnostics_.start_time = base::Time::Now();
 
   if (!used_) {
@@ -432,7 +429,7 @@
   // Transactions must also be marked as completed before the
   // front-end is notified, as the transaction completion unblocks
   // operations like closing connections.
-  database_->transaction_coordinator().DidFinishTransaction(this);
+  locks_.clear();
 
   if (committed) {
     abort_task_stack_.clear();
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
index 469cbb6..f13e1a0 100644
--- a/content/browser/indexed_db/indexed_db_transaction.h
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -23,6 +23,7 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_observer.h"
+#include "content/browser/indexed_db/scopes/scope_lock.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 
@@ -71,7 +72,7 @@
   void Abort(const IndexedDBDatabaseError& error);
 
   // Called by the transaction coordinator when this transaction is unblocked.
-  void Start();
+  void Start(std::vector<ScopeLock> locks);
 
   blink::mojom::IDBTransactionMode mode() const { return mode_; }
   const std::set<int64_t>& scope() const { return object_store_ids_; }
@@ -126,6 +127,10 @@
   void set_size(int64_t size) { size_ = size; }
   int64_t size() const { return size_; }
 
+  base::WeakPtr<IndexedDBTransaction> AsWeakPtr() {
+    return ptr_factory_.GetWeakPtr();
+  }
+
  protected:
   // Test classes may derive, but most creation should be done via
   // IndexedDBClassFactory.
@@ -188,6 +193,7 @@
 
   bool used_ = false;
   State state_ = CREATED;
+  std::vector<ScopeLock> locks_;
   bool is_commit_pending_ = false;
   // We are owned by the connection object, but during force closes sometimes
   // there are issues if there is a pending OpenRequest. So use a WeakPtr.
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
deleted file mode 100644
index 735bd6e..0000000
--- a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
-
-#include "base/logging.h"
-#include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/indexed_db_transaction.h"
-#include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
-
-namespace content {
-namespace {
-
-// Only this many transactions can be active at any time before they are queued.
-// Limited to prevent transaction trashing which can consume a ton of RAM. Ten
-// is chosen to reduce performance regressions.
-// TODO(dmurph): crbug.com/693260 Create better scheduling or limits.
-static const size_t kMaxStartedTransactions = 10;
-
-}  // namespace
-
-IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {}
-
-IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() {
-  DCHECK(queued_transactions_.empty());
-  DCHECK(started_transactions_.empty());
-}
-
-void IndexedDBTransactionCoordinator::DidCreateTransaction(
-    IndexedDBTransaction* transaction) {
-  DCHECK(!queued_transactions_.count(transaction));
-  DCHECK(!started_transactions_.count(transaction));
-  DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state());
-
-  queued_transactions_.insert(transaction);
-  ProcessQueuedTransactions();
-}
-
-void IndexedDBTransactionCoordinator::DidFinishTransaction(
-    IndexedDBTransaction* transaction) {
-  if (queued_transactions_.count(transaction)) {
-    DCHECK(!started_transactions_.count(transaction));
-    queued_transactions_.erase(transaction);
-  } else {
-    DCHECK(started_transactions_.count(transaction));
-    started_transactions_.erase(transaction);
-  }
-
-  ProcessQueuedTransactions();
-}
-
-bool IndexedDBTransactionCoordinator::IsRunningVersionChangeTransaction()
-    const {
-  return !started_transactions_.empty() &&
-         (*started_transactions_.begin())->mode() ==
-             blink::mojom::IDBTransactionMode::VersionChange;
-}
-
-#ifndef NDEBUG
-// Verifies internal consistency while returning whether anything is found.
-bool IndexedDBTransactionCoordinator::IsActive(
-    IndexedDBTransaction* transaction) {
-  bool found = false;
-  if (queued_transactions_.count(transaction))
-    found = true;
-  if (started_transactions_.count(transaction)) {
-    DCHECK(!found);
-    found = true;
-  }
-  return found;
-}
-#endif
-
-std::vector<const IndexedDBTransaction*>
-IndexedDBTransactionCoordinator::GetTransactions() const {
-  std::vector<const IndexedDBTransaction*> result;
-  result.reserve(started_transactions_.size() + queued_transactions_.size());
-  result.insert(result.end(), started_transactions_.begin(),
-                started_transactions_.end());
-  result.insert(result.end(), queued_transactions_.begin(),
-                queued_transactions_.end());
-  return result;
-}
-
-void IndexedDBTransactionCoordinator::RecordMetrics() const {
-  IDB_TRACE_COUNTER2("IndexedDBTransactionCoordinator", "StartedTransactions",
-                     started_transactions_.size(), "QueuedTransactions",
-                     queued_transactions_.size());
-}
-
-void IndexedDBTransactionCoordinator::ProcessQueuedTransactions() {
-  if (queued_transactions_.empty())
-    return;
-
-  DCHECK(!IsRunningVersionChangeTransaction());
-
-  // The locked_scope set accumulates the ids of object stores in the scope of
-  // running read/write transactions. Other read-write transactions with
-  // stores in this set may not be started. Read-only transactions may start,
-  // taking a snapshot of the database, which does not include uncommitted
-  // data. ("Version change" transactions are exclusive, but handled by the
-  // connection sequencing in IndexedDBDatabase.)
-  std::set<int64_t> locked_scope;
-  for (auto* transaction : started_transactions_) {
-    if (transaction->mode() == blink::mojom::IDBTransactionMode::ReadWrite) {
-      // Started read/write transactions have exclusive access to the object
-      // stores within their scopes.
-      locked_scope.insert(transaction->scope().begin(),
-                          transaction->scope().end());
-    }
-  }
-
-  auto it = queued_transactions_.begin();
-  while (it != queued_transactions_.end()) {
-    IndexedDBTransaction* transaction = *it;
-    ++it;
-    if (CanStartTransaction(transaction, locked_scope)) {
-      DCHECK_EQ(IndexedDBTransaction::CREATED, transaction->state());
-      queued_transactions_.erase(transaction);
-      started_transactions_.insert(transaction);
-      transaction->Start();
-      DCHECK_EQ(IndexedDBTransaction::STARTED, transaction->state());
-    }
-    if (transaction->mode() == blink::mojom::IDBTransactionMode::ReadWrite) {
-      // Either the transaction started, so it has exclusive access to the
-      // stores in its scope, or per the spec the transaction which was
-      // created first must get access first, so the stores are also locked.
-      locked_scope.insert(transaction->scope().begin(),
-                          transaction->scope().end());
-    }
-  }
-  RecordMetrics();
-}
-
-template<typename T>
-static bool DoSetsIntersect(const std::set<T>& set1,
-                            const std::set<T>& set2) {
-  auto it1 = set1.begin();
-  auto it2 = set2.begin();
-  while (it1 != set1.end() && it2 != set2.end()) {
-    if (*it1 < *it2)
-      ++it1;
-    else if (*it2 < *it1)
-      ++it2;
-    else
-      return true;
-  }
-  return false;
-}
-
-bool IndexedDBTransactionCoordinator::CanStartTransaction(
-    IndexedDBTransaction* const transaction,
-    const std::set<int64_t>& locked_scope) const {
-  if (started_transactions_.size() >= kMaxStartedTransactions) {
-    return false;
-  }
-  DCHECK(queued_transactions_.count(transaction));
-  switch (transaction->mode()) {
-    case blink::mojom::IDBTransactionMode::VersionChange:
-      DCHECK_EQ(1u, queued_transactions_.size());
-      DCHECK(started_transactions_.empty());
-      DCHECK(locked_scope.empty());
-      return true;
-
-    case blink::mojom::IDBTransactionMode::ReadOnly:
-    case blink::mojom::IDBTransactionMode::ReadWrite:
-      return !DoSetsIntersect(transaction->scope(), locked_scope);
-  }
-  NOTREACHED();
-  return false;
-}
-
-}  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.h b/content/browser/indexed_db/indexed_db_transaction_coordinator.h
deleted file mode 100644
index 4fcbfacd..0000000
--- a/content/browser/indexed_db/indexed_db_transaction_coordinator.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
-#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <vector>
-
-#include "base/macros.h"
-#include "content/browser/indexed_db/list_set.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-class IndexedDBTransaction;
-
-// Transactions are executed in the order the were created.
-class CONTENT_EXPORT IndexedDBTransactionCoordinator {
- public:
-  IndexedDBTransactionCoordinator();
-  ~IndexedDBTransactionCoordinator();
-
-  // Called by transactions as they start and finish.
-  void DidCreateTransaction(IndexedDBTransaction* transaction);
-  void DidFinishTransaction(IndexedDBTransaction* transaction);
-
-  bool IsRunningVersionChangeTransaction() const;
-
-#ifndef NDEBUG
-  bool IsActive(IndexedDBTransaction* transaction);
-#endif
-
-  // Makes a snapshot of the transaction queue. For diagnostics only.
-  std::vector<const IndexedDBTransaction*> GetTransactions() const;
-
- private:
-  friend class IndexedDBTransactionCoordinatorTest;
-
-  void RecordMetrics() const;
-
-  void ProcessQueuedTransactions();
-  bool CanStartTransaction(IndexedDBTransaction* const transaction,
-                           const std::set<int64_t>& locked_scope) const;
-
-  // Transactions in different states are grouped below.
-  // list_set is used to provide stable ordering; required by spec
-  // for the queue, convenience for diagnostics for the rest.
-  list_set<IndexedDBTransaction*> queued_transactions_;
-  list_set<IndexedDBTransaction*> started_transactions_;
-
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionCoordinator);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
diff --git a/content/browser/indexed_db/indexed_db_transaction_unittest.cc b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
index 2e51dce7..da69069 100644
--- a/content/browser/indexed_db/indexed_db_transaction_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_unittest.cc
@@ -16,10 +16,12 @@
 #include "content/browser/indexed_db/indexed_db_connection.h"
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
 #include "content/browser/indexed_db/indexed_db_observer.h"
 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
+#include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
@@ -44,7 +46,9 @@
 
 class IndexedDBTransactionTest : public testing::Test {
  public:
-  IndexedDBTransactionTest() : factory_(new MockIndexedDBFactory()) {
+  IndexedDBTransactionTest()
+      : factory_(new MockIndexedDBFactory()),
+        lock_manager_(kIndexedDBLockLevelCount) {
     backing_store_ = new IndexedDBFakeBackingStore();
     CreateDB();
   }
@@ -57,7 +61,7 @@
     std::tie(db_, s) = IndexedDBDatabase::Create(
         base::ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
         std::make_unique<FakeIndexedDBMetadataCoding>(),
-        IndexedDBDatabase::Identifier());
+        IndexedDBDatabase::Identifier(), &lock_manager_);
     ASSERT_TRUE(s.ok());
   }
 
@@ -80,6 +84,7 @@
 
  private:
   TestBrowserThreadBundle thread_bundle_;
+  DisjointRangeLockManager lock_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest);
 };
@@ -105,7 +110,7 @@
           id, connection.get(), scope,
           blink::mojom::IDBTransactionMode::ReadWrite,
           new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
@@ -152,7 +157,7 @@
           id, connection.get(), scope,
           blink::mojom::IDBTransactionMode::ReadOnly,
           new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
@@ -195,7 +200,7 @@
   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
 
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -257,7 +262,7 @@
   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
 
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -323,7 +328,7 @@
   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
 
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   EXPECT_FALSE(transaction->HasPendingTasks());
   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
@@ -376,7 +381,7 @@
       std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
           id, connection.get(), scope, GetParam(),
           new IndexedDBFakeBackingStore::FakeTransaction(commit_failure)));
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   AbortObserver observer;
   transaction->ScheduleTask(
@@ -405,7 +410,7 @@
       std::unique_ptr<IndexedDBTransaction>(new IndexedDBTransaction(
           id, connection.get(), scope, GetParam(),
           new IndexedDBFakeBackingStore::FakeTransaction(commit_success)));
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   // No conflicting transactions, so coordinator will start it immediately:
   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
@@ -463,7 +468,7 @@
               blink::mojom::IDBTransactionMode::ReadWrite,
               new IndexedDBFakeBackingStore::FakeTransaction(commit_success))));
   ASSERT_TRUE(transaction);
-  db_->TransactionCreated(transaction.get());
+  db_->RegisterAndScheduleTransaction(transaction.get());
 
   EXPECT_EQ(0UL, transaction->pending_observers_.size());
   EXPECT_EQ(0UL, connection->active_observers().size());
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
index 3cd82f7..d77fe67 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
@@ -54,12 +54,14 @@
       scoped_refptr<IndexedDBBackingStore> backing_store,
       scoped_refptr<IndexedDBFactory> factory,
       std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-      const IndexedDBDatabase::Identifier& unique_identifier)
+      const IndexedDBDatabase::Identifier& unique_identifier,
+      ScopesLockManager* transaction_lock_manager)
       : IndexedDBDatabase(name,
                           backing_store,
                           factory,
                           std::move(metadata_coding),
-                          unique_identifier) {}
+                          unique_identifier,
+                          transaction_lock_manager) {}
 
  protected:
   ~IndexedDBTestDatabase() override {}
@@ -271,10 +273,11 @@
     scoped_refptr<IndexedDBBackingStore> backing_store,
     scoped_refptr<IndexedDBFactory> factory,
     std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-    const IndexedDBDatabase::Identifier& unique_identifier) {
+    const IndexedDBDatabase::Identifier& unique_identifier,
+    ScopesLockManager* transaction_lock_manager) {
   return new IndexedDBTestDatabase(name, backing_store, factory,
                                    std::move(metadata_coding),
-                                   unique_identifier);
+                                   unique_identifier, transaction_lock_manager);
 }
 
 std::unique_ptr<IndexedDBTransaction>
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
index 810e947..031459c 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
@@ -14,6 +14,7 @@
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/scopes/scopes_lock_manager.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 
 namespace content {
@@ -37,6 +38,10 @@
   FAIL_METHOD_SEEK,
 };
 
+// TODO(dmurph): Remove the need for this class. We should be solving these
+// problems with dependency injection, factories, using a failing fake leveldb
+// database, or test-specific settings (like
+// SetUsableMessageSizeInBytesForTesting).
 class MockBrowserTestIndexedDBClassFactory : public IndexedDBClassFactory {
  public:
   MockBrowserTestIndexedDBClassFactory();
@@ -47,7 +52,8 @@
       scoped_refptr<IndexedDBBackingStore> backing_store,
       scoped_refptr<IndexedDBFactory> factory,
       std::unique_ptr<IndexedDBMetadataCoding> metadata_coding,
-      const IndexedDBDatabase::Identifier& unique_identifier) override;
+      const IndexedDBDatabase::Identifier& unique_identifier,
+      ScopesLockManager* transaction_lock_manager) override;
   std::unique_ptr<IndexedDBTransaction> CreateIndexedDBTransaction(
       int64_t id,
       IndexedDBConnection* connection,
diff --git a/content/browser/indexed_db/scopes/scope_lock_range.cc b/content/browser/indexed_db/scopes/scope_lock_range.cc
index 669ea9c..538a437 100644
--- a/content/browser/indexed_db/scopes/scope_lock_range.cc
+++ b/content/browser/indexed_db/scopes/scope_lock_range.cc
@@ -9,9 +9,6 @@
 
 namespace content {
 
-ScopeLockRange::ScopeLockRange(std::string begin, std::string end)
-    : begin(std::move(begin)), end(std::move(end)) {}
-
 std::ostream& operator<<(std::ostream& out, const ScopeLockRange& range) {
   out << "<ScopeLockRange>{begin: 0x";
   out << std::setfill('0');
diff --git a/content/browser/indexed_db/scopes/scope_lock_range.h b/content/browser/indexed_db/scopes/scope_lock_range.h
index 3570320..1e68794 100644
--- a/content/browser/indexed_db/scopes/scope_lock_range.h
+++ b/content/browser/indexed_db/scopes/scope_lock_range.h
@@ -17,7 +17,6 @@
 // The range is [begin, end). Bytewise comparison is used to determine
 // overlapping ranges.
 struct CONTENT_EXPORT ScopeLockRange {
-  ScopeLockRange(std::string begin, std::string end);
   ScopeLockRange() = default;
   ~ScopeLockRange() = default;
   std::string begin;
diff --git a/content/browser/indexed_db/scopes/scopes_lock_manager_unittest.cc b/content/browser/indexed_db/scopes/scopes_lock_manager_unittest.cc
index 2670c79..b4d9e42 100644
--- a/content/browser/indexed_db/scopes/scopes_lock_manager_unittest.cc
+++ b/content/browser/indexed_db/scopes/scopes_lock_manager_unittest.cc
@@ -14,7 +14,7 @@
 namespace {
 
 TEST(ScopesLockManager, TestRangePopulation) {
-  ScopeLockRange range("a", "b");
+  ScopeLockRange range{"a", "b"};
   EXPECT_EQ("a", range.begin);
   EXPECT_EQ("b", range.end);
 }
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 12a4cfa..3e9e078 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -34,6 +34,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -1053,7 +1054,7 @@
     delete this;
   }
 
-  // Allow all methods that aren't explicitly overriden to pass through
+  // Allow all methods that aren't explicitly overridden to pass through
   // unmodified.
   blink::mojom::StoragePartitionService* GetForwardingInterface() override {
     return storage_partition_service_;
@@ -1793,4 +1794,105 @@
   EXPECT_EQ("foo=bar", EvalJs(child, "document.cookie"));
 }
 
+// This class allows intercepting the BroadcastChannelProvider::ConnectToChannel
+// method and changing the |origin| parameter before passing the call to the
+// real implementation of BroadcastChannelProvider.
+class BroadcastChannelProviderInterceptor
+    : public blink::mojom::BroadcastChannelProviderInterceptorForTesting,
+      public RenderProcessHostObserver {
+ public:
+  BroadcastChannelProviderInterceptor(
+      RenderProcessHostImpl* rph,
+      blink::mojom::BroadcastChannelProviderRequest request,
+      const url::Origin& origin_to_inject)
+      : origin_to_inject_(origin_to_inject) {
+    StoragePartitionImpl* storage_partition =
+        static_cast<StoragePartitionImpl*>(rph->GetStoragePartition());
+
+    // Bind the real BroadcastChannelProvider implementation.
+    mojo::BindingId binding_id =
+        storage_partition->GetBroadcastChannelProvider()->Connect(
+            rph->GetID(), std::move(request));
+
+    // Now replace it with this object and keep a pointer to the real
+    // implementation.
+    original_broadcast_channel_provider_ =
+        storage_partition->GetBroadcastChannelProvider()
+            ->bindings_for_testing()
+            .SwapImplForTesting(binding_id, this);
+
+    // Register the |this| as a RenderProcessHostObserver, so it can be
+    // correctly cleaned up when the process exits.
+    rph->AddObserver(this);
+  }
+
+  // Ensure this object is cleaned up when the process goes away, since it
+  // is not owned by anyone else.
+  void RenderProcessExited(RenderProcessHost* host,
+                           const ChildProcessTerminationInfo& info) override {
+    host->RemoveObserver(this);
+    delete this;
+  }
+
+  // Allow all methods that aren't explicitly overridden to pass through
+  // unmodified.
+  blink::mojom::BroadcastChannelProvider* GetForwardingInterface() override {
+    return original_broadcast_channel_provider_;
+  }
+
+  // Override this method to allow changing the origin. It simulates a
+  // renderer process sending incorrect data to the browser process, so
+  // security checks can be tested.
+  void ConnectToChannel(
+      const url::Origin& origin,
+      const std::string& name,
+      blink::mojom::BroadcastChannelClientAssociatedPtrInfo client,
+      blink::mojom::BroadcastChannelClientAssociatedRequest connection)
+      override {
+    GetForwardingInterface()->ConnectToChannel(
+        origin_to_inject_, name, std::move(client), std::move(connection));
+  }
+
+ private:
+  // Keep a pointer to the original implementation of the service, so all
+  // calls can be forwarded to it.
+  blink::mojom::BroadcastChannelProvider* original_broadcast_channel_provider_;
+
+  url::Origin origin_to_inject_;
+
+  DISALLOW_COPY_AND_ASSIGN(BroadcastChannelProviderInterceptor);
+};
+
+void CreateTestBroadcastChannelProvider(
+    const url::Origin& origin_to_inject,
+    RenderProcessHostImpl* rph,
+    blink::mojom::BroadcastChannelProviderRequest request) {
+  // This object will register as RenderProcessHostObserver, so it will
+  // clean itself automatically on process exit.
+  new BroadcastChannelProviderInterceptor(rph, std::move(request),
+                                          origin_to_inject);
+}
+
+// Test verifying that a compromised renderer can't lie about |origin| argument
+// passed in the BroadcastChannelProvider::ConnectToChannel IPC message.
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, BroadcastChannelOriginEnforcement) {
+  auto mismatched_origin = url::Origin::Create(GURL("http://abc.foo.com"));
+  EXPECT_FALSE(IsIsolatedOrigin(mismatched_origin));
+  RenderProcessHostImpl::SetBroadcastChannelProviderRequestHandlerForTesting(
+      base::BindRepeating(&CreateTestBroadcastChannelProvider,
+                          mismatched_origin));
+
+  GURL isolated_url(
+      embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
+  EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
+  EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
+
+  content::RenderProcessHostKillWaiter kill_waiter(
+      shell()->web_contents()->GetMainFrame()->GetProcess());
+  ExecuteScriptAsync(
+      shell()->web_contents()->GetMainFrame(),
+      "window.test_channel = new BroadcastChannel('test_channel');");
+  EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
+}
+
 }  // namespace content
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index f1d0fd1..05e6cd4 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -36,6 +36,7 @@
 #include "content/public/test/url_loader_interceptor.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/test_content_browser_client.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/cross_origin_read_blocking.h"
@@ -106,11 +107,6 @@
     is_restricted_uma_expected = true;
     FetchHistogramsFromChildProcesses();
 
-    // TODO(lukasza): https://crbug.com/910287: Remove the special case below
-    // after ensuring that |request_initiator| coming through AppCache is
-    // trustworthy (today kBrowserProcess will be reported in
-    // NetworkService.URLLoader.RequestInitiatorOriginLockCompatibility UMA when
-    // AppCache is relaying renderer requests through a browser process).
     auto expected_lock_compatibility =
         special_request_initiator_origin_lock_check_for_appcache
             ? network::InitiatorLockCompatibility::kBrowserProcess
@@ -249,6 +245,8 @@
     test_client_ptr_info_ = test_client_.CreateInterfacePtr().PassInterface();
   }
 
+  ~RequestInterceptor() { WaitForCleanUpOnIOThread(); }
+
   // Waits until a request gets intercepted and completed.
   void WaitForRequestCompletion() {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -273,13 +271,7 @@
     }
 
     // Wait until IO cleanup completes.
-    base::RunLoop run_loop;
-    base::PostTaskWithTraitsAndReply(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&RequestInterceptor::CleanUpOnIOThread,
-                       base::Unretained(this)),
-        run_loop.QuitClosure());
-    run_loop.Run();
+    WaitForCleanUpOnIOThread();
 
     // Mark the request as completed (for DCHECK purposes).
     request_completed_ = true;
@@ -324,6 +316,10 @@
     }
   }
 
+  void InjectRequestInitiator(const url::Origin& request_initiator) {
+    request_initiator_to_inject_ = request_initiator;
+  }
+
  private:
   bool InterceptorCallback(URLLoaderInterceptor::RequestParams* params) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -337,6 +333,10 @@
       return false;
     request_intercepted_ = true;
 
+    // Modify |params| if requested.
+    if (request_initiator_to_inject_.has_value())
+      params->url_request.request_initiator = request_initiator_to_inject_;
+
     // Inject |test_client_| into the request.
     DCHECK(!original_client_);
     original_client_ = std::move(params->client);
@@ -349,6 +349,23 @@
     return false;
   }
 
+  void WaitForCleanUpOnIOThread() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+    if (io_cleanup_done_)
+      return;
+
+    base::RunLoop run_loop;
+    base::PostTaskWithTraitsAndReply(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&RequestInterceptor::CleanUpOnIOThread,
+                       base::Unretained(this)),
+        run_loop.QuitClosure());
+    run_loop.Run();
+
+    io_cleanup_done_ = true;
+  }
+
   void CleanUpOnIOThread() {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -366,6 +383,8 @@
   const GURL url_to_intercept_;
   URLLoaderInterceptor interceptor_;
 
+  base::Optional<url::Origin> request_initiator_to_inject_;
+
   // |test_client_ptr_info_| below is used to transition results of
   // |test_client_.CreateInterfacePtr()| into IO thread.
   network::mojom::URLLoaderClientPtrInfo test_client_ptr_info_;
@@ -374,6 +393,7 @@
   network::TestURLLoaderClient test_client_;
   std::string body_;
   bool request_completed_ = false;
+  bool io_cleanup_done_ = false;
 
   // IO thread state:
   network::mojom::URLLoaderClientPtr original_client_;
@@ -753,6 +773,59 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingTest,
+                       AppCache_InitiatorEnforcement) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  // Verification of |request_initiator| is only done in the NetworkService code
+  // path.
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
+  // Prepare to intercept the network request at the IPC layer.
+  // in a way, that injects |spoofed_initiator| (simulating a compromised
+  // renderer that pretends to be making the request on behalf of another
+  // origin).
+  //
+  // Note that RequestInterceptor has to be constructed before the
+  // RenderFrameHostImpl is created.
+  GURL cross_site_url("http://cross-origin.com/site_isolation/nosniff.json");
+  RequestInterceptor interceptor(cross_site_url);
+  url::Origin spoofed_initiator =
+      url::Origin::Create(GURL("https://victim.example.com"));
+  interceptor.InjectRequestInitiator(spoofed_initiator);
+
+  // Load the main page twice. The second navigation should have AppCache
+  // initialized for the page.
+  GURL main_url = embedded_test_server()->GetURL(
+      "/appcache/simple_page_with_manifest.html");
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  base::string16 expected_title = base::ASCIIToUTF16("AppCache updated");
+  content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Trigger an AppCache request with an incorrect |request_initiator| and
+  // verify that this will terminate the renderer process.
+  //
+  // Note that during the test, no renderer processes will be actually
+  // terminated, because the malicious/invalid message originates from within
+  // the test process (i.e. from URLLoaderInterceptor::Interceptor's
+  // CreateLoaderAndStart method which forwards the
+  // InjectRequestInitiator-modified request into
+  // AppCacheSubresourceURLFactory).  This necessitates testing via
+  // mojo::test::BadMessageObserver rather than via RenderProcessHostWatcher or
+  // RenderProcessHostKillWaiter.
+  mojo::test::BadMessageObserver bad_message_observer;
+  const char kScriptTemplate[] = R"(
+      var img = document.createElement('img');
+      img.src = $1;
+      document.body.appendChild(img); )";
+  EXPECT_TRUE(ExecJs(shell(), JsReplace(kScriptTemplate, cross_site_url)));
+  EXPECT_EQ("APP_CACHE_SUBRESOURCE_URL_FACTORY_INVALID_INITIATOR",
+            bad_message_observer.WaitForBadMessage());
+}
+
 IN_PROC_BROWSER_TEST_P(CrossSiteDocumentBlockingTest, PrefetchIsNotImpacted) {
   // Prepare for intercepting the resource request for testing prefetching.
   const char* kPrefetchResourcePath = "/prefetch-test";
diff --git a/content/browser/loader/prefetch_url_loader.cc b/content/browser/loader/prefetch_url_loader.cc
index eec76c9..fc6fbc99f 100644
--- a/content/browser/loader/prefetch_url_loader.cc
+++ b/content/browser/loader/prefetch_url_loader.cc
@@ -81,7 +81,7 @@
     const std::vector<std::string>& removed_headers,
     const net::HttpRequestHeaders& modified_headers,
     const base::Optional<GURL>& new_url) {
-  DCHECK(removed_headers.empty() && modified_headers.IsEmpty())
+  DCHECK(modified_headers.IsEmpty())
       << "Redirect with modified headers was not supported yet. "
          "crbug.com/845683";
   DCHECK(!new_url) << "Redirect with modified URL was not "
@@ -111,8 +111,8 @@
     }
   }
 
-  loader_->FollowRedirect(std::vector<std::string>() /* removed_headers */,
-                          modified_request_headers_for_accept, base::nullopt);
+  loader_->FollowRedirect(removed_headers, modified_request_headers_for_accept,
+                          base::nullopt);
 }
 
 void PrefetchURLLoader::ProceedWithResponse() {
diff --git a/content/browser/loader/upload_data_stream_builder.cc b/content/browser/loader/upload_data_stream_builder.cc
index fed54ae..01076623 100644
--- a/content/browser/loader/upload_data_stream_builder.cc
+++ b/content/browser/loader/upload_data_stream_builder.cc
@@ -42,7 +42,7 @@
                      const network::DataElement& element)
       : net::UploadBytesElementReader(element.bytes(), element.length()),
         resource_request_body_(resource_request_body) {
-    DCHECK_EQ(network::DataElement::TYPE_BYTES, element.type());
+    DCHECK_EQ(network::mojom::DataElementType::kBytes, element.type());
   }
 
   ~BytesElementReader() override {}
@@ -68,7 +68,7 @@
                                      element.length(),
                                      element.expected_modification_time()),
         resource_request_body_(resource_request_body) {
-    DCHECK_EQ(network::DataElement::TYPE_FILE, element.type());
+    DCHECK_EQ(network::mojom::DataElementType::kFile, element.type());
   }
 
   ~FileElementReader() override {}
@@ -89,15 +89,15 @@
   std::vector<std::unique_ptr<net::UploadElementReader>> element_readers;
   for (const auto& element : *body->elements()) {
     switch (element.type()) {
-      case network::DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         element_readers.push_back(
             std::make_unique<BytesElementReader>(body, element));
         break;
-      case network::DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         element_readers.push_back(std::make_unique<FileElementReader>(
             body, file_task_runner, element));
         break;
-      case network::DataElement::TYPE_BLOB: {
+      case network::mojom::DataElementType::kBlob: {
         DCHECK_EQ(0ul, element.offset());
         std::unique_ptr<storage::BlobDataHandle> handle =
             blob_context->GetBlobDataFromUUID(element.blob_uuid());
@@ -108,10 +108,10 @@
                 std::move(handle)));
         break;
       }
-      case network::DataElement::TYPE_RAW_FILE:
-      case network::DataElement::TYPE_DATA_PIPE:
-      case network::DataElement::TYPE_CHUNKED_DATA_PIPE:
-      case network::DataElement::TYPE_UNKNOWN:
+      case network::mojom::DataElementType::kRawFile:
+      case network::mojom::DataElementType::kDataPipe:
+      case network::mojom::DataElementType::kChunkedDataPipe:
+      case network::mojom::DataElementType::kUnknown:
         NOTREACHED();
         break;
     }
diff --git a/content/browser/media/media_devices_permission_checker.cc b/content/browser/media/media_devices_permission_checker.cc
index 2e1b1829..eb4af5d 100644
--- a/content/browser/media/media_devices_permission_checker.cc
+++ b/content/browser/media/media_devices_permission_checker.cc
@@ -12,12 +12,12 @@
 #include "base/task/post_task.h"
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_switches.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -52,18 +52,18 @@
   // Speakers.
   // TODO(guidou): use specific permission for audio output when it becomes
   // available. See http://crbug.com/556542.
-  result[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
-      requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] &&
+  result[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      requested_device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] &&
       audio_permission;
 
   // Mic.
-  result[MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
-      requested_device_types[MEDIA_DEVICE_TYPE_AUDIO_INPUT] &&
+  result[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      requested_device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] &&
       audio_permission && mic_feature_policy;
 
   // Camera.
-  result[MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
-      requested_device_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
+  result[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
+      requested_device_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
       delegate->CheckMediaAccessPermission(frame_host, origin,
                                            blink::MEDIA_DEVICE_VIDEO_CAPTURE) &&
       camera_feature_policy;
@@ -71,7 +71,7 @@
   return result;
 }
 
-bool CheckSinglePermissionOnUIThread(MediaDeviceType device_type,
+bool CheckSinglePermissionOnUIThread(blink::MediaDeviceType device_type,
                                      int render_process_id,
                                      int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -96,7 +96,7 @@
     : use_override_(true), override_value_(override_value) {}
 
 bool MediaDevicesPermissionChecker::CheckPermissionOnUIThread(
-    MediaDeviceType device_type,
+    blink::MediaDeviceType device_type,
     int render_process_id,
     int render_frame_id) const {
   if (use_override_)
@@ -107,7 +107,7 @@
 }
 
 void MediaDevicesPermissionChecker::CheckPermission(
-    MediaDeviceType device_type,
+    blink::MediaDeviceType device_type,
     int render_process_id,
     int render_frame_id,
     base::OnceCallback<void(bool)> callback) const {
diff --git a/content/browser/media/media_devices_permission_checker.h b/content/browser/media/media_devices_permission_checker.h
index babbf971..8c6df4a 100644
--- a/content/browser/media/media_devices_permission_checker.h
+++ b/content/browser/media/media_devices_permission_checker.h
@@ -28,7 +28,7 @@
   // |render_process_id| and |render_frame_id| is allowed to access the media
   // device type |device_type|.
   // This method must be called on the UI thread.
-  bool CheckPermissionOnUIThread(MediaDeviceType device_type,
+  bool CheckPermissionOnUIThread(blink::MediaDeviceType device_type,
                                  int render_process_id,
                                  int render_frame_id) const;
 
@@ -37,7 +37,7 @@
   // device type |device_type|. The result is passed to |callback|.
   // This method can be called on any thread. |callback| is fired on the same
   // thread this method is called on.
-  void CheckPermission(MediaDeviceType device_type,
+  void CheckPermission(blink::MediaDeviceType device_type,
                        int render_process_id,
                        int render_frame_id,
                        base::OnceCallback<void(bool)> callback) const;
@@ -45,12 +45,11 @@
   // Checks if the origin associated to a render frame identified by
   // |render_process_id| and |render_frame_id| is allowed to access the media
   // device types marked with a value of true in |requested_device_types|. The
-  // result is passed to |callback|. The result is indexed by MediaDeviceType.
-  // Entries in the result with a value of true for requested device types
-  // indicate that the frame has permission to access devices of the
-  // corresponding types.
-  // This method can be called on any thread. |callback| is fired on the same
-  // thread this method is called on.
+  // result is passed to |callback|. The result is indexed by
+  // blink::MediaDeviceType. Entries in the result with a value of true for
+  // requested device types indicate that the frame has permission to access
+  // devices of the corresponding types. This method can be called on any
+  // thread. |callback| is fired on the same thread this method is called on.
   void CheckPermissions(
       MediaDevicesManager::BoolDeviceTypes requested_device_types,
       int render_process_id,
diff --git a/content/browser/media/media_devices_permission_checker_unittest.cc b/content/browser/media/media_devices_permission_checker_unittest.cc
index e21184e..46651a2 100644
--- a/content/browser/media/media_devices_permission_checker_unittest.cc
+++ b/content/browser/media/media_devices_permission_checker_unittest.cc
@@ -59,7 +59,7 @@
         ->SimulateFeaturePolicyHeader(feature, whitelist);
   }
 
-  bool CheckPermission(MediaDeviceType device_type) {
+  bool CheckPermission(blink::MediaDeviceType device_type) {
     base::RunLoop run_loop;
     quit_closure_ = run_loop.QuitClosure();
     checker_.CheckPermission(
@@ -101,18 +101,18 @@
 TEST_F(MediaDevicesPermissionCheckerTest, CheckPermissionWithFeaturePolicy) {
   // Mic and Camera should be enabled by default for a frame (if permission is
   // granted).
-  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
-  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+  EXPECT_TRUE(CheckPermission(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_TRUE(CheckPermission(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT));
 
   RefreshPageAndSetHeaderPolicy(blink::mojom::FeaturePolicyFeature::kMicrophone,
                                 /*enabled=*/false);
-  EXPECT_FALSE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
-  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+  EXPECT_FALSE(CheckPermission(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_TRUE(CheckPermission(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT));
 
   RefreshPageAndSetHeaderPolicy(blink::mojom::FeaturePolicyFeature::kCamera,
                                 /*enabled=*/false);
-  EXPECT_TRUE(CheckPermission(MEDIA_DEVICE_TYPE_AUDIO_INPUT));
-  EXPECT_FALSE(CheckPermission(MEDIA_DEVICE_TYPE_VIDEO_INPUT));
+  EXPECT_TRUE(CheckPermission(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT));
+  EXPECT_FALSE(CheckPermission(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT));
 }
 
 }  // namespace
diff --git a/content/browser/media/media_devices_util.cc b/content/browser/media/media_devices_util.cc
index 9ee36aa..7b5b355 100644
--- a/content/browser/media/media_devices_util.cc
+++ b/content/browser/media/media_devices_util.cc
@@ -28,9 +28,10 @@
 
 namespace {
 
-std::string GetDefaultMediaDeviceIDOnUIThread(MediaDeviceType device_type,
-                                              int render_process_id,
-                                              int render_frame_id) {
+std::string GetDefaultMediaDeviceIDOnUIThread(
+    blink::MediaDeviceType device_type,
+    int render_process_id,
+    int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderFrameHostImpl* frame_host =
       RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
@@ -43,10 +44,10 @@
 
   blink::MediaStreamType media_stream_type;
   switch (device_type) {
-    case MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
       media_stream_type = blink::MEDIA_DEVICE_AUDIO_CAPTURE;
       break;
-    case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       media_stream_type = blink::MEDIA_DEVICE_VIDEO_CAPTURE;
       break;
     default:
@@ -59,7 +60,7 @@
 // This function is intended for testing purposes. It returns an empty string
 // if no default device is supplied via the command line.
 std::string GetDefaultMediaDeviceIDFromCommandLine(
-    MediaDeviceType device_type) {
+    blink::MediaDeviceType device_type) {
   DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kUseFakeDeviceForMediaStream));
   const std::string option =
@@ -81,10 +82,10 @@
       return std::string();
     }
 
-    if (device_type == MEDIA_DEVICE_TYPE_AUDIO_INPUT &&
+    if (device_type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT &&
         param.front() == "audio-input-default-id") {
       return param.back();
-    } else if (device_type == MEDIA_DEVICE_TYPE_VIDEO_INPUT &&
+    } else if (device_type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT &&
                param.front() == "video-input-default-id") {
       return param.back();
     }
@@ -105,7 +106,7 @@
       origin(std::move(origin)) {}
 
 void GetDefaultMediaDeviceID(
-    MediaDeviceType device_type,
+    blink::MediaDeviceType device_type,
     int render_process_id,
     int render_frame_id,
     const base::Callback<void(const std::string&)>& callback) {
@@ -150,11 +151,11 @@
           std::move(origin)};
 }
 
-MediaDeviceInfo TranslateMediaDeviceInfo(
+blink::WebMediaDeviceInfo TranslateMediaDeviceInfo(
     bool has_permission,
     const MediaDeviceSaltAndOrigin& salt_and_origin,
-    const MediaDeviceInfo& device_info) {
-  return MediaDeviceInfo(
+    const blink::WebMediaDeviceInfo& device_info) {
+  return blink::WebMediaDeviceInfo(
       GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
                               salt_and_origin.origin, device_info.device_id),
       has_permission ? device_info.label : std::string(),
@@ -167,11 +168,11 @@
                      : media::MEDIA_VIDEO_FACING_NONE);
 }
 
-MediaDeviceInfoArray TranslateMediaDeviceInfoArray(
+blink::WebMediaDeviceInfoArray TranslateMediaDeviceInfoArray(
     bool has_permission,
     const MediaDeviceSaltAndOrigin& salt_and_origin,
-    const MediaDeviceInfoArray& device_infos) {
-  MediaDeviceInfoArray result;
+    const blink::WebMediaDeviceInfoArray& device_infos) {
+  blink::WebMediaDeviceInfoArray result;
   for (const auto& device_info : device_infos) {
     result.push_back(
         TranslateMediaDeviceInfo(has_permission, salt_and_origin, device_info));
diff --git a/content/browser/media/media_devices_util.h b/content/browser/media/media_devices_util.h
index d2a02ef..7665000 100644
--- a/content/browser/media/media_devices_util.h
+++ b/content/browser/media/media_devices_util.h
@@ -10,7 +10,7 @@
 
 #include "base/callback.h"
 #include "content/common/content_export.h"
-#include "content/common/media/media_devices.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "url/origin.h"
 
 namespace content {
@@ -18,7 +18,7 @@
 // Returns the ID of the user-default device ID via |callback|.
 // If no such device ID can be found, |callback| receives an empty string.
 CONTENT_EXPORT void GetDefaultMediaDeviceID(
-    MediaDeviceType device_type,
+    blink::MediaDeviceType device_type,
     int render_process_id,
     int render_frame_id,
     const base::Callback<void(const std::string&)>& callback);
@@ -47,17 +47,17 @@
 // The |device_id| field is hashed using |device_id_salt| and |security_origin|.
 // The |group_id| field is hashed using |group_id_salt| and |security_origin|.
 // The |label| field is removed if |has_permission| is false.
-MediaDeviceInfo TranslateMediaDeviceInfo(
+blink::WebMediaDeviceInfo TranslateMediaDeviceInfo(
     bool has_permission,
     const MediaDeviceSaltAndOrigin& salt_and_origin,
-    const MediaDeviceInfo& device_info);
+    const blink::WebMediaDeviceInfo& device_info);
 
 // Returns a translated version of |device_infos|, with each element translated
 // using TranslateMediaDeviceInfo().
-MediaDeviceInfoArray TranslateMediaDeviceInfoArray(
+blink::WebMediaDeviceInfoArray TranslateMediaDeviceInfoArray(
     bool has_permission,
     const MediaDeviceSaltAndOrigin& salt_and_origin,
-    const MediaDeviceInfoArray& device_infos);
+    const blink::WebMediaDeviceInfoArray& device_infos);
 
 // Type definition to make it easier to use mock alternatives to
 // GetMediaDeviceSaltAndOrigin.
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 6016791..df96436 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -343,11 +343,9 @@
     bool video_decoder_changed = false;
     bool has_cdm = false;
     bool is_incognito = false;
-    std::string audio_codec_name;
     std::string video_codec_name;
     std::string video_decoder;
     bool is_platform_video_decoder = false;
-    GURL origin_url;
   };
 
   // Helper function to report PipelineStatus associated with a player to UMA.
@@ -379,6 +377,15 @@
 
   auto it = player_info_map.find(event.id);
   if (it == player_info_map.end()) {
+    if (event.type != media::MediaLogEvent::WEBMEDIAPLAYER_CREATED) {
+      // Due to the asynchronous cleanup order of PipelineImpl and WMPI,
+      // sometimes a kStopped / kStopping event can sneak in after
+      // WEBMEDIAPLAYER_DESTROYED. This causes a new memory leak because the
+      // newly created PipelineImpl would never get cleaned up.
+      // As a result, we should be dropping any event that would target a
+      // player that hasn't already been created.
+      return;
+    }
     bool success = false;
     std::tie(it, success) = player_info_map.emplace(
         std::make_pair(event.id, PipelineInfo(IsIncognito(render_process_id))));
@@ -391,12 +398,6 @@
   PipelineInfo& player_info = it->second;
 
   switch (event.type) {
-    case media::MediaLogEvent::Type::WEBMEDIAPLAYER_CREATED: {
-      std::string origin_url;
-      event.params.GetString("origin_url", &origin_url);
-      player_info.origin_url = GURL(origin_url);
-      break;
-    }
     case media::MediaLogEvent::PLAY: {
       player_info.has_ever_played = true;
       break;
@@ -419,10 +420,6 @@
       if (event.params.HasKey("found_video_stream")) {
         event.params.GetBoolean("found_video_stream", &player_info.has_video);
       }
-      if (event.params.HasKey("audio_codec_name")) {
-        event.params.GetString("audio_codec_name",
-                               &player_info.audio_codec_name);
-      }
       if (event.params.HasKey("video_codec_name")) {
         event.params.GetString("video_codec_name",
                                &player_info.video_codec_name);
diff --git a/content/browser/media/media_internals_audio_focus_helper.cc b/content/browser/media/media_internals_audio_focus_helper.cc
index 6a84cbd..758cc2d 100644
--- a/content/browser/media/media_internals_audio_focus_helper.cc
+++ b/content/browser/media/media_internals_audio_focus_helper.cc
@@ -28,6 +28,7 @@
 const char kAudioFocusSessionsKey[] = "sessions";
 
 const char kAudioFocusForceDuck[] = "ForceDuck";
+const char kAudioFocusPreferStop[] = "PreferStop";
 
 const char kAudioFocusTypeGain[] = "Gain";
 const char kAudioFocusTypeGainTransient[] = "GainTransient";
@@ -52,7 +53,7 @@
 void MediaInternalsAudioFocusHelper::SendAudioFocusState() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (!CanUpdate())
+  if (!EnsureServiceConnection())
     return;
 
   // Get the audio focus state from the media session service.
@@ -84,40 +85,32 @@
 void MediaInternalsAudioFocusHelper::SetEnabled(bool enabled) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (enabled_ == enabled)
-    return;
-
   enabled_ = enabled;
 
   EnsureServiceConnection();
 
-  if (enabled) {
-    // Add the observer to receive audio focus events.
-    media_session::mojom::AudioFocusObserverPtr observer;
-    binding_.Bind(mojo::MakeRequest(&observer));
-    audio_focus_ptr_->AddObserver(std::move(observer));
-
-    binding_.set_connection_error_handler(base::BindRepeating(
-        &MediaInternalsAudioFocusHelper::OnMojoError, base::Unretained(this)));
-  } else {
+  if (!enabled) {
     audio_focus_ptr_.reset();
     audio_focus_debug_ptr_.reset();
     binding_.Close();
   }
 }
 
-void MediaInternalsAudioFocusHelper::EnsureServiceConnection() {
+bool MediaInternalsAudioFocusHelper::EnsureServiceConnection() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  if (!enabled_)
+    return false;
+
   // |connection| and |connector| may be nullptr in some tests.
   ServiceManagerConnection* connection =
       ServiceManagerConnection::GetForProcess();
   if (!connection)
-    return;
+    return false;
 
   service_manager::Connector* connector = connection->GetConnector();
   if (!connector)
-    return;
+    return false;
 
   // Connect to the media session service.
   if (!audio_focus_ptr_.is_bound()) {
@@ -131,22 +124,38 @@
   if (!audio_focus_debug_ptr_.is_bound()) {
     connector->BindInterface(media_session::mojom::kServiceName,
                              mojo::MakeRequest(&audio_focus_debug_ptr_));
-    audio_focus_debug_ptr_.set_connection_error_handler(base::BindRepeating(
+    audio_focus_debug_ptr_.set_connection_error_handler(
+        base::BindRepeating(&MediaInternalsAudioFocusHelper::OnDebugMojoError,
+                            base::Unretained(this)));
+  }
+
+  // Add the observer to receive audio focus events.
+  if (!binding_.is_bound()) {
+    media_session::mojom::AudioFocusObserverPtr observer;
+    binding_.Bind(mojo::MakeRequest(&observer));
+    audio_focus_ptr_->AddObserver(std::move(observer));
+
+    binding_.set_connection_error_handler(base::BindRepeating(
         &MediaInternalsAudioFocusHelper::OnMojoError, base::Unretained(this)));
   }
+
+  return true;
 }
 
 void MediaInternalsAudioFocusHelper::OnMojoError() {
   audio_focus_ptr_.reset();
-  audio_focus_debug_ptr_.reset();
   binding_.Close();
 }
 
+void MediaInternalsAudioFocusHelper::OnDebugMojoError() {
+  audio_focus_debug_ptr_.reset();
+}
+
 void MediaInternalsAudioFocusHelper::DidGetAudioFocusRequestList(
     std::vector<media_session::mojom::AudioFocusRequestStatePtr> stack) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (!CanUpdate())
+  if (!EnsureServiceConnection())
     return;
 
   audio_focus_data_.Clear();
@@ -186,7 +195,7 @@
     media_session::mojom::MediaSessionDebugInfoPtr info) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (!CanUpdate())
+  if (!EnsureServiceConnection())
     return;
 
   base::Value* sessions_list =
@@ -215,11 +224,6 @@
   SerializeAndSendUpdate(kAudioFocusFunction, &audio_focus_data_);
 }
 
-bool MediaInternalsAudioFocusHelper::CanUpdate() const {
-  return enabled_ && audio_focus_ptr_.is_bound() &&
-         audio_focus_debug_ptr_.is_bound();
-}
-
 void MediaInternalsAudioFocusHelper::SerializeAndSendUpdate(
     const std::string& function,
     const base::Value* value) {
@@ -295,6 +299,10 @@
   if (state->session_info->force_duck)
     stream << " " << kAudioFocusForceDuck;
 
+  // Convert the |prefer_stop_for_gain_focus_loss| boolean into a string.
+  if (state->session_info->prefer_stop_for_gain_focus_loss)
+    stream << " " << kAudioFocusPreferStop;
+
   // Convert the |is_controllable| boolean into a string.
   if (state->session_info->is_controllable)
     stream << " " << kMediaSessionIsControllable;
diff --git a/content/browser/media/media_internals_audio_focus_helper.h b/content/browser/media/media_internals_audio_focus_helper.h
index e7c3e8e..272ea209 100644
--- a/content/browser/media/media_internals_audio_focus_helper.h
+++ b/content/browser/media/media_internals_audio_focus_helper.h
@@ -36,8 +36,9 @@
   void SetEnabled(bool enabled);
 
  private:
-  void EnsureServiceConnection();
+  bool EnsureServiceConnection();
   void OnMojoError();
+  void OnDebugMojoError();
 
   // Called when we receive the list of audio focus requests to display.
   void DidGetAudioFocusRequestList(
@@ -49,7 +50,6 @@
       const std::string& id,
       media_session::mojom::MediaSessionDebugInfoPtr info);
 
-  bool CanUpdate() const;
   void SerializeAndSendUpdate(const std::string& function,
                               const base::Value* value);
 
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index d2a9592..74a0e1c 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -205,9 +205,7 @@
 void MediaSessionImpl::NotifyAddedObserver(MediaSessionObserver* observer) {
   observer->MediaSessionMetadataChanged(
       routed_service_ ? routed_service_->metadata() : base::nullopt);
-  observer->MediaSessionActionsChanged(
-      routed_service_ ? routed_service_->actions()
-                      : std::set<media_session::mojom::MediaSessionAction>());
+  observer->MediaSessionActionsChanged(actions_);
   observer->MediaSessionStateChanged(IsControllable(), IsActuallyPaused());
 }
 
@@ -222,19 +220,6 @@
       });
 }
 
-void MediaSessionImpl::NotifyMediaSessionActionsChange(
-    const std::set<media_session::mojom::MediaSessionAction>& actions) {
-  for (auto& observer : observers_)
-    observer.MediaSessionActionsChanged(actions);
-
-  std::vector<media_session::mojom::MediaSessionAction> actions_vec(
-      actions.begin(), actions.end());
-  mojo_observers_.ForAllPtrs(
-      [&actions_vec](media_session::mojom::MediaSessionObserver* observer) {
-        observer->MediaSessionActionsChanged(actions_vec);
-      });
-}
-
 bool MediaSessionImpl::AddPlayer(MediaSessionPlayerObserver* observer,
                                  int player_id,
                                  media::MediaContentType media_content_type) {
@@ -294,6 +279,7 @@
   if (old_audio_focus_state != audio_focus_state_ ||
       old_controllable != IsControllable()) {
     NotifyLegacyObserversStateChange();
+    RebuildAndNotifyActionsChanged();
   }
 
   return true;
@@ -323,8 +309,10 @@
   // The session may become controllable after removing a one-shot player.
   // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify
   // about the state change.
-  if (!was_controllable && IsControllable())
+  if (!was_controllable && IsControllable()) {
     NotifyLegacyObserversStateChange();
+    RebuildAndNotifyActionsChanged();
+  }
 }
 
 void MediaSessionImpl::RemovePlayers(MediaSessionPlayerObserver* observer) {
@@ -357,8 +345,10 @@
   // The session may become controllable after removing a one-shot player.
   // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify
   // about the state change.
-  if (!was_controllable && IsControllable())
+  if (!was_controllable && IsControllable()) {
     NotifyLegacyObserversStateChange();
+    RebuildAndNotifyActionsChanged();
+  }
 }
 
 void MediaSessionImpl::RecordSessionDuck() {
@@ -406,7 +396,7 @@
   if (suspend_type == SuspendType::kUI) {
     // If the site has registered an action handler for play then we should
     // pass it to the site and let them handle it.
-    if (IsActionSupported(media_session::mojom::MediaSessionAction::kPlay)) {
+    if (ShouldRouteAction(media_session::mojom::MediaSessionAction::kPlay)) {
       DidReceiveAction(media_session::mojom::MediaSessionAction::kPlay);
       return;
     }
@@ -442,7 +432,7 @@
   if (suspend_type == SuspendType::kUI) {
     // If the site has registered an action handler for pause then we should
     // pass it to the site and let them handle it.
-    if (IsActionSupported(media_session::mojom::MediaSessionAction::kPause)) {
+    if (ShouldRouteAction(media_session::mojom::MediaSessionAction::kPause)) {
       DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
       return;
     }
@@ -485,7 +475,7 @@
   if (seek_time > base::TimeDelta()) {
     // If the site has registered an action handler for seek forward then we
     // should pass it to the site and let them handle it.
-    if (IsActionSupported(
+    if (ShouldRouteAction(
             media_session::mojom::MediaSessionAction::kSeekForward)) {
       DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekForward);
       return;
@@ -496,7 +486,7 @@
   } else if (seek_time < base::TimeDelta()) {
     // If the site has registered an action handler for seek backward then we
     // should pass it to the site and let them handle it.
-    if (IsActionSupported(
+    if (ShouldRouteAction(
             media_session::mojom::MediaSessionAction::kSeekBackward)) {
       DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekBackward);
       return;
@@ -786,7 +776,7 @@
 
   if (routed_service_) {
     std::vector<media_session::mojom::MediaSessionAction> actions(
-        routed_service_->actions().begin(), routed_service_->actions().end());
+        actions_.begin(), actions_.end());
     observer->MediaSessionActionsChanged(actions);
   } else {
     observer->MediaSessionActionsChanged(
@@ -853,6 +843,7 @@
 
   SetAudioFocusState(State::INACTIVE);
   NotifyLegacyObserversStateChange();
+  RebuildAndNotifyActionsChanged();
 }
 
 void MediaSessionImpl::NotifyLegacyObserversStateChange() {
@@ -960,7 +951,9 @@
     MediaSessionServiceImpl* service) {
   if (service != routed_service_)
     return;
+
   NotifyLegacyObserversStateChange();
+  RebuildAndNotifyActionsChanged();
 }
 
 void MediaSessionImpl::OnMediaSessionMetadataChanged(
@@ -976,7 +969,7 @@
   if (service != routed_service_)
     return;
 
-  NotifyMediaSessionActionsChange(routed_service_->actions());
+  RebuildAndNotifyActionsChanged();
 }
 
 void MediaSessionImpl::DidReceiveAction(
@@ -1034,7 +1027,7 @@
   routed_service_ = new_service;
   if (routed_service_) {
     NotifyMediaSessionMetadataChange(routed_service_->metadata());
-    NotifyMediaSessionActionsChange(routed_service_->actions());
+    RebuildAndNotifyActionsChanged();
   }
 }
 
@@ -1079,12 +1072,41 @@
   return best_frame ? services_[best_frame] : nullptr;
 }
 
-bool MediaSessionImpl::IsActionSupported(
+bool MediaSessionImpl::ShouldRouteAction(
     media_session::mojom::MediaSessionAction action) const {
   return routed_service_ &&
          base::ContainsKey(routed_service_->actions(), action);
 }
 
+void MediaSessionImpl::RebuildAndNotifyActionsChanged() {
+  std::set<media_session::mojom::MediaSessionAction> actions =
+      routed_service_ ? routed_service_->actions()
+                      : std::set<media_session::mojom::MediaSessionAction>();
+
+  // If we are controllable then we should always add these actions as we can
+  // support them by directly interacting with the players underneath.
+  if (IsControllable()) {
+    actions.insert(media_session::mojom::MediaSessionAction::kPlay);
+    actions.insert(media_session::mojom::MediaSessionAction::kPause);
+    actions.insert(media_session::mojom::MediaSessionAction::kStop);
+  }
+
+  if (actions_ == actions)
+    return;
+
+  actions_ = actions;
+
+  for (auto& observer : observers_)
+    observer.MediaSessionActionsChanged(actions);
+
+  std::vector<media_session::mojom::MediaSessionAction> actions_vec(
+      actions.begin(), actions.end());
+  mojo_observers_.ForAllPtrs(
+      [&actions_vec](media_session::mojom::MediaSessionObserver* observer) {
+        observer->MediaSessionActionsChanged(actions_vec);
+      });
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(MediaSessionImpl)
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 9ca371b..1fd2b202 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -95,8 +95,6 @@
 
   void NotifyMediaSessionMetadataChange(
       const base::Optional<media_session::MediaMetadata>& metadata);
-  void NotifyMediaSessionActionsChange(
-      const std::set<media_session::mojom::MediaSessionAction>& actions);
 
   // Adds the given player to the current media session. Returns whether the
   // player was successfully added. If it returns false, AddPlayer() should be
@@ -350,8 +348,15 @@
   // to update |routed_service_|.
   CONTENT_EXPORT MediaSessionServiceImpl* ComputeServiceForRouting();
 
-  // Returns whether the action is supported by the media session.
-  bool IsActionSupported(media_session::mojom::MediaSessionAction action) const;
+  // Returns whether the action should be routed to |routed_service_|.
+  bool ShouldRouteAction(media_session::mojom::MediaSessionAction action) const;
+
+  // Rebuilds |actions_| and notifies observers if they have changed.
+  void RebuildAndNotifyActionsChanged();
+
+  // A set of actions supported by |routed_service_| and the current media
+  // session.
+  std::set<media_session::mojom::MediaSessionAction> actions_;
 
   std::unique_ptr<AudioFocusDelegate> delegate_;
   std::map<PlayerIdentifier, media_session::mojom::AudioFocusType>
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index ba257dc..7faecb7 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -1823,10 +1823,16 @@
   mock_media_session_service_->SetMetadata(metadata);
 
   mock_media_session_service_->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+      media_session::mojom::MediaSessionAction::kSeekForward);
   std::set<media_session::mojom::MediaSessionAction> expectedActions =
       mock_media_session_service_->actions();
 
+  // These actions are provided by media session automatically if we are
+  // controllable.
+  expectedActions.insert(media_session::mojom::MediaSessionAction::kPlay);
+  expectedActions.insert(media_session::mojom::MediaSessionAction::kPause);
+  expectedActions.insert(media_session::mojom::MediaSessionAction::kStop);
+
   // Make sure the service is routed,
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
       shell()->web_contents()->GetMainFrame());
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index b04d1667..c4a99ab 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -28,6 +28,8 @@
 using ::testing::InvokeWithoutArgs;
 using ::testing::NiceMock;
 
+using media_session::mojom::MediaSessionAction;
+
 namespace content {
 
 namespace {
@@ -64,7 +66,12 @@
 class MediaSessionImplServiceRoutingTest
     : public RenderViewHostImplTestHarness {
  public:
-  MediaSessionImplServiceRoutingTest() = default;
+  MediaSessionImplServiceRoutingTest() {
+    actions_.insert(MediaSessionAction::kPlay);
+    actions_.insert(MediaSessionAction::kPause);
+    actions_.insert(MediaSessionAction::kStop);
+  }
+
   ~MediaSessionImplServiceRoutingTest() override = default;
 
   void SetUp() override {
@@ -138,6 +145,17 @@
     return MediaSessionImpl::Get(contents());
   }
 
+  std::set<MediaSessionAction> GetDefaultActionsWithExtra(
+      MediaSessionAction action) const {
+    std::set<MediaSessionAction> actions(actions_.begin(), actions_.end());
+    actions.insert(action);
+    return actions;
+  }
+
+  const std::set<MediaSessionAction>& default_actions() const {
+    return actions_;
+  }
+
   TestRenderFrameHost* main_frame_;
   TestRenderFrameHost* sub_frame_;
 
@@ -152,6 +170,8 @@
   PlayerMap players_;
 
  private:
+  std::set<MediaSessionAction> actions_;
+
   std::unique_ptr<content::TestServiceManagerContext>
       test_service_manager_context_;
 };
@@ -254,8 +274,7 @@
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(media_session::MediaMetadata());
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -265,30 +284,26 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<media_session::mojom::MediaSessionAction> empty_actions;
-  std::set<media_session::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
-
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionMetadataChanged(Eq(base::nullopt)))
       .Times(AnyNumber());
   EXPECT_CALL(*mock_media_session_observer(),
-              MediaSessionActionsChanged(Eq(empty_actions)))
+              MediaSessionActionsChanged(Eq(default_actions())))
       .Times(AnyNumber());
 
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionMetadataChanged(Eq(expected_metadata)))
       .Times(1);
   EXPECT_CALL(*mock_media_session_observer(),
-              MediaSessionActionsChanged(Eq(expected_actions)))
+              MediaSessionActionsChanged(Eq(GetDefaultActionsWithExtra(
+                  MediaSessionAction::kSeekForward))))
       .Times(1);
 
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -298,21 +313,18 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<media_session::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
-
   EXPECT_CALL(*mock_media_session_observer(),
               MediaSessionMetadataChanged(Eq(expected_metadata)))
       .Times(1);
   EXPECT_CALL(*mock_media_session_observer(),
-              MediaSessionActionsChanged(Eq(expected_actions)))
+              MediaSessionActionsChanged(Eq(GetDefaultActionsWithExtra(
+                  MediaSessionAction::kSeekForward))))
       .Times(1);
 
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
   StartPlayerForFrame(main_frame_);
 }
@@ -324,9 +336,9 @@
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
 
-  std::set<media_session::mojom::MediaSessionAction> empty_actions;
-  std::set<media_session::mojom::MediaSessionAction> expected_actions;
-  expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
+  std::set<MediaSessionAction> empty_actions;
+  std::set<MediaSessionAction> expected_actions;
+  expected_actions.insert(MediaSessionAction::kPlay);
 
   EXPECT_CALL(*mock_media_session_observer(), MediaSessionMetadataChanged(_))
       .Times(AnyNumber());
@@ -342,8 +354,7 @@
   CreateServiceForFrame(main_frame_);
 
   services_[main_frame_]->SetMetadata(expected_metadata);
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
 
   StartPlayerForFrame(main_frame_);
   ClearPlayersForFrame(main_frame_);
@@ -359,16 +370,14 @@
   CreateServiceForFrame(main_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(sub_frame_), OnSuspend(_));
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kPause))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kPause))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPause);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPause);
 
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
+      ->DidReceiveAction(MediaSessionAction::kPause);
 
   run_loop.Run();
 }
@@ -383,16 +392,14 @@
   CreateServiceForFrame(sub_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(main_frame_), OnSuspend(_));
-  EXPECT_CALL(
-      *GetClientForFrame(sub_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kPause))
+  EXPECT_CALL(*GetClientForFrame(sub_frame_),
+              DidReceiveAction(MediaSessionAction::kPause))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[sub_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPause);
+  services_[sub_frame_]->EnableAction(MediaSessionAction::kPause);
 
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
+      ->DidReceiveAction(MediaSessionAction::kPause);
 
   run_loop.Run();
 }
@@ -406,7 +413,7 @@
 
   // This should not crash.
   MediaSessionImpl::Get(contents())
-      ->DidReceiveAction(media_session::mojom::MediaSessionAction::kPause);
+      ->DidReceiveAction(MediaSessionAction::kPause);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -419,12 +426,10 @@
   CreateServiceForFrame(main_frame_);
 
   EXPECT_CALL(*GetClientForFrame(main_frame_),
-              DidReceiveAction(
-                  media_session::mojom::MediaSessionAction::kPreviousTrack))
+              DidReceiveAction(MediaSessionAction::kPreviousTrack))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPreviousTrack);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPreviousTrack);
 
   MediaSessionImpl::Get(contents())->PreviousTrack();
   run_loop.Run();
@@ -439,13 +444,11 @@
 
   CreateServiceForFrame(main_frame_);
 
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kNextTrack))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kNextTrack))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kNextTrack);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kNextTrack);
 
   MediaSessionImpl::Get(contents())->NextTrack();
   run_loop.Run();
@@ -460,9 +463,8 @@
   EXPECT_CALL(*GetPlayerForFrame(main_frame_),
               OnSeekBackward(_, kDefaultSeekTime))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekBackward))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kSeekBackward))
       .Times(0);
 
   MediaSessionImpl::Get(contents())->Seek(kDefaultSeekTime * -1);
@@ -477,13 +479,11 @@
   CreateServiceForFrame(main_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(main_frame_), OnSeekBackward(_, _)).Times(0);
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekBackward))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kSeekBackward))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kSeekBackward);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekBackward);
 
   MediaSessionImpl::Get(contents())->Seek(kDefaultSeekTime * -1);
   run_loop.Run();
@@ -498,9 +498,8 @@
   EXPECT_CALL(*GetPlayerForFrame(main_frame_),
               OnSeekForward(_, kDefaultSeekTime))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekForward))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kSeekForward))
       .Times(0);
 
   MediaSessionImpl::Get(contents())->Seek(kDefaultSeekTime);
@@ -515,13 +514,11 @@
   CreateServiceForFrame(main_frame_);
 
   EXPECT_CALL(*GetPlayerForFrame(main_frame_), OnSeekForward(_, _)).Times(0);
-  EXPECT_CALL(
-      *GetClientForFrame(main_frame_),
-      DidReceiveAction(media_session::mojom::MediaSessionAction::kSeekForward))
+  EXPECT_CALL(*GetClientForFrame(main_frame_),
+              DidReceiveAction(MediaSessionAction::kSeekForward))
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kSeekForward);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
   MediaSessionImpl::Get(contents())->Seek(kDefaultSeekTime);
   run_loop.Run();
@@ -586,22 +583,65 @@
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_);
 
-  services_[main_frame_]->EnableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
   observer.WaitForActions();
 
-  EXPECT_EQ(1u, observer.actions().size());
-  EXPECT_EQ(media_session::mojom::MediaSessionAction::kPlay,
-            observer.actions()[0]);
+  EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
+            observer.actions_set());
 
-  services_[main_frame_]->DisableAction(
-      media_session::mojom::MediaSessionAction::kPlay);
+  services_[main_frame_]->DisableAction(MediaSessionAction::kSeekForward);
   observer.WaitForActions();
 
-  EXPECT_TRUE(observer.actions().empty());
+  EXPECT_EQ(default_actions(), observer.actions_set());
+}
+
+TEST_F(MediaSessionImplServiceRoutingTest, DefaultActionsAlwaysSupported) {
+  CreateServiceForFrame(main_frame_);
+  StartPlayerForFrame(main_frame_);
+
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+
+  EXPECT_EQ(default_actions(), observer.actions_set());
+
+  services_[main_frame_]->DisableAction(MediaSessionAction::kPlay);
+
+  // This will cause the observer to be flushed with the latest actions and
+  // kPlay should still be there even though we disabled it.
+  services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
+  observer.WaitForActions();
+
+  EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
+            observer.actions_set());
+}
+
+TEST_F(MediaSessionImplServiceRoutingTest,
+       DefaultActionsRemovedIfUncontrollable) {
+  CreateServiceForFrame(main_frame_);
+  StartPlayerForFrame(main_frame_);
+
+  services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+
+  EXPECT_EQ(default_actions(), observer.actions_set());
+
+  // This will make the media session uncontrollable so we only should have the
+  // actions from the service.
+  ClearPlayersForFrame(main_frame_);
+  observer.WaitForActions();
+
+  std::set<MediaSessionAction> expected_actions;
+  expected_actions.insert(MediaSessionAction::kPlay);
+  EXPECT_EQ(expected_actions, observer.actions_set());
 }
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl_unittest.cc b/content/browser/media/session/media_session_impl_unittest.cc
index 625a10f3..df048f77 100644
--- a/content/browser/media/session/media_session_impl_unittest.cc
+++ b/content/browser/media/session/media_session_impl_unittest.cc
@@ -76,7 +76,11 @@
 
 class MediaSessionImplTest : public RenderViewHostTestHarness {
  public:
-  MediaSessionImplTest() = default;
+  MediaSessionImplTest() {
+    default_actions_.insert(media_session::mojom::MediaSessionAction::kPlay);
+    default_actions_.insert(media_session::mojom::MediaSessionAction::kPause);
+    default_actions_.insert(media_session::mojom::MediaSessionAction::kStop);
+  }
 
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
@@ -163,7 +167,14 @@
                                  media::MediaContentType::Persistent);
   }
 
+  const std::set<media_session::mojom::MediaSessionAction>& default_actions()
+      const {
+    return default_actions_;
+  }
+
  private:
+  std::set<media_session::mojom::MediaSessionAction> default_actions_;
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
   std::unique_ptr<MockMediaSessionServiceImpl> mock_media_session_service_;
@@ -330,6 +341,11 @@
 
   GetMediaSession()->Suspend(MediaSession::SuspendType::kUI);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, SuspendContent_WithAction) {
@@ -344,6 +360,11 @@
 
   GetMediaSession()->Suspend(MediaSession::SuspendType::kContent);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, SuspendSystem_WithAction) {
@@ -358,6 +379,11 @@
 
   GetMediaSession()->Suspend(MediaSession::SuspendType::kSystem);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, SuspendUI_WithAction) {
@@ -371,6 +397,11 @@
 
   GetMediaSession()->Suspend(MediaSession::SuspendType::kUI);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, ResumeUI) {
@@ -383,6 +414,11 @@
   GetMediaSession()->Suspend(MediaSession::SuspendType::kSystem);
   GetMediaSession()->Resume(MediaSession::SuspendType::kUI);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, ResumeContent_WithAction) {
@@ -397,6 +433,11 @@
   GetMediaSession()->Suspend(MediaSession::SuspendType::kSystem);
   GetMediaSession()->Resume(MediaSession::SuspendType::kContent);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, ResumeSystem_WithAction) {
@@ -411,6 +452,11 @@
   GetMediaSession()->Suspend(MediaSession::SuspendType::kSystem);
   GetMediaSession()->Resume(MediaSession::SuspendType::kSystem);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 TEST_F(MediaSessionImplTest, ResumeUI_WithAction) {
@@ -425,6 +471,11 @@
   GetMediaSession()->Suspend(MediaSession::SuspendType::kSystem);
   GetMediaSession()->Resume(MediaSession::SuspendType::kUI);
   mock_media_session_service().FlushForTesting();
+
+  media_session::test::MockMediaSessionMojoObserver observer(
+      *GetMediaSession());
+  observer.WaitForActions();
+  EXPECT_EQ(default_actions(), observer.actions_set());
 }
 
 #if !defined(OS_ANDROID)
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc
index 4909703..66dc7f4 100644
--- a/content/browser/media/session/media_session_service_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -31,10 +31,11 @@
   void MediaSessionActionsChanged(
       const std::set<media_session::mojom::MediaSessionAction>& actions)
       override {
-    // The actions might be empty when the service becomes routed for the first
-    // time.
-    if (actions.size() == 1)
+    // Wait for the page action to be present.
+    if (base::ContainsKey(
+            actions, media_session::mojom::MediaSessionAction::kSeekForward)) {
       closure_on_actions_change_.Run();
+    }
   }
 
  private:
@@ -90,7 +91,7 @@
 char kSetUpMediaSessionScript[] =
     "navigator.mediaSession.playbackState = \"playing\";\n"
     "navigator.mediaSession.metadata = new MediaMetadata({ title: \"foo\" });\n"
-    "navigator.mediaSession.setActionHandler(\"play\", _ => {});";
+    "navigator.mediaSession.setActionHandler(\"seekforward\", _ => {});";
 
 const int kPlayerId = 0;
 
diff --git a/content/browser/network_service_client_unittest.cc b/content/browser/network_service_client_unittest.cc
index 5d6e02160..de2c0047 100644
--- a/content/browser/network_service_client_unittest.cc
+++ b/content/browser/network_service_client_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/test_file_util.h"
 #include "build/build_config.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -76,7 +77,7 @@
   }
 
  protected:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestBrowserThreadBundle scoped_task_environment_;
   network::mojom::NetworkServiceClientPtr client_ptr_;
   NetworkServiceClient client_;
   base::ScopedTempDir temp_dir_;
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
index 1aaa42e9..8e33684 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
@@ -289,6 +289,9 @@
 
   base::string16 file_path_folded = base::i18n::FoldCase(file_path);
 
+  if (!file_path_folded.size())
+    return false;
+
   if (!base::StartsWith(file_path_folded, windows_fonts_path,
                         base::CompareCase::SENSITIVE)) {
     LogLoaderType(FILE_OUTSIDE_SANDBOX);
@@ -678,6 +681,12 @@
                            &custom_font_path_set, &ttc_index)) {
         if (IsLastResortFallbackFont(family_index))
           LogMessageFilterError(LAST_RESORT_FONT_ADD_FILES_FAILED);
+
+        // It's possible to not be able to retrieve a font file for a font that
+        // is in the system font collection, see https://crbug.com/922183. If we
+        // were not able to retrieve a file for a registered font, we do not
+        // need to add it to the map.
+        continue;
       }
 
       // After having received clarification from Microsoft, the API is designed
diff --git a/content/browser/renderer_host/frame_sink_provider_impl.h b/content/browser/renderer_host/frame_sink_provider_impl.h
index 05949c53..ff610d3a 100644
--- a/content/browser/renderer_host/frame_sink_provider_impl.h
+++ b/content/browser/renderer_host/frame_sink_provider_impl.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 CONTENT_BROWSER_RENDER_WIDGET_COMPOSITOR_FRAME_SINK_PROVIDER_H_
-#define CONTENT_BROWSER_RENDER_WIDGET_COMPOSITOR_FRAME_SINK_PROVIDER_H_
+#ifndef CONTENT_BROWSER_RENDERER_HOST_FRAME_SINK_PROVIDER_IMPL_H_
+#define CONTENT_BROWSER_RENDERER_HOST_FRAME_SINK_PROVIDER_IMPL_H_
 
 #include "content/common/frame_sink_provider.mojom.h"
 #include "content/common/render_frame_metadata.mojom.h"
@@ -42,4 +42,4 @@
 
 }  // namespace content
 
-#endif  //  CONTENT_BROWSER_RENDER_WIDGET_COMPOSITOR_FRAME_SINK_PROVIDER_H_
+#endif  //  CONTENT_BROWSER_RENDERER_HOST_FRAME_SINK_PROVIDER_IMPL_H_
diff --git a/content/browser/renderer_host/input/compositor_event_ack_browsertest.cc b/content/browser/renderer_host/input/compositor_event_ack_browsertest.cc
index dda80454..3e50c08 100644
--- a/content/browser/renderer_host/input/compositor_event_ack_browsertest.cc
+++ b/content/browser/renderer_host/input/compositor_event_ack_browsertest.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/hit_test_region_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -134,8 +135,8 @@
     TitleWatcher watcher(shell()->web_contents(), ready_title);
     ignore_result(watcher.WaitAndGetTitle());
 
-    MainThreadFrameObserver main_thread_sync(host);
-    main_thread_sync.Wait();
+    HitTestRegionObserver observer(host->GetFrameSinkId());
+    observer.WaitForHitTestData();
   }
 
   int ExecuteScriptAndExtractInt(const std::string& script) {
diff --git a/content/browser/renderer_host/input/main_thread_event_queue_browsertest.cc b/content/browser/renderer_host/input/main_thread_event_queue_browsertest.cc
index a26f229a..70a04eb 100644
--- a/content/browser/renderer_host/input/main_thread_event_queue_browsertest.cc
+++ b/content/browser/renderer_host/input/main_thread_event_queue_browsertest.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/hit_test_region_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -97,8 +98,8 @@
     TitleWatcher watcher(shell()->web_contents(), ready_title);
     ignore_result(watcher.WaitAndGetTitle());
 
-    MainThreadFrameObserver main_thread_sync(host);
-    main_thread_sync.Wait();
+    HitTestRegionObserver observer(host->GetFrameSinkId());
+    observer.WaitForHitTestData();
   }
 
   int ExecuteScriptAndExtractInt(const std::string& script) {
diff --git a/content/browser/renderer_host/input/scroll_latency_browsertest.cc b/content/browser/renderer_host/input/scroll_latency_browsertest.cc
index ad008cb..5be1c7dd 100644
--- a/content/browser/renderer_host/input/scroll_latency_browsertest.cc
+++ b/content/browser/renderer_host/input/scroll_latency_browsertest.cc
@@ -21,6 +21,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/hit_test_region_observer.h"
 #include "content/shell/browser/shell.h"
 
 namespace {
@@ -80,12 +81,6 @@
     run_loop.Run();
   }
 
-  void WaitAFrame() {
-    while (!GetWidgetHost()->RequestRepaintForTesting())
-      GiveItSomeTime();
-    frame_observer_->Wait();
-  }
-
   void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
     EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
     run_loop_->Quit();
@@ -99,12 +94,10 @@
     RenderWidgetHostImpl* host = GetWidgetHost();
     host->GetView()->SetSize(gfx::Size(400, 400));
 
-    frame_observer_ = std::make_unique<MainThreadFrameObserver>(
-        shell()->web_contents()->GetRenderViewHost()->GetWidget());
+    HitTestRegionObserver observer(host->GetFrameSinkId());
 
-    // Wait a frame to make sure the page has renderered.
-    WaitAFrame();
-    frame_observer_.reset();
+    // Wait for the hit test data to be ready
+    observer.WaitForHitTestData();
   }
 
   // Generate a single wheel tick, scrolling by |distance|. This will perform a
@@ -140,7 +133,6 @@
 
  private:
   base::HistogramTester histogram_tester_;
-  std::unique_ptr<MainThreadFrameObserver> frame_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(ScrollLatencyBrowserTest);
 };
diff --git a/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc b/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
index bf2f46d..8895b47 100644
--- a/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
+++ b/content/browser/renderer_host/input/touchpad_pinch_browsertest.cc
@@ -12,6 +12,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/hit_test_region_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 
@@ -77,7 +78,8 @@
   void LoadURL() {
     const GURL data_url(kTouchpadPinchDataURL);
     NavigateToURL(shell(), data_url);
-    SynchronizeThreads();
+    HitTestRegionObserver observer(GetRenderWidgetHost()->GetFrameSinkId());
+    observer.WaitForHitTestData();
   }
 
   RenderWidgetHostImpl* GetRenderWidgetHost() {
@@ -87,7 +89,7 @@
                                           ->GetRenderWidgetHost());
   }
 
-  void SynchronizeThreads() {
+  void WaitForJavascriptExecution() {
     MainThreadFrameObserver observer(GetRenderWidgetHost());
     observer.Wait();
   }
@@ -124,7 +126,7 @@
   LoadURL();
   ASSERT_TRUE(
       content::ExecuteScript(shell()->web_contents(), "setListener(false);"));
-  SynchronizeThreads();
+  WaitForJavascriptExecution();
 
   content::TestPageScaleObserver scale_observer(shell()->web_contents());
 
@@ -168,7 +170,7 @@
 
   ASSERT_TRUE(
       content::ExecuteScript(shell()->web_contents(), "setListener(true);"));
-  SynchronizeThreads();
+  WaitForJavascriptExecution();
 
   std::move(send_events).Run(shell()->web_contents(), pinch_position);
 
@@ -187,7 +189,7 @@
   ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
                                      "reset(); "
                                      "setListener(false);"));
-  SynchronizeThreads();
+  WaitForJavascriptExecution();
 
   content::TestPageScaleObserver scale_observer(shell()->web_contents());
   SimulateGesturePinchSequence(shell()->web_contents(), pinch_position, 2.0,
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc
index f80b0a6..7df8a09 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -339,7 +339,12 @@
     // has moved outside the bounds of the parent.
     POINT cursor_pos;
     ::GetCursorPos(&cursor_pos);
-    if (::WindowFromPoint(cursor_pos) != GetParent()) {
+
+    // WindowFromPoint returns the top-most HWND. As hwnd() may not
+    // respond with HTTRANSPARENT to a WM_NCHITTEST message,
+    // it may be returned.
+    HWND window_from_point = ::WindowFromPoint(cursor_pos);
+    if (window_from_point != hwnd() && window_from_point != GetParent()) {
       bool msg_handled = false;
       ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
           message, w_param, l_param, &msg_handled);
diff --git a/content/browser/renderer_host/media/audio_output_authorization_handler.cc b/content/browser/renderer_host/media/audio_output_authorization_handler.cc
index 04e2fdb..21f3b537 100644
--- a/content/browser/renderer_host/media/audio_output_authorization_handler.cc
+++ b/content/browser/renderer_host/media/audio_output_authorization_handler.cc
@@ -54,11 +54,11 @@
     return;
   }
 
-  std::move(cb).Run(
-      std::move(salt_and_origin.device_id_salt),
-      std::move(salt_and_origin.origin),
-      MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
-          MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, render_process_id, render_frame_id));
+  std::move(cb).Run(std::move(salt_and_origin.device_id_salt),
+                    std::move(salt_and_origin.origin),
+                    MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
+                        blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT,
+                        render_process_id, render_frame_id));
 }
 
 }  // namespace
@@ -252,7 +252,7 @@
   }
 
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   media_stream_manager_->media_devices_manager()->EnumerateDevices(
       devices_to_enumerate,
       base::BindOnce(&AudioOutputAuthorizationHandler::TranslateDeviceID,
@@ -271,8 +271,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!media::AudioDeviceDescription::IsDefaultDevice(device_id));
 
-  for (const MediaDeviceInfo& device_info :
-       enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
+  for (const blink::WebMediaDeviceInfo& device_info :
+       enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
     if (DoesMediaDeviceIDMatchHMAC(salt, security_origin, device_id,
                                    device_info.device_id)) {
       GetDeviceParameters(std::move(trace_scope), std::move(cb),
diff --git a/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc b/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
index 33e7daf..f75d5e1 100644
--- a/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
+++ b/content/browser/renderer_host/media/audio_output_authorization_handler_unittest.cc
@@ -166,18 +166,21 @@
   void GetRawNondefaultIdOnIOThread(std::string* out) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-    devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+    devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
 
     media_stream_manager_->media_devices_manager()->EnumerateDevices(
         devices_to_enumerate,
         base::BindOnce(
             [](std::string* out, const MediaDeviceEnumeration& result) {
               // Index 0 is default, so use 1.
-              CHECK(result[MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]
-                        .size() > 1)
+              CHECK(
+                  result[blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]
+                      .size() > 1)
                   << "Expected to have a nondefault device.";
-              *out = result[MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT][1]
-                         .device_id;
+              *out =
+                  result[blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]
+                        [1]
+                            .device_id;
             },
             base::Unretained(out)));
   }
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index 78395c3..181c255 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -16,7 +16,6 @@
 #include "content/browser/media/media_devices_permission_checker.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -27,6 +26,7 @@
 #include "media/base/video_facing.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "url/origin.h"
 
@@ -111,9 +111,12 @@
   }
 
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = request_audio_input;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = request_video_input;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = request_audio_output;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      request_audio_input;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
+      request_video_input;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      request_audio_output;
 
   media_stream_manager_->media_devices_manager()->EnumerateDevices(
       render_process_id_, render_frame_id_, devices_to_enumerate,
@@ -176,9 +179,12 @@
   }
 
   MediaDevicesManager::BoolDeviceTypes devices_to_subscribe;
-  devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = subscribe_audio_input;
-  devices_to_subscribe[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = subscribe_video_input;
-  devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = subscribe_audio_output;
+  devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      subscribe_audio_input;
+  devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
+      subscribe_video_input;
+  devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      subscribe_audio_output;
 
   uint32_t subscription_id = media_stream_manager_->media_devices_manager()
                                  ->SubscribeDeviceChangeNotifications(
@@ -192,7 +198,8 @@
     MediaDeviceSaltAndOrigin salt_and_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   GetDefaultMediaDeviceID(
-      MEDIA_DEVICE_TYPE_VIDEO_INPUT, render_process_id_, render_frame_id_,
+      blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, render_process_id_,
+      render_frame_id_,
       base::Bind(&MediaDevicesDispatcherHost::GotDefaultVideoInputDeviceID,
                  weak_factory_.GetWeakPtr(), base::Passed(&client_callback),
                  std::move(salt_and_origin)));
@@ -206,7 +213,7 @@
   MediaDevicesManager::BoolDeviceTypes requested_types;
   // Also request audio devices to make sure the heuristic to determine
   // the video group ID works.
-  requested_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  requested_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   media_stream_manager_->media_devices_manager()->EnumerateDevices(
       requested_types,
       base::BindOnce(
@@ -223,7 +230,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr>
       video_input_capabilities;
-  for (const auto& device_info : enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
+  for (const auto& device_info :
+       enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
     std::string hmac_device_id =
         GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
                                 salt_and_origin.origin, device_info.device_id);
@@ -318,7 +326,8 @@
 
   DCHECK(current_audio_input_capabilities_.empty());
   GetDefaultMediaDeviceID(
-      MEDIA_DEVICE_TYPE_AUDIO_INPUT, render_process_id_, render_frame_id_,
+      blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT, render_process_id_,
+      render_frame_id_,
       base::Bind(&MediaDevicesDispatcherHost::GotDefaultAudioInputDeviceID,
                  weak_factory_.GetWeakPtr()));
 }
@@ -329,7 +338,7 @@
   DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U);
   DCHECK(current_audio_input_capabilities_.empty());
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
   media_stream_manager_->media_devices_manager()->EnumerateDevices(
       devices_to_enumerate,
       base::BindOnce(&MediaDevicesDispatcherHost::GotAudioInputEnumeration,
@@ -343,7 +352,8 @@
   DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U);
   DCHECK(current_audio_input_capabilities_.empty());
   DCHECK_EQ(num_pending_audio_input_parameters_, 0U);
-  for (const auto& device_info : enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
+  for (const auto& device_info :
+       enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
     blink::mojom::AudioInputDeviceCapabilities capabilities(
         device_info.device_id, device_info.group_id,
         media::AudioParameters::UnavailableDeviceParams());
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.h b/content/browser/renderer_host/media/media_devices_dispatcher_host.h
index f112b46..c2525bef 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.h
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.h
@@ -15,7 +15,7 @@
 #include "content/browser/renderer_host/media/media_devices_manager.h"
 #include "content/common/content_export.h"
 #include "media/capture/video/video_capture_device_descriptor.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
index 1d70504..9d67bfd 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
@@ -68,7 +68,8 @@
   MockMediaDevicesListener() {}
 
   MOCK_METHOD2(OnDevicesChanged,
-               void(MediaDeviceType, const MediaDeviceInfoArray&));
+               void(blink::MediaDeviceType,
+                    const blink::WebMediaDeviceInfoArray&));
 
   blink::mojom::MediaDevicesListenerPtr CreateInterfacePtrAndBind() {
     blink::mojom::MediaDevicesListenerPtr listener;
@@ -144,24 +145,29 @@
 
     base::RunLoop run_loop;
     MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-    devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-    devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
-    devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+    devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+    devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+    devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
     media_stream_manager_->media_devices_manager()->EnumerateDevices(
         devices_to_enumerate,
         base::BindOnce(&PhysicalDevicesEnumerated, run_loop.QuitClosure(),
                        &physical_devices_));
     run_loop.Run();
 
-    ASSERT_GT(physical_devices_[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size(), 0u);
-    ASSERT_GT(physical_devices_[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size(), 0u);
-    ASSERT_GT(physical_devices_[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size(), 0u);
+    ASSERT_GT(physical_devices_[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size(),
+              0u);
+    ASSERT_GT(physical_devices_[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size(),
+              0u);
+    ASSERT_GT(physical_devices_[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size(),
+              0u);
   }
 
-  MOCK_METHOD1(UniqueOriginCallback,
-               void(const std::vector<std::vector<MediaDeviceInfo>>&));
-  MOCK_METHOD1(ValidOriginCallback,
-               void(const std::vector<std::vector<MediaDeviceInfo>>&));
+  MOCK_METHOD1(
+      UniqueOriginCallback,
+      void(const std::vector<std::vector<blink::WebMediaDeviceInfo>>&));
+  MOCK_METHOD1(
+      ValidOriginCallback,
+      void(const std::vector<std::vector<blink::WebMediaDeviceInfo>>&));
   MOCK_METHOD0(MockVideoInputCapabilitiesCallback, void());
   MOCK_METHOD0(MockAudioInputCapabilitiesCallback, void());
   MOCK_METHOD0(MockAllVideoInputDeviceFormatsCallback, void());
@@ -233,7 +239,7 @@
  protected:
   void DevicesEnumerated(
       const base::Closure& closure,
-      const std::vector<std::vector<MediaDeviceInfo>>& devices,
+      const std::vector<std::vector<blink::WebMediaDeviceInfo>>& devices,
       std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr>
           video_input_capabilities) {
     enumerated_devices_ = devices;
@@ -257,19 +263,22 @@
 
     ASSERT_FALSE(enumerated_devices_.empty());
     if (enumerate_audio_input)
-      EXPECT_FALSE(enumerated_devices_[MEDIA_DEVICE_TYPE_AUDIO_INPUT].empty());
+      EXPECT_FALSE(
+          enumerated_devices_[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].empty());
     if (enumerate_video_input)
-      EXPECT_FALSE(enumerated_devices_[MEDIA_DEVICE_TYPE_VIDEO_INPUT].empty());
+      EXPECT_FALSE(
+          enumerated_devices_[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].empty());
     if (enumerate_audio_output)
-      EXPECT_FALSE(enumerated_devices_[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].empty());
+      EXPECT_FALSE(
+          enumerated_devices_[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].empty());
 
     EXPECT_FALSE(DoesContainRawIds(enumerated_devices_));
     EXPECT_TRUE(DoesEveryDeviceMapToRawId(enumerated_devices_, origin_));
   }
 
   bool DoesContainRawIds(
-      const std::vector<std::vector<MediaDeviceInfo>>& enumeration) {
-    for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+      const std::vector<std::vector<blink::WebMediaDeviceInfo>>& enumeration) {
+    for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
       for (const auto& device_info : enumeration[i]) {
         for (const auto& raw_device_info : physical_devices_[i]) {
           // Skip default and communications audio devices, whose IDs are not
@@ -289,9 +298,9 @@
   }
 
   bool DoesEveryDeviceMapToRawId(
-      const std::vector<std::vector<MediaDeviceInfo>>& enumeration,
+      const std::vector<std::vector<blink::WebMediaDeviceInfo>>& enumeration,
       const url::Origin& origin) {
-    for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+    for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
       for (const auto& device_info : enumeration[i]) {
         bool found_match = false;
         for (const auto& raw_device_info : physical_devices_[i]) {
@@ -310,7 +319,7 @@
   }
 
   // Returns true if all devices have labels, false otherwise.
-  bool DoesContainLabels(const MediaDeviceInfoArray& device_infos) {
+  bool DoesContainLabels(const blink::WebMediaDeviceInfoArray& device_infos) {
     for (const auto& device_info : device_infos) {
       if (device_info.label.empty())
         return false;
@@ -319,7 +328,8 @@
   }
 
   // Returns true if all devices have labels, false otherwise.
-  bool DoesContainLabels(const std::vector<MediaDeviceInfoArray>& enumeration) {
+  bool DoesContainLabels(
+      const std::vector<blink::WebMediaDeviceInfoArray>& enumeration) {
     for (const auto& device_infos : enumeration) {
       if (!DoesContainLabels(device_infos))
         return false;
@@ -328,7 +338,8 @@
   }
 
   // Returns true if no devices have labels, false otherwise.
-  bool DoesNotContainLabels(const MediaDeviceInfoArray& device_infos) {
+  bool DoesNotContainLabels(
+      const blink::WebMediaDeviceInfoArray& device_infos) {
     for (const auto& device_info : device_infos) {
       if (!device_info.label.empty())
         return false;
@@ -338,7 +349,7 @@
 
   // Returns true if no devices have labels, false otherwise.
   bool DoesNotContainLabels(
-      const std::vector<std::vector<MediaDeviceInfo>>& enumeration) {
+      const std::vector<std::vector<blink::WebMediaDeviceInfo>>& enumeration) {
     for (const auto& device_infos : enumeration) {
       if (!DoesNotContainLabels(device_infos))
         return false;
@@ -350,14 +361,14 @@
     media_stream_manager_->media_devices_manager()->SetPermissionChecker(
         std::make_unique<MediaDevicesPermissionChecker>(has_permission));
     MockMediaDevicesListener device_change_listener;
-    for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
-      MediaDeviceType type = static_cast<MediaDeviceType>(i);
+    for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
+      blink::MediaDeviceType type = static_cast<blink::MediaDeviceType>(i);
       host_->AddMediaDevicesListener(
-          type == MEDIA_DEVICE_TYPE_AUDIO_INPUT,
-          type == MEDIA_DEVICE_TYPE_VIDEO_INPUT,
-          type == MEDIA_DEVICE_TYPE_AUDIO_OUTPUT,
+          type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT,
+          type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT,
+          type == blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT,
           device_change_listener.CreateInterfacePtrAndBind());
-      MediaDeviceInfoArray changed_devices;
+      blink::WebMediaDeviceInfoArray changed_devices;
       EXPECT_CALL(device_change_listener, OnDevicesChanged(type, _))
           .WillRepeatedly(SaveArg<1>(&changed_devices));
 
@@ -396,7 +407,7 @@
   MediaDeviceEnumeration physical_devices_;
   url::Origin origin_;
 
-  std::vector<MediaDeviceInfoArray> enumerated_devices_;
+  std::vector<blink::WebMediaDeviceInfoArray> enumerated_devices_;
 };
 
 TEST_P(MediaDevicesDispatcherHostTest, EnumerateAudioInputDevices) {
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc
index 92b5599..de7a5a5c 100644
--- a/content/browser/renderer_host/media/media_devices_manager.cc
+++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -62,8 +62,9 @@
 
 // Private helper method to generate a string for the log message that lists the
 // human readable names of |devices|.
-std::string GetLogMessageString(MediaDeviceType device_type,
-                                const MediaDeviceInfoArray& device_infos) {
+std::string GetLogMessageString(
+    blink::MediaDeviceType device_type,
+    const blink::WebMediaDeviceInfoArray& device_infos) {
   std::string output_string =
       base::StringPrintf("Getting devices of type %d:\n", device_type);
   if (device_infos.empty())
@@ -73,8 +74,8 @@
   return output_string;
 }
 
-MediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
-  MediaDeviceInfoArray result;
+blink::WebMediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
+  blink::WebMediaDeviceInfoArray result;
   if (is_input) {
     result.emplace_back(media::AudioDeviceDescription::kDefaultDeviceId,
                         "Fake Default Audio Input",
@@ -122,8 +123,8 @@
          !media::AudioDeviceDescription::IsCommunicationsDevice(device_id);
 }
 
-static bool EqualDeviceAndGroupID(const MediaDeviceInfo& lhs,
-                                  const MediaDeviceInfo& rhs) {
+static bool EqualDeviceAndGroupID(const blink::WebMediaDeviceInfo& lhs,
+                                  const blink::WebMediaDeviceInfo& rhs) {
   return lhs == rhs && lhs.group_id == rhs.group_id;
 }
 
@@ -136,8 +137,8 @@
 
 }  // namespace
 
-std::string GuessVideoGroupID(const MediaDeviceInfoArray& audio_infos,
-                              const MediaDeviceInfo& video_info) {
+std::string GuessVideoGroupID(const blink::WebMediaDeviceInfoArray& audio_infos,
+                              const blink::WebMediaDeviceInfo& video_info) {
   const std::string video_label = VideoLabelWithoutModelID(video_info.label);
 
   // If |video_label| is very small, do not guess in order to avoid false
@@ -145,10 +146,10 @@
   if (video_label.size() <= 3)
     return video_info.device_id;
 
-  base::RepeatingCallback<bool(const MediaDeviceInfo&)>
+  base::RepeatingCallback<bool(const blink::WebMediaDeviceInfo&)>
       video_label_is_included_in_audio_label = base::BindRepeating(
           [](const std::string& video_label,
-             const MediaDeviceInfo& audio_info) {
+             const blink::WebMediaDeviceInfo& audio_info) {
             return audio_info.label.find(video_label) != std::string::npos;
           },
           base::ConstRef(video_label));
@@ -157,10 +158,10 @@
   std::string video_usb_model = video_has_usb_model
                                     ? GetUSBModelFromLabel(video_info.label)
                                     : std::string();
-  base::RepeatingCallback<bool(const MediaDeviceInfo&)> usb_model_matches =
-      base::BindRepeating(
+  base::RepeatingCallback<bool(const blink::WebMediaDeviceInfo&)>
+      usb_model_matches = base::BindRepeating(
           [](bool video_has_usb_model, const std::string& video_usb_model,
-             const MediaDeviceInfo& audio_info) {
+             const blink::WebMediaDeviceInfo& audio_info) {
             return video_has_usb_model && LabelHasUSBModel(audio_info.label)
                        ? video_usb_model ==
                              GetUSBModelFromLabel(audio_info.label)
@@ -173,10 +174,11 @@
     // The label for the default and communication audio devices may contain the
     // same label as the real devices, so they should be ignored when trying to
     // find unique matches.
-    auto real_device_matches = [callback](const MediaDeviceInfo& audio_info) {
-      return IsRealAudioDeviceID(audio_info.device_id) &&
-             (*callback).Run(audio_info);
-    };
+    auto real_device_matches =
+        [callback](const blink::WebMediaDeviceInfo& audio_info) {
+          return IsRealAudioDeviceID(audio_info.device_id) &&
+                 (*callback).Run(audio_info);
+        };
     auto it_first = std::find_if(audio_infos.begin(), audio_infos.end(),
                                  real_device_matches);
     if (it_first == audio_infos.end())
@@ -380,7 +382,7 @@
       stop_removed_input_device_cb_(std::move(stop_removed_input_device_cb)),
       ui_input_device_change_cb_(std::move(ui_input_device_change_cb)),
       permission_checker_(std::make_unique<MediaDevicesPermissionChecker>()),
-      cache_infos_(NUM_MEDIA_DEVICE_TYPES),
+      cache_infos_(blink::NUM_MEDIA_DEVICE_TYPES),
       monitoring_started_(false),
       salt_and_origin_callback_(
           base::BindRepeating(&GetMediaDeviceSaltAndOrigin)),
@@ -406,10 +408,10 @@
 
   requests_.emplace_back(requested_types, std::move(callback));
   bool all_results_cached = true;
-  for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+  for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
     if (requested_types[i] && cache_policies_[i] == CachePolicy::NO_CACHE) {
       all_results_cached = false;
-      DoEnumerateDevices(static_cast<MediaDeviceType>(i));
+      DoEnumerateDevices(static_cast<blink::MediaDeviceType>(i));
     }
   }
 
@@ -463,7 +465,7 @@
   subscriptions_.erase(subscription_id);
 }
 
-void MediaDevicesManager::SetCachePolicy(MediaDeviceType type,
+void MediaDevicesManager::SetCachePolicy(blink::MediaDeviceType type,
                                          CachePolicy policy) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
@@ -512,9 +514,9 @@
   base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
 
   if (base::FeatureList::IsEnabled(features::kMediaDevicesSystemMonitorCache)) {
-    for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+    for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
       DCHECK(cache_policies_[i] != CachePolicy::SYSTEM_MONITOR);
-      SetCachePolicy(static_cast<MediaDeviceType>(i),
+      SetCachePolicy(static_cast<blink::MediaDeviceType>(i),
                      CachePolicy::SYSTEM_MONITOR);
     }
   }
@@ -544,8 +546,9 @@
   base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
   audio_service_device_listener_.reset();
   monitoring_started_ = false;
-  for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i)
-    SetCachePolicy(static_cast<MediaDeviceType>(i), CachePolicy::NO_CACHE);
+  for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i)
+    SetCachePolicy(static_cast<blink::MediaDeviceType>(i),
+                   CachePolicy::NO_CACHE);
 }
 
 bool MediaDevicesManager::IsMonitoringStarted() {
@@ -558,11 +561,11 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   switch (device_type) {
     case base::SystemMonitor::DEVTYPE_AUDIO:
-      HandleDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_INPUT);
-      HandleDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
+      HandleDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT);
+      HandleDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
       break;
     case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
-      HandleDevicesChanged(MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+      HandleDevicesChanged(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
       break;
     default:
       break;  // Uninteresting device change.
@@ -608,8 +611,8 @@
   return formats;
 }
 
-MediaDeviceInfoArray MediaDevicesManager::GetCachedDeviceInfo(
-    MediaDeviceType type) {
+blink::WebMediaDeviceInfoArray MediaDevicesManager::GetCachedDeviceInfo(
+    blink::MediaDeviceType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   return current_snapshot_[type];
 }
@@ -657,13 +660,13 @@
   // TODO(crbug.com/627793): Remove |internal_requested_types| and use
   // |requested_types| directly when video capture supports group IDs.
   BoolDeviceTypes internal_requested_types;
-  internal_requested_types[MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
-      requested_types[MEDIA_DEVICE_TYPE_AUDIO_INPUT] ||
-      requested_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT];
-  internal_requested_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
-      requested_types[MEDIA_DEVICE_TYPE_VIDEO_INPUT];
-  internal_requested_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
-      requested_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT];
+  internal_requested_types[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      requested_types[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] ||
+      requested_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT];
+  internal_requested_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
+      requested_types[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT];
+  internal_requested_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      requested_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT];
 
   EnumerateDevices(
       internal_requested_types,
@@ -682,11 +685,12 @@
     const MediaDeviceEnumeration& enumeration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   const bool video_input_capabilities_requested =
-      has_permissions[MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
+      has_permissions[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] &&
       request_video_input_capabilities;
 
-  std::vector<MediaDeviceInfoArray> result(NUM_MEDIA_DEVICE_TYPES);
-  for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+  std::vector<blink::WebMediaDeviceInfoArray> result(
+      blink::NUM_MEDIA_DEVICE_TYPES);
+  for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
     if (!requested_types[i])
       continue;
 
@@ -696,18 +700,18 @@
     }
   }
 
-  std::move(callback).Run(result,
-                          video_input_capabilities_requested
-                              ? ComputeVideoInputCapabilities(
-                                    enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT],
-                                    result[MEDIA_DEVICE_TYPE_VIDEO_INPUT])
-                              : std::vector<VideoInputDeviceCapabilitiesPtr>());
+  std::move(callback).Run(
+      result, video_input_capabilities_requested
+                  ? ComputeVideoInputCapabilities(
+                        enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT],
+                        result[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT])
+                  : std::vector<VideoInputDeviceCapabilitiesPtr>());
 }
 
 std::vector<VideoInputDeviceCapabilitiesPtr>
 MediaDevicesManager::ComputeVideoInputCapabilities(
-    const MediaDeviceInfoArray& raw_device_infos,
-    const MediaDeviceInfoArray& translated_device_infos) {
+    const blink::WebMediaDeviceInfoArray& raw_device_infos,
+    const blink::WebMediaDeviceInfoArray& translated_device_infos) {
   DCHECK_EQ(raw_device_infos.size(), translated_device_infos.size());
   std::vector<VideoInputDeviceCapabilitiesPtr> video_input_capabilities;
   for (size_t i = 0; i < raw_device_infos.size(); ++i) {
@@ -722,7 +726,7 @@
   return video_input_capabilities;
 }
 
-void MediaDevicesManager::DoEnumerateDevices(MediaDeviceType type) {
+void MediaDevicesManager::DoEnumerateDevices(blink::MediaDeviceType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
   CacheInfo& cache_info = cache_infos_[type];
@@ -731,15 +735,15 @@
 
   cache_info.UpdateStarted();
   switch (type) {
-    case MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
       EnumerateAudioDevices(true /* is_input */);
       break;
-    case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       video_capture_manager_->EnumerateDevices(
           base::BindOnce(&MediaDevicesManager::VideoInputDevicesEnumerated,
                          weak_factory_.GetWeakPtr()));
       break;
-    case MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
       EnumerateAudioDevices(false /* is_input */);
       break;
     default:
@@ -749,8 +753,9 @@
 
 void MediaDevicesManager::EnumerateAudioDevices(bool is_input) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  MediaDeviceType type =
-      is_input ? MEDIA_DEVICE_TYPE_AUDIO_INPUT : MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
+  blink::MediaDeviceType type = is_input
+                                    ? blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT
+                                    : blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
   if (use_fake_devices_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(&MediaDevicesManager::DevicesEnumerated,
@@ -767,28 +772,30 @@
 void MediaDevicesManager::VideoInputDevicesEnumerated(
     const media::VideoCaptureDeviceDescriptors& descriptors) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  MediaDeviceInfoArray snapshot;
+  blink::WebMediaDeviceInfoArray snapshot;
   for (const auto& descriptor : descriptors) {
     snapshot.emplace_back(descriptor);
   }
-  DevicesEnumerated(MEDIA_DEVICE_TYPE_VIDEO_INPUT, snapshot);
+  DevicesEnumerated(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, snapshot);
 }
 
 void MediaDevicesManager::AudioDevicesEnumerated(
-    MediaDeviceType type,
+    blink::MediaDeviceType type,
     media::AudioDeviceDescriptions device_descriptions) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  MediaDeviceInfoArray snapshot;
+  blink::WebMediaDeviceInfoArray snapshot;
   for (const media::AudioDeviceDescription& description : device_descriptions) {
-    snapshot.emplace_back(description);
+    snapshot.emplace_back(description.unique_id, description.device_name,
+                          description.group_id,
+                          media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE);
   }
   DevicesEnumerated(type, snapshot);
 }
 
 void MediaDevicesManager::DevicesEnumerated(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& snapshot) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& snapshot) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
   UpdateSnapshot(type, snapshot);
@@ -813,38 +820,39 @@
 }
 
 void MediaDevicesManager::UpdateSnapshot(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& new_snapshot,
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& new_snapshot,
     bool ignore_group_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
 
   bool need_update_device_change_subscribers = false;
-  MediaDeviceInfoArray& old_snapshot = current_snapshot_[type];
+  blink::WebMediaDeviceInfoArray& old_snapshot = current_snapshot_[type];
 
-  if (type == MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
-      type == MEDIA_DEVICE_TYPE_VIDEO_INPUT) {
+  if (type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
+      type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT) {
     MaybeStopRemovedInputDevices(type, new_snapshot);
   }
 
   // Update the cached snapshot and send notifications only if the device list
   // has changed.
   if (old_snapshot.size() != new_snapshot.size() ||
-      !std::equal(new_snapshot.begin(), new_snapshot.end(),
-                  old_snapshot.begin(),
-                  ignore_group_id
-                      ? [](const MediaDeviceInfo& lhs,
-                           const MediaDeviceInfo& rhs) { return lhs == rhs; }
-                      : EqualDeviceAndGroupID)) {
+      !std::equal(
+          new_snapshot.begin(), new_snapshot.end(), old_snapshot.begin(),
+          ignore_group_id
+              ? [](const blink::WebMediaDeviceInfo& lhs,
+                   const blink::WebMediaDeviceInfo& rhs) { return lhs == rhs; }
+              : EqualDeviceAndGroupID)) {
     // Prevent sending notifications until group IDs are updated using
     // a heuristic in ProcessRequests().
     // TODO(crbug.com/627793): Remove |is_video_with_group_ids| and the
     // corresponding checks when the video-capture subsystem supports
     // group IDs.
     bool is_video_with_good_group_ids =
-        type == MEDIA_DEVICE_TYPE_VIDEO_INPUT &&
+        type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT &&
         (new_snapshot.size() == 0 || !new_snapshot[0].group_id.empty());
-    if (type == MEDIA_DEVICE_TYPE_AUDIO_INPUT || is_video_with_good_group_ids)
+    if (type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
+        is_video_with_good_group_ids)
       ui_input_device_change_cb_.Run(type, new_snapshot);
 
     // Do not notify device-change subscribers after the first enumeration
@@ -852,7 +860,8 @@
     need_update_device_change_subscribers =
         has_seen_result_[type] &&
         (old_snapshot.size() != 0 || new_snapshot.size() != 0) &&
-        (type != MEDIA_DEVICE_TYPE_VIDEO_INPUT || is_video_with_good_group_ids);
+        (type != blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT ||
+         is_video_with_good_group_ids);
     current_snapshot_[type] = new_snapshot;
   }
 
@@ -866,14 +875,15 @@
   // for device coincidences with audio input devices.
   // TODO(crbug.com/627793): Remove this once the video-capture subsystem
   // supports group IDs.
-  if (has_seen_result_[MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
-    MediaDeviceInfoArray video_devices =
-        current_snapshot_[MEDIA_DEVICE_TYPE_VIDEO_INPUT];
+  if (has_seen_result_[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
+    blink::WebMediaDeviceInfoArray video_devices =
+        current_snapshot_[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT];
     for (auto& video_device_info : video_devices) {
       video_device_info.group_id = GuessVideoGroupID(
-          current_snapshot_[MEDIA_DEVICE_TYPE_AUDIO_INPUT], video_device_info);
+          current_snapshot_[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT],
+          video_device_info);
     }
-    UpdateSnapshot(MEDIA_DEVICE_TYPE_VIDEO_INPUT, video_devices,
+    UpdateSnapshot(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, video_devices,
                    false /* ignore_group_id */);
   }
 
@@ -893,7 +903,7 @@
     const EnumerationRequest& request_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   bool is_ready = true;
-  for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+  for (size_t i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
     if (!request_info.requested[i])
       continue;
     switch (cache_policies_[i]) {
@@ -912,7 +922,7 @@
   return is_ready;
 }
 
-void MediaDevicesManager::HandleDevicesChanged(MediaDeviceType type) {
+void MediaDevicesManager::HandleDevicesChanged(blink::MediaDeviceType type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
   cache_infos_[type].InvalidateCache();
@@ -920,17 +930,18 @@
 }
 
 void MediaDevicesManager::MaybeStopRemovedInputDevices(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& new_snapshot) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& new_snapshot) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(type == MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
-         type == MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  DCHECK(type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
+         type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
 
   for (const auto& old_device_info : current_snapshot_[type]) {
-    auto it = std::find_if(new_snapshot.begin(), new_snapshot.end(),
-                           [&old_device_info](const MediaDeviceInfo& info) {
-                             return info.device_id == old_device_info.device_id;
-                           });
+    auto it =
+        std::find_if(new_snapshot.begin(), new_snapshot.end(),
+                     [&old_device_info](const blink::WebMediaDeviceInfo& info) {
+                       return info.device_id == old_device_info.device_id;
+                     });
 
     // If a device was removed, notify the MediaStreamManager to stop all
     // streams using that device.
@@ -940,8 +951,8 @@
 }
 
 void MediaDevicesManager::NotifyDeviceChangeSubscribers(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& snapshot) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& snapshot) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(IsValidMediaDeviceType(type));
 
@@ -966,8 +977,8 @@
     uint32_t subscription_id,
     int render_process_id,
     int render_frame_id,
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& device_infos,
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& device_infos,
     MediaDeviceSaltAndOrigin salt_and_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   permission_checker_->CheckPermission(
@@ -979,8 +990,8 @@
 
 void MediaDevicesManager::NotifyDeviceChange(
     uint32_t subscription_id,
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& device_infos,
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& device_infos,
     const MediaDeviceSaltAndOrigin& salt_and_origin,
     bool has_permission) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
diff --git a/content/browser/renderer_host/media/media_devices_manager.h b/content/browser/renderer_host/media/media_devices_manager.h
index 2a4b6cf..1c0e8bd 100644
--- a/content/browser/renderer_host/media/media_devices_manager.h
+++ b/content/browser/renderer_host/media/media_devices_manager.h
@@ -18,11 +18,11 @@
 #include "base/system/system_monitor.h"
 #include "content/browser/media/media_devices_util.h"
 #include "content/common/content_export.h"
-#include "content/common/media/media_devices.h"
 #include "media/audio/audio_device_description.h"
 #include "media/capture/video/video_capture_device_descriptor.h"
 #include "media/capture/video_capture_types.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 
 using blink::mojom::VideoInputDeviceCapabilitiesPtr;
 
@@ -39,9 +39,9 @@
 class MediaDevicesPermissionChecker;
 class VideoCaptureManager;
 
-// Use MediaDeviceType values to index on this type.
+// Use blink::MediaDeviceType values to index on this type.
 using MediaDeviceEnumeration =
-    std::array<MediaDeviceInfoArray, NUM_MEDIA_DEVICE_TYPES>;
+    std::array<blink::WebMediaDeviceInfoArray, blink::NUM_MEDIA_DEVICE_TYPES>;
 
 // MediaDevicesManager is responsible for doing media-device enumerations.
 // In addition it implements caching for enumeration results and device
@@ -50,25 +50,25 @@
 class CONTENT_EXPORT MediaDevicesManager
     : public base::SystemMonitor::DevicesChangedObserver {
  public:
-  // Use MediaDeviceType values to index on this type. By default all device
-  // types are false.
+  // Use blink::MediaDeviceType values to index on this type. By default all
+  // device types are false.
   class BoolDeviceTypes final
-      : public std::array<bool, NUM_MEDIA_DEVICE_TYPES> {
+      : public std::array<bool, blink::NUM_MEDIA_DEVICE_TYPES> {
    public:
     BoolDeviceTypes() { fill(false); }
   };
 
   using EnumerationCallback =
       base::OnceCallback<void(const MediaDeviceEnumeration&)>;
-  using EnumerateDevicesCallback =
-      base::OnceCallback<void(const std::vector<MediaDeviceInfoArray>&,
-                              std::vector<VideoInputDeviceCapabilitiesPtr>)>;
-  using StopRemovedInputDeviceCallback =
-      base::RepeatingCallback<void(MediaDeviceType type,
-                                   const MediaDeviceInfo& media_device_info)>;
-  using UIInputDeviceChangeCallback =
-      base::RepeatingCallback<void(MediaDeviceType stream_type,
-                                   const MediaDeviceInfoArray& devices)>;
+  using EnumerateDevicesCallback = base::OnceCallback<void(
+      const std::vector<blink::WebMediaDeviceInfoArray>&,
+      std::vector<VideoInputDeviceCapabilitiesPtr>)>;
+  using StopRemovedInputDeviceCallback = base::RepeatingCallback<void(
+      blink::MediaDeviceType type,
+      const blink::WebMediaDeviceInfo& media_device_info)>;
+  using UIInputDeviceChangeCallback = base::RepeatingCallback<void(
+      blink::MediaDeviceType stream_type,
+      const blink::WebMediaDeviceInfoArray& devices)>;
 
   MediaDevicesManager(
       media::AudioSystem* audio_system,
@@ -134,7 +134,8 @@
   // TODO(guidou): Remove this function once content::GetMediaDeviceIDForHMAC
   // is rewritten to receive devices via a callback.
   // See http://crbug.com/648155.
-  MediaDeviceInfoArray GetCachedDeviceInfo(MediaDeviceType type);
+  blink::WebMediaDeviceInfoArray GetCachedDeviceInfo(
+      blink::MediaDeviceType type);
 
   MediaDevicesPermissionChecker* media_devices_permission_checker();
 
@@ -182,7 +183,7 @@
   };
 
   // Manually sets a caching policy for a given device type.
-  void SetCachePolicy(MediaDeviceType type, CachePolicy policy);
+  void SetCachePolicy(blink::MediaDeviceType type, CachePolicy policy);
 
   // Helpers to handle enumeration results for a renderer process.
   void CheckPermissionsForEnumerateDevices(
@@ -207,11 +208,11 @@
       const MediaDeviceEnumeration& enumeration);
 
   std::vector<VideoInputDeviceCapabilitiesPtr> ComputeVideoInputCapabilities(
-      const MediaDeviceInfoArray& raw_device_infos,
-      const MediaDeviceInfoArray& translated_device_infos);
+      const blink::WebMediaDeviceInfoArray& raw_device_infos,
+      const blink::WebMediaDeviceInfoArray& translated_device_infos);
 
   // Helpers to issue low-level device enumerations.
-  void DoEnumerateDevices(MediaDeviceType type);
+  void DoEnumerateDevices(blink::MediaDeviceType type);
   void EnumerateAudioDevices(bool is_input);
 
   // Callback for VideoCaptureManager::EnumerateDevices.
@@ -220,33 +221,36 @@
 
   // Callback for AudioSystem::GetDeviceDescriptions.
   void AudioDevicesEnumerated(
-      MediaDeviceType type,
+      blink::MediaDeviceType type,
       media::AudioDeviceDescriptions device_descriptions);
 
   // Helpers to handle enumeration results.
-  void DevicesEnumerated(MediaDeviceType type,
-                         const MediaDeviceInfoArray& snapshot);
-  void UpdateSnapshot(MediaDeviceType type,
-                      const MediaDeviceInfoArray& new_snapshot,
+  void DevicesEnumerated(blink::MediaDeviceType type,
+                         const blink::WebMediaDeviceInfoArray& snapshot);
+  void UpdateSnapshot(blink::MediaDeviceType type,
+                      const blink::WebMediaDeviceInfoArray& new_snapshot,
                       bool ignore_group_id = true);
   void ProcessRequests();
   bool IsEnumerationRequestReady(const EnumerationRequest& request_info);
 
   // Helpers to handle device-change notification.
-  void HandleDevicesChanged(MediaDeviceType type);
-  void MaybeStopRemovedInputDevices(MediaDeviceType type,
-                                    const MediaDeviceInfoArray& new_snapshot);
-  void NotifyDeviceChangeSubscribers(MediaDeviceType type,
-                                     const MediaDeviceInfoArray& snapshot);
-  void CheckPermissionForDeviceChange(uint32_t subscription_id,
-                                      int render_process_id,
-                                      int render_frame_id,
-                                      MediaDeviceType type,
-                                      const MediaDeviceInfoArray& device_infos,
-                                      MediaDeviceSaltAndOrigin salt_and_origin);
+  void HandleDevicesChanged(blink::MediaDeviceType type);
+  void MaybeStopRemovedInputDevices(
+      blink::MediaDeviceType type,
+      const blink::WebMediaDeviceInfoArray& new_snapshot);
+  void NotifyDeviceChangeSubscribers(
+      blink::MediaDeviceType type,
+      const blink::WebMediaDeviceInfoArray& snapshot);
+  void CheckPermissionForDeviceChange(
+      uint32_t subscription_id,
+      int render_process_id,
+      int render_frame_id,
+      blink::MediaDeviceType type,
+      const blink::WebMediaDeviceInfoArray& device_infos,
+      MediaDeviceSaltAndOrigin salt_and_origin);
   void NotifyDeviceChange(uint32_t subscription_id,
-                          MediaDeviceType type,
-                          const MediaDeviceInfoArray& device_infos,
+                          blink::MediaDeviceType type,
+                          const blink::WebMediaDeviceInfoArray& device_infos,
                           const MediaDeviceSaltAndOrigin& salt_and_origin,
                           bool has_permission);
 
@@ -262,7 +266,7 @@
 
   std::unique_ptr<MediaDevicesPermissionChecker> permission_checker_;
 
-  using CachePolicies = std::array<CachePolicy, NUM_MEDIA_DEVICE_TYPES>;
+  using CachePolicies = std::array<CachePolicy, blink::NUM_MEDIA_DEVICE_TYPES>;
   CachePolicies cache_policies_;
 
   class CacheInfo;
@@ -300,8 +304,8 @@
 // TODO(crbug.com/627793): Replace the heuristic with proper associations
 // provided by the OS.
 CONTENT_EXPORT std::string GuessVideoGroupID(
-    const MediaDeviceInfoArray& audio_infos,
-    const MediaDeviceInfo& video_info);
+    const blink::WebMediaDeviceInfoArray& audio_infos,
+    const blink::WebMediaDeviceInfo& video_info);
 
 }  // namespace content
 
diff --git a/content/browser/renderer_host/media/media_devices_manager_unittest.cc b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
index 6fd207b..29852c6d 100644
--- a/content/browser/renderer_host/media/media_devices_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
@@ -138,7 +138,8 @@
   MockMediaDevicesListener() {}
 
   MOCK_METHOD2(OnDevicesChanged,
-               void(MediaDeviceType, const MediaDeviceInfoArray&));
+               void(blink::MediaDeviceType,
+                    const blink::WebMediaDeviceInfoArray&));
 
   blink::mojom::MediaDevicesListenerPtr CreateInterfacePtrAndBind() {
     blink::mojom::MediaDevicesListenerPtr listener;
@@ -153,14 +154,15 @@
 class MockMediaDevicesManagerClient {
  public:
   MOCK_METHOD2(StopRemovedInputDevice,
-               void(MediaDeviceType type,
-                    const MediaDeviceInfo& media_device_info));
+               void(blink::MediaDeviceType type,
+                    const blink::WebMediaDeviceInfo& media_device_info));
   MOCK_METHOD2(InputDevicesChangedUI,
-               void(MediaDeviceType stream_type,
-                    const MediaDeviceInfoArray& devices));
+               void(blink::MediaDeviceType stream_type,
+                    const blink::WebMediaDeviceInfoArray& devices));
 };
 
-void VerifyDeviceAndGroupID(const std::vector<MediaDeviceInfoArray>& array) {
+void VerifyDeviceAndGroupID(
+    const std::vector<blink::WebMediaDeviceInfoArray>& array) {
   for (const auto& device_infos : array) {
     for (const auto& device_info : device_infos) {
       EXPECT_FALSE(device_info.device_id.empty());
@@ -182,7 +184,7 @@
 
   void EnumerateCallback(base::RunLoop* run_loop,
                          const MediaDeviceEnumeration& result) {
-    for (int i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
+    for (int i = 0; i < blink::NUM_MEDIA_DEVICE_TYPES; ++i) {
       for (const auto& device_info : result[i]) {
         EXPECT_FALSE(device_info.device_id.empty());
         EXPECT_FALSE(device_info.group_id.empty());
@@ -196,7 +198,7 @@
       const std::vector<media::FakeVideoCaptureDeviceSettings>&
           expected_video_capture_device_settings,
       base::RunLoop* run_loop,
-      const std::vector<MediaDeviceInfoArray>& devices,
+      const std::vector<blink::WebMediaDeviceInfoArray>& devices,
       std::vector<VideoInputDeviceCapabilitiesPtr> capabilities) {
     EXPECT_EQ(capabilities.size(),
               expected_video_capture_device_settings.size());
@@ -243,7 +245,7 @@
         std::make_unique<MediaDevicesPermissionChecker>(true));
   }
 
-  void EnableCache(MediaDeviceType type) {
+  void EnableCache(blink::MediaDeviceType type) {
     media_devices_manager_->SetCachePolicy(
         type, MediaDevicesManager::CachePolicy::SYSTEM_MONITOR);
   }
@@ -273,7 +275,7 @@
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   EXPECT_CALL(media_devices_manager_client_, InputDevicesChangedUI(_, _));
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -292,7 +294,7 @@
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   EXPECT_CALL(media_devices_manager_client_, InputDevicesChangedUI(_, _));
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -311,7 +313,7 @@
       .Times(kNumCalls);
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -330,8 +332,8 @@
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   EXPECT_CALL(media_devices_manager_client_, InputDevicesChangedUI(_, _));
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -349,11 +351,11 @@
   EXPECT_CALL(*audio_manager_, MockGetAudioOutputDeviceNames(_)).Times(1);
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   EXPECT_CALL(media_devices_manager_client_, InputDevicesChangedUI(_, _));
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_INPUT);
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -371,9 +373,9 @@
   EXPECT_CALL(*audio_manager_, MockGetAudioOutputDeviceNames(_)).Times(0);
   EXPECT_CALL(*this, MockCallback(_)).Times(kNumCalls);
   EXPECT_CALL(media_devices_manager_client_, InputDevicesChangedUI(_, _));
-  EnableCache(MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -398,11 +400,11 @@
   size_t num_audio_output_devices = 3;
   audio_manager_->SetNumAudioInputDevices(num_audio_input_devices);
   audio_manager_->SetNumAudioOutputDevices(num_audio_output_devices);
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_INPUT);
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -411,9 +413,9 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_audio_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
     EXPECT_EQ(num_audio_output_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
   }
 
   // Simulate removal of devices.
@@ -438,9 +440,9 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_audio_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
     EXPECT_EQ(num_audio_output_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
   }
 
   // Simulate addition of devices.
@@ -461,9 +463,9 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_audio_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
     EXPECT_EQ(num_audio_output_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
   }
 }
 
@@ -482,9 +484,9 @@
   size_t num_video_input_devices = 5;
   video_capture_device_factory_->SetToDefaultDevicesConfig(
       num_video_input_devices);
-  EnableCache(MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -493,7 +495,7 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_video_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
   }
 
   // Simulate addition of devices.
@@ -514,7 +516,7 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_video_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
   }
 
   // Simulate removal of devices.
@@ -539,7 +541,7 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_video_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
   }
 }
 
@@ -562,13 +564,13 @@
   video_capture_device_factory_->SetToDefaultDevicesConfig(
       num_video_input_devices);
   audio_manager_->SetNumAudioOutputDevices(num_audio_output_devices);
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_INPUT);
-  EnableCache(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
-  EnableCache(MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
+  EnableCache(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   for (int i = 0; i < kNumCalls; i++) {
     base::RunLoop run_loop;
     media_devices_manager_->EnumerateDevices(
@@ -577,11 +579,11 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_audio_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
     EXPECT_EQ(num_video_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
     EXPECT_EQ(num_audio_output_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
   }
 
   // Simulate device changes
@@ -615,11 +617,11 @@
                        base::Unretained(this), &run_loop));
     run_loop.Run();
     EXPECT_EQ(num_audio_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT].size());
     EXPECT_EQ(num_video_input_devices,
-              enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT].size());
     EXPECT_EQ(num_audio_output_devices,
-              enumeration[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
+              enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT].size());
   }
 }
 
@@ -643,9 +645,9 @@
   // configuration.
   EXPECT_CALL(*this, MockCallback(_));
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   base::RunLoop run_loop;
   media_devices_manager_->EnumerateDevices(
       devices_to_enumerate,
@@ -656,7 +658,7 @@
   // Add device-change event listeners.
   MockMediaDevicesListener listener_audio_input;
   MediaDevicesManager::BoolDeviceTypes audio_input_devices_to_subscribe;
-  audio_input_devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  audio_input_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
   uint32_t audio_input_subscription_id =
       media_devices_manager_->SubscribeDeviceChangeNotifications(
           kRenderProcessId, kRenderFrameId, audio_input_devices_to_subscribe,
@@ -664,7 +666,7 @@
 
   MockMediaDevicesListener listener_video_input;
   MediaDevicesManager::BoolDeviceTypes video_input_devices_to_subscribe;
-  video_input_devices_to_subscribe[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  video_input_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   uint32_t video_input_subscription_id =
       media_devices_manager_->SubscribeDeviceChangeNotifications(
           kRenderProcessId, kRenderFrameId, video_input_devices_to_subscribe,
@@ -672,7 +674,8 @@
 
   MockMediaDevicesListener listener_audio_output;
   MediaDevicesManager::BoolDeviceTypes audio_output_devices_to_subscribe;
-  audio_output_devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  audio_output_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] =
+      true;
   uint32_t audio_output_subscription_id =
       media_devices_manager_->SubscribeDeviceChangeNotifications(
           kRenderProcessId, kRenderFrameId, audio_output_devices_to_subscribe,
@@ -680,38 +683,41 @@
 
   MockMediaDevicesListener listener_all;
   MediaDevicesManager::BoolDeviceTypes all_devices_to_subscribe;
-  all_devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
-  all_devices_to_subscribe[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
-  all_devices_to_subscribe[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  all_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+  all_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  all_devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   media_devices_manager_->SubscribeDeviceChangeNotifications(
       kRenderProcessId, kRenderFrameId, all_devices_to_subscribe,
       listener_all.CreateInterfacePtrAndBind());
 
-  MediaDeviceInfoArray notification_audio_input;
-  MediaDeviceInfoArray notification_video_input;
-  MediaDeviceInfoArray notification_audio_output;
-  MediaDeviceInfoArray notification_all_audio_input;
-  MediaDeviceInfoArray notification_all_video_input;
-  MediaDeviceInfoArray notification_all_audio_output;
+  blink::WebMediaDeviceInfoArray notification_audio_input;
+  blink::WebMediaDeviceInfoArray notification_video_input;
+  blink::WebMediaDeviceInfoArray notification_audio_output;
+  blink::WebMediaDeviceInfoArray notification_all_audio_input;
+  blink::WebMediaDeviceInfoArray notification_all_video_input;
+  blink::WebMediaDeviceInfoArray notification_all_audio_output;
   EXPECT_CALL(listener_audio_input,
-              OnDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_INPUT, _))
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT, _))
       .Times(1)
       .WillOnce(SaveArg<1>(&notification_audio_input));
   EXPECT_CALL(listener_video_input,
-              OnDevicesChanged(MEDIA_DEVICE_TYPE_VIDEO_INPUT, _))
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, _))
       .Times(1)
       .WillOnce(SaveArg<1>(&notification_video_input));
   EXPECT_CALL(listener_audio_output,
-              OnDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _))
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _))
       .Times(1)
       .WillOnce(SaveArg<1>(&notification_audio_output));
-  EXPECT_CALL(listener_all, OnDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_INPUT, _))
+  EXPECT_CALL(listener_all,
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT, _))
       .Times(2)
       .WillRepeatedly(SaveArg<1>(&notification_all_audio_input));
-  EXPECT_CALL(listener_all, OnDevicesChanged(MEDIA_DEVICE_TYPE_VIDEO_INPUT, _))
+  EXPECT_CALL(listener_all,
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, _))
       .Times(2)
       .WillRepeatedly(SaveArg<1>(&notification_all_video_input));
-  EXPECT_CALL(listener_all, OnDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _))
+  EXPECT_CALL(listener_all,
+              OnDevicesChanged(blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, _))
       .Times(2)
       .WillRepeatedly(SaveArg<1>(&notification_all_audio_output));
 
@@ -792,10 +798,10 @@
   // Audio is enumerated due to heuristics to compute video group IDs.
   EXPECT_CALL(*audio_manager_, MockGetAudioInputDeviceNames(_));
   EXPECT_CALL(media_devices_manager_client_,
-              InputDevicesChangedUI(MEDIA_DEVICE_TYPE_AUDIO_INPUT, _));
+              InputDevicesChangedUI(blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT, _));
   EXPECT_CALL(*video_capture_device_factory_, MockGetDeviceDescriptors());
   EXPECT_CALL(media_devices_manager_client_,
-              InputDevicesChangedUI(MEDIA_DEVICE_TYPE_VIDEO_INPUT, _));
+              InputDevicesChangedUI(blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, _));
   // Configure fake devices with video formats different from the fallback
   // formats to make sure that expected capabilities are what devices actually
   // report.
@@ -821,7 +827,7 @@
       fake_capture_device_settings);
 
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
   base::RunLoop run_loop;
   media_devices_manager_->EnumerateDevices(
       -1, -1, devices_to_enumerate, true,
@@ -832,7 +838,7 @@
 }
 
 TEST_F(MediaDevicesManagerTest, GuessVideoGroupID) {
-  MediaDeviceInfoArray audio_devices = {
+  blink::WebMediaDeviceInfoArray audio_devices = {
       {media::AudioDeviceDescription::kDefaultDeviceId,
        "Default - Unique USB Mic (1234:5678)", "group_1"},
       {media::AudioDeviceDescription::kCommunicationsDeviceId,
@@ -860,33 +866,33 @@
        "communications - Unique USB Mic (1234:5678)", "group_1"},
   };
 
-  MediaDeviceInfo logitech_video("logitech_video",
-                                 "Logitech Webcam C930e (046d:0843)", "");
-  MediaDeviceInfo hd_pro_video("hd_pro_video", "HD Pro Webcam C920 (046d:082d)",
-                               "");
-  MediaDeviceInfo lifecam_video(
+  blink::WebMediaDeviceInfo logitech_video(
+      "logitech_video", "Logitech Webcam C930e (046d:0843)", "");
+  blink::WebMediaDeviceInfo hd_pro_video("hd_pro_video",
+                                         "HD Pro Webcam C920 (046d:082d)", "");
+  blink::WebMediaDeviceInfo lifecam_video(
       "lifecam_video", "Microsoft® LifeCam Cinema(TM) (045e:075d)", "");
-  MediaDeviceInfo repeated_webcam1_video("repeated_webcam_1", "Repeated webcam",
-                                         "");
-  MediaDeviceInfo repeated_webcam2_video("repeated_webcam_2", "Repeated webcam",
-                                         "");
-  MediaDeviceInfo dual_mic_video("dual_mic_video",
-                                 "Dual-mic webcam device (1111:1111)", "");
-  MediaDeviceInfo webcam_only_video("webcam_only_video", "Webcam-only device",
-                                    "");
-  MediaDeviceInfo repeated_dual_mic1_video(
+  blink::WebMediaDeviceInfo repeated_webcam1_video("repeated_webcam_1",
+                                                   "Repeated webcam", "");
+  blink::WebMediaDeviceInfo repeated_webcam2_video("repeated_webcam_2",
+                                                   "Repeated webcam", "");
+  blink::WebMediaDeviceInfo dual_mic_video(
+      "dual_mic_video", "Dual-mic webcam device (1111:1111)", "");
+  blink::WebMediaDeviceInfo webcam_only_video("webcam_only_video",
+                                              "Webcam-only device", "");
+  blink::WebMediaDeviceInfo repeated_dual_mic1_video(
       "repeated_dual_mic1_video", "Repeated dual-mic webcam device", "");
-  MediaDeviceInfo repeated_dual_mic2_video(
+  blink::WebMediaDeviceInfo repeated_dual_mic2_video(
       "repeated_dual_mic2_video", "Repeated dual-mic webcam device", "");
-  MediaDeviceInfo short_label_video("short_label_video", " ()", "");
-  MediaDeviceInfo unique_usb_video("unique_usb_video",
-                                   "Unique USB webcam (1234:5678)", "");
-  MediaDeviceInfo another_unique_usb_video(
+  blink::WebMediaDeviceInfo short_label_video("short_label_video", " ()", "");
+  blink::WebMediaDeviceInfo unique_usb_video(
+      "unique_usb_video", "Unique USB webcam (1234:5678)", "");
+  blink::WebMediaDeviceInfo another_unique_usb_video(
       "another_unique_usb_video", "Another Unique USB webcam (5678:9abc)", "");
-  MediaDeviceInfo repeated_usb1_video("repeated_usb1_video",
-                                      "Repeated USB webcam (8765:4321)", "");
-  MediaDeviceInfo repeated_usb2_video("repeated_usb2_video",
-                                      "Repeated USB webcam (8765:4321)", "");
+  blink::WebMediaDeviceInfo repeated_usb1_video(
+      "repeated_usb1_video", "Repeated USB webcam (8765:4321)", "");
+  blink::WebMediaDeviceInfo repeated_usb2_video(
+      "repeated_usb2_video", "Repeated USB webcam (8765:4321)", "");
 
   EXPECT_EQ(GuessVideoGroupID(audio_devices, logitech_video), "group_1");
   EXPECT_EQ(GuessVideoGroupID(audio_devices, hd_pro_video), "group_2");
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
index bc7e464..967b1fec 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc
@@ -70,7 +70,7 @@
 void AudioInputDevicesEnumerated(base::Closure quit_closure,
                                  media::AudioDeviceDescriptions* out,
                                  const MediaDeviceEnumeration& enumeration) {
-  for (const auto& info : enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
+  for (const auto& info : enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
     out->emplace_back(info.label, info.device_id, info.group_id);
   }
   std::move(quit_closure).Run();
@@ -304,7 +304,7 @@
 
     base::RunLoop run_loop;
     MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-    devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
+    devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] = true;
     media_stream_manager_->media_devices_manager()->EnumerateDevices(
         devices_to_enumerate,
         base::BindOnce(&AudioInputDevicesEnumerated, run_loop.QuitClosure(),
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 440ddf2..59b22af 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -146,7 +146,7 @@
 bool GetDeviceIDFromHMAC(const std::string& salt,
                          const url::Origin& security_origin,
                          const std::string& hmac_device_id,
-                         const MediaDeviceInfoArray& devices,
+                         const blink::WebMediaDeviceInfoArray& devices,
                          std::string* device_id) {
   // The source_id can be empty if the constraint is set but empty.
   if (hmac_device_id.empty())
@@ -162,11 +162,11 @@
   return false;
 }
 
-MediaStreamType ConvertToMediaStreamType(MediaDeviceType type) {
+MediaStreamType ConvertToMediaStreamType(blink::MediaDeviceType type) {
   switch (type) {
-    case MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
       return blink::MEDIA_DEVICE_AUDIO_CAPTURE;
-    case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       return blink::MEDIA_DEVICE_VIDEO_CAPTURE;
     default:
       NOTREACHED();
@@ -175,17 +175,17 @@
   return blink::MEDIA_NO_SERVICE;
 }
 
-MediaDeviceType ConvertToMediaDeviceType(MediaStreamType stream_type) {
+blink::MediaDeviceType ConvertToMediaDeviceType(MediaStreamType stream_type) {
   switch (stream_type) {
     case blink::MEDIA_DEVICE_AUDIO_CAPTURE:
-      return MEDIA_DEVICE_TYPE_AUDIO_INPUT;
+      return blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
     case blink::MEDIA_DEVICE_VIDEO_CAPTURE:
-      return MEDIA_DEVICE_TYPE_VIDEO_INPUT;
+      return blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
     default:
       NOTREACHED();
   }
 
-  return NUM_MEDIA_DEVICE_TYPES;
+  return blink::NUM_MEDIA_DEVICE_TYPES;
 }
 
 void SendVideoCaptureLogMessage(const std::string& message) {
@@ -922,7 +922,7 @@
 
   // TODO(guidou): Change to use MediaDevicesManager::EnumerateDevices.
   // See http://crbug.com/648155.
-  MediaDeviceInfoArray cached_devices =
+  blink::WebMediaDeviceInfoArray cached_devices =
       media_devices_manager_->GetCachedDeviceInfo(
           ConvertToMediaDeviceType(stream_type));
   return GetDeviceIDFromHMAC(salt, security_origin, source_id, cached_devices,
@@ -935,11 +935,11 @@
 }
 
 void MediaStreamManager::StopRemovedDevice(
-    MediaDeviceType type,
-    const MediaDeviceInfo& media_device_info) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfo& media_device_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(type == MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
-         type == MEDIA_DEVICE_TYPE_VIDEO_INPUT);
+  DCHECK(type == blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
+         type == blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT);
 
   MediaStreamType stream_type = ConvertToMediaStreamType(type);
   std::vector<int> session_ids;
@@ -972,7 +972,7 @@
 bool MediaStreamManager::PickDeviceId(
     const MediaDeviceSaltAndOrigin& salt_and_origin,
     const TrackControls& controls,
-    const MediaDeviceInfoArray& devices,
+    const blink::WebMediaDeviceInfoArray& devices,
     std::string* device_id) const {
   if (controls.device_id.empty())
     return true;
@@ -989,7 +989,7 @@
 bool MediaStreamManager::GetRequestedDeviceCaptureId(
     const DeviceRequest* request,
     MediaStreamType type,
-    const MediaDeviceInfoArray& devices,
+    const blink::WebMediaDeviceInfoArray& devices,
     std::string* device_id) const {
   if (type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) {
     return PickDeviceId(request->salt_and_origin, request->controls.audio,
@@ -1039,8 +1039,10 @@
   // UI thread, after the IO thread has been stopped.
   DCHECK(request_audio_input || request_video_input);
   MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_INPUT] = request_audio_input;
-  devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = request_video_input;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
+      request_audio_input;
+  devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
+      request_video_input;
   media_devices_manager_->EnumerateDevices(
       devices_to_enumerate,
       base::BindOnce(&MediaStreamManager::DevicesEnumerated,
@@ -1153,9 +1155,11 @@
           label));
     } else {
       MediaStreamDevices audio_devices = ConvertToMediaStreamDevices(
-          request->audio_type(), enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT]);
+          request->audio_type(),
+          enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT]);
       MediaStreamDevices video_devices = ConvertToMediaStreamDevices(
-          request->video_type(), enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT]);
+          request->video_type(),
+          enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT]);
       devices.reserve(audio_devices.size() + video_devices.size());
       devices.insert(devices.end(), audio_devices.begin(), audio_devices.end());
       devices.insert(devices.end(), video_devices.begin(), video_devices.end());
@@ -1261,17 +1265,19 @@
           request->video_type() == blink::MEDIA_NO_SERVICE));
   std::string audio_device_id;
   if (request->controls.audio.requested &&
-      !GetRequestedDeviceCaptureId(request, request->audio_type(),
-                                   enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT],
-                                   &audio_device_id)) {
+      !GetRequestedDeviceCaptureId(
+          request, request->audio_type(),
+          enumeration[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT],
+          &audio_device_id)) {
     return false;
   }
 
   std::string video_device_id;
   if (request->controls.video.requested &&
-      !GetRequestedDeviceCaptureId(request, request->video_type(),
-                                   enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT],
-                                   &video_device_id)) {
+      !GetRequestedDeviceCaptureId(
+          request, request->video_type(),
+          enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT],
+          &video_device_id)) {
     return false;
   }
   request->CreateUIRequest(audio_device_id, video_device_id);
@@ -1951,8 +1957,8 @@
 }
 
 void MediaStreamManager::NotifyDevicesChanged(
-    MediaDeviceType device_type,
-    const MediaDeviceInfoArray& devices) {
+    blink::MediaDeviceType device_type,
+    const blink::WebMediaDeviceInfoArray& devices) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   MediaObserver* media_observer =
       GetContentClient()->browser()->GetMediaObserver();
@@ -2126,7 +2132,7 @@
 
 MediaStreamDevices MediaStreamManager::ConvertToMediaStreamDevices(
     MediaStreamType stream_type,
-    const MediaDeviceInfoArray& device_infos) {
+    const blink::WebMediaDeviceInfoArray& device_infos) {
   MediaStreamDevices devices;
   for (const auto& info : device_infos) {
     devices.emplace_back(stream_type, info.device_id, info.label,
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index 3a3d2fe..970b690 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -47,11 +47,11 @@
 #include "content/browser/renderer_host/media/media_devices_manager.h"
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/common/content_export.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/browser/media_request_state.h"
 #include "content/public/browser/media_stream_request.h"
 #include "media/base/video_facing.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/common/mediastream/media_stream_controls.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
@@ -314,14 +314,14 @@
 
   // Helper for sending up-to-date device lists to media observer when a
   // capture device is plugged in or unplugged.
-  void NotifyDevicesChanged(MediaDeviceType stream_type,
-                            const MediaDeviceInfoArray& devices);
+  void NotifyDevicesChanged(blink::MediaDeviceType stream_type,
+                            const blink::WebMediaDeviceInfoArray& devices);
 
   // This method is called when an audio or video device is removed. It makes
   // sure all MediaStreams that use a removed device are stopped and that the
   // render process is notified. Must be called on the IO thread.
-  void StopRemovedDevice(MediaDeviceType type,
-                         const MediaDeviceInfo& media_device_info);
+  void StopRemovedDevice(blink::MediaDeviceType type,
+                         const blink::WebMediaDeviceInfo& media_device_info);
 
   void SetGenerateStreamCallbackForTesting(
       GenerateStreamTestCallback test_callback);
@@ -451,15 +451,16 @@
   // Otherwise, if no valid device is found, device_id is unchanged.
   bool PickDeviceId(const MediaDeviceSaltAndOrigin& salt_and_origin,
                     const blink::TrackControls& controls,
-                    const MediaDeviceInfoArray& devices,
+                    const blink::WebMediaDeviceInfoArray& devices,
                     std::string* device_id) const;
 
   // Finds the requested device id from request. The requested device type
   // must be MEDIA_DEVICE_AUDIO_CAPTURE or MEDIA_DEVICE_VIDEO_CAPTURE.
-  bool GetRequestedDeviceCaptureId(const DeviceRequest* request,
-                                   blink::MediaStreamType type,
-                                   const MediaDeviceInfoArray& devices,
-                                   std::string* device_id) const;
+  bool GetRequestedDeviceCaptureId(
+      const DeviceRequest* request,
+      blink::MediaStreamType type,
+      const blink::WebMediaDeviceInfoArray& devices,
+      std::string* device_id) const;
 
   void TranslateDeviceIdToSourceId(DeviceRequest* request,
                                    blink::MediaStreamDevice* device);
@@ -487,7 +488,7 @@
   // |video_capture_manager_| to set the MediaStreamDevice fields.
   blink::MediaStreamDevices ConvertToMediaStreamDevices(
       blink::MediaStreamType stream_type,
-      const MediaDeviceInfoArray& device_infos);
+      const blink::WebMediaDeviceInfoArray& device_infos);
 
   media::AudioSystem* const audio_system_;  // not owned
   scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_;
diff --git a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.cc b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.cc
index a84a5952..fc1511a 100644
--- a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.cc
+++ b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.cc
@@ -31,7 +31,8 @@
   // Check permissions for everything but the default device
   if (!media::AudioDeviceDescription::IsDefaultDevice(output_device_id) &&
       !MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
-          MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, render_process_id, render_frame_id)) {
+          blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, render_process_id,
+          render_frame_id)) {
     // If we're not allowed to use the device, don't call |cb|.
     return;
   }
@@ -47,7 +48,7 @@
     const MediaDeviceSaltAndOrigin& salt_and_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   MediaDevicesManager::BoolDeviceTypes device_types;
-  device_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   media_devices_manager->EnumerateDevices(device_types,
                                           base::BindOnce(cb, salt_and_origin));
 }
@@ -215,7 +216,8 @@
     const MediaDeviceEnumeration& device_array) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   std::string raw_output_device_id;
-  for (const auto& device_info : device_array[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
+  for (const auto& device_info :
+       device_array[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
     if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
             salt_and_origin.device_id_salt, salt_and_origin.origin,
             output_device_id, device_info.device_id)) {
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
index ccd9404..b9690c7 100644
--- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
+++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory.cc
@@ -61,7 +61,7 @@
                             MediaDevicesManager::EnumerationCallback cb) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   MediaDevicesManager::BoolDeviceTypes device_types;
-  device_types[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
+  device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
   media_stream_manager->media_devices_manager()->EnumerateDevices(
       device_types, std::move(cb));
 }
@@ -71,7 +71,8 @@
                        base::RepeatingCallback<void(const std::string&)> cb,
                        const MediaDeviceEnumeration& device_array) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const auto& device_info : device_array[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
+  for (const auto& device_info :
+       device_array[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
     if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
             salt_and_origin.device_id_salt, salt_and_origin.origin, device_id,
             device_info.device_id)) {
@@ -89,7 +90,7 @@
                             bool has_access)> cb) {
   auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
   bool access = MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
-      MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id);
+      blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index bed252e0..5af6f4a 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -22,7 +22,6 @@
 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
 #include "content/browser/renderer_host/media/video_capture_host.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -36,6 +35,7 @@
 #include "net/url_request/url_request_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 
 using ::testing::_;
 using ::testing::AnyNumber;
@@ -54,12 +54,13 @@
 void VideoInputDevicesEnumerated(base::Closure quit_closure,
                                  const std::string& salt,
                                  const url::Origin& security_origin,
-                                 MediaDeviceInfoArray* out,
+                                 blink::WebMediaDeviceInfoArray* out,
                                  const MediaDeviceEnumeration& enumeration) {
-  for (const auto& info : enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
+  for (const auto& info : enumeration[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT]) {
     std::string device_id = MediaStreamManager::GetHMACForMediaDeviceID(
         salt, security_origin, info.device_id);
-    out->push_back(MediaDeviceInfo(device_id, info.label, std::string()));
+    out->push_back(
+        blink::WebMediaDeviceInfo(device_id, info.label, std::string()));
   }
   std::move(quit_closure).Run();
 }
@@ -136,11 +137,11 @@
     ASSERT_TRUE(opened_device_label_.empty());
 
     // Enumerate video devices.
-    MediaDeviceInfoArray video_devices;
+    blink::WebMediaDeviceInfoArray video_devices;
     {
       base::RunLoop run_loop;
       MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
-      devices_to_enumerate[MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
+      devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] = true;
       media_stream_manager_->media_devices_manager()->EnumerateDevices(
           devices_to_enumerate,
           base::BindOnce(&VideoInputDevicesEnumerated, run_loop.QuitClosure(),
diff --git a/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h b/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
index 8840d0d..63203da 100644
--- a/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h
@@ -17,6 +17,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ppapi/c/pp_instance.h"
 #include "ppapi/host/resource_message_filter.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 
 struct PP_HostResolver_Private_Hint;
@@ -40,7 +41,7 @@
 
 class CONTENT_EXPORT PepperHostResolverMessageFilter
     : public ppapi::host::ResourceMessageFilter,
-      public network::mojom::ResolveHostClient {
+      public network::ResolveHostClientBase {
  public:
   PepperHostResolverMessageFilter(BrowserPpapiHostImpl* host,
                                   PP_Instance instance,
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
index 8c49f6c..9f8c692 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
@@ -29,6 +29,7 @@
 #include "ppapi/c/private/ppb_net_address_private.h"
 #include "ppapi/host/resource_message_filter.h"
 #include "ppapi/shared_impl/ppb_tcp_socket_shared.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/tcp_socket.mojom.h"
 #include "services/network/public/mojom/tls_socket.mojom.h"
@@ -57,7 +58,7 @@
 class CONTENT_EXPORT PepperTCPSocketMessageFilter
     : public ppapi::host::ResourceMessageFilter,
       public BrowserPpapiHostImpl::InstanceObserver,
-      public network::mojom::ResolveHostClient,
+      public network::ResolveHostClientBase,
       public network::mojom::SocketObserver {
  public:
   // |factory| must be non-nullptr unless the consumer immediately calls
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e5f26ff..cb4cdde 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1245,6 +1245,14 @@
   return *instance;
 }
 
+RenderProcessHostImpl::BroadcastChannelProviderRequestHandler&
+GetBroadcastChannelProviderRequestHandler() {
+  static base::NoDestructor<
+      RenderProcessHostImpl::BroadcastChannelProviderRequestHandler>
+      instance;
+  return *instance;
+}
+
 void RemoveCorbExceptionForPluginOnIOThread(int process_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -1686,6 +1694,11 @@
   GetStoragePartitionServiceRequestHandler() = handler;
 }
 
+void RenderProcessHostImpl::SetBroadcastChannelProviderRequestHandlerForTesting(
+    BroadcastChannelProviderRequestHandler handler) {
+  GetBroadcastChannelProviderRequestHandler() = handler;
+}
+
 RenderProcessHostImpl::~RenderProcessHostImpl() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 #ifndef NDEBUG
@@ -2122,41 +2135,45 @@
       base::Unretained(service_worker_dispatcher_host_.get())));
 
   AddUIThreadInterface(
-      registry.get(), base::Bind(&ForwardRequest<device::mojom::BatteryMonitor>,
-                                 device::mojom::kServiceName));
+      registry.get(),
+      base::BindRepeating(&ForwardRequest<device::mojom::BatteryMonitor>,
+                          device::mojom::kServiceName));
 
   AddUIThreadInterface(
       registry.get(),
-      base::Bind(&RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider,
-                 base::Unretained(this)));
+      base::BindRepeating(
+          &RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider,
+          base::Unretained(this)));
+
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(&RenderProcessHostImpl::BindFrameSinkProvider,
+                          base::Unretained(this)));
+
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(&RenderProcessHostImpl::BindCompositingModeReporter,
+                          base::Unretained(this)));
+
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(
+          &BackgroundSyncContext::CreateService,
+          base::Unretained(
+              storage_partition_impl_->GetBackgroundSyncContext())));
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(&RenderProcessHostImpl::CreateStoragePartitionService,
+                          base::Unretained(this)));
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(
+          &RenderProcessHostImpl::CreateBroadcastChannelProvider,
+          base::Unretained(this)));
 
   AddUIThreadInterface(registry.get(),
-                       base::Bind(&RenderProcessHostImpl::BindFrameSinkProvider,
-                                  base::Unretained(this)));
-
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&RenderProcessHostImpl::BindCompositingModeReporter,
-                 base::Unretained(this)));
-
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&BackgroundSyncContext::CreateService,
-                 base::Unretained(
-                     storage_partition_impl_->GetBackgroundSyncContext())));
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&RenderProcessHostImpl::CreateStoragePartitionService,
-                 base::Unretained(this)));
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&BroadcastChannelProvider::Connect,
-                 base::Unretained(
-                     storage_partition_impl_->GetBroadcastChannelProvider())));
-
-  AddUIThreadInterface(
-      registry.get(),
-      base::Bind(&CreateProcessResourceCoordinator, base::Unretained(this)));
+                       base::BindRepeating(&CreateProcessResourceCoordinator,
+                                           base::Unretained(this)));
 
   AddUIThreadInterface(registry.get(),
                        base::BindRepeating(&ClipboardHostImpl::Create));
@@ -2169,13 +2186,14 @@
                           base::Unretained(video_perf_history)));
 
   registry->AddInterface(
-      base::Bind(&MimeRegistryImpl::Create),
+      base::BindRepeating(&MimeRegistryImpl::Create),
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
            base::TaskPriority::USER_BLOCKING}));
 #if BUILDFLAG(USE_MINIKIN_HYPHENATION)
-  registry->AddInterface(base::Bind(&hyphenation::HyphenationImpl::Create),
-                         hyphenation::HyphenationImpl::GetTaskRunner());
+  registry->AddInterface(
+      base::BindRepeating(&hyphenation::HyphenationImpl::Create),
+      hyphenation::HyphenationImpl::GetTaskRunner());
 #endif
 #if defined(OS_ANDROID)
   if (base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)) {
@@ -2185,11 +2203,12 @@
   }
 #endif
 
-  registry->AddInterface(base::Bind(&device::GamepadHapticsManager::Create));
+  registry->AddInterface(
+      base::BindRepeating(&device::GamepadHapticsManager::Create));
 
   registry->AddInterface(
-      base::Bind(&PushMessagingManager::BindRequest,
-                 base::Unretained(push_messaging_manager_.get())));
+      base::BindRepeating(&PushMessagingManager::BindRequest,
+                          base::Unretained(push_messaging_manager_.get())));
 
   file_system_manager_impl_.reset(new FileSystemManagerImpl(
       GetID(), MSG_ROUTING_NONE,
@@ -2216,7 +2235,7 @@
   }
 
   registry->AddInterface(
-      base::Bind(
+      base::BindRepeating(
           &WebDatabaseHostImpl::Create, GetID(),
           base::WrapRefCounted(storage_partition_impl_->GetDatabaseTracker())),
       storage_partition_impl_->GetDatabaseTracker()->task_runner());
@@ -2224,11 +2243,11 @@
   MediaStreamManager* media_stream_manager =
       BrowserMainLoop::GetInstance()->media_stream_manager();
 
-  registry->AddInterface(
-      base::Bind(&VideoCaptureHost::Create, GetID(), media_stream_manager));
+  registry->AddInterface(base::BindRepeating(&VideoCaptureHost::Create, GetID(),
+                                             media_stream_manager));
 
   registry->AddInterface(
-      base::Bind(&FileUtilitiesHostImpl::Create, GetID()),
+      base::BindRepeating(&FileUtilitiesHostImpl::Create, GetID()),
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE}));
 
@@ -2237,7 +2256,7 @@
       base::Unretained(this)));
 
   registry->AddInterface(
-      base::Bind(&metrics::CreateSingleSampleMetricsProvider));
+      base::BindRepeating(&metrics::CreateSingleSampleMetricsProvider));
 
   registry->AddInterface(base::BindRepeating(
       &CodeCacheHostImpl::Create, GetID(),
@@ -2268,15 +2287,16 @@
                           base::Unretained(this)));
 #endif  // BUILDFLAG(ENABLE_MDNS)
 
-  AddUIThreadInterface(registry.get(), base::Bind(&FieldTrialRecorder::Create));
+  AddUIThreadInterface(registry.get(),
+                       base::BindRepeating(&FieldTrialRecorder::Create));
 
   associated_interfaces_ =
       std::make_unique<blink::AssociatedInterfaceRegistry>();
   blink::AssociatedInterfaceRegistry* associated_registry =
       associated_interfaces_.get();
-  associated_registry->AddInterface(base::Bind(
+  associated_registry->AddInterface(base::BindRepeating(
       &RenderProcessHostImpl::BindRouteProvider, base::Unretained(this)));
-  associated_registry->AddInterface(base::Bind(
+  associated_registry->AddInterface(base::BindRepeating(
       &RenderProcessHostImpl::CreateRendererHost, base::Unretained(this)));
 
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
@@ -2393,6 +2413,17 @@
   storage_partition_impl_->Bind(id_, std::move(request));
 }
 
+void RenderProcessHostImpl::CreateBroadcastChannelProvider(
+    blink::mojom::BroadcastChannelProviderRequest request) {
+  if (!GetBroadcastChannelProviderRequestHandler().is_null()) {
+    GetBroadcastChannelProviderRequestHandler().Run(this, std::move(request));
+    return;
+  }
+
+  storage_partition_impl_->GetBroadcastChannelProvider()->Connect(
+      id_, std::move(request));
+}
+
 void RenderProcessHostImpl::BindVideoDecoderService(
     media::mojom::InterfaceFactoryRequest request) {
   if (!video_decoder_proxy_)
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 55a2a0a..ec6d9e7 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -64,6 +64,7 @@
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
+#include "third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
 #if defined(OS_ANDROID)
@@ -380,6 +381,15 @@
   static void SetStoragePartitionServiceRequestHandlerForTesting(
       StoragePartitionServiceRequestHandler handler);
 
+  // Allows external code to supply a callback which handles a
+  // BroadcastChannelProviderRequest. Used for supplying test versions of the
+  // service.
+  using BroadcastChannelProviderRequestHandler = base::RepeatingCallback<void(
+      RenderProcessHostImpl* rph,
+      blink::mojom::BroadcastChannelProviderRequest request)>;
+  static void SetBroadcastChannelProviderRequestHandlerForTesting(
+      BroadcastChannelProviderRequestHandler handler);
+
   RenderFrameMessageFilter* render_frame_message_filter_for_testing() const {
     return render_frame_message_filter_.get();
   }
@@ -543,6 +553,8 @@
       viz::mojom::CompositingModeReporterRequest request);
   void CreateStoragePartitionService(
       blink::mojom::StoragePartitionServiceRequest request);
+  void CreateBroadcastChannelProvider(
+      blink::mojom::BroadcastChannelProviderRequest request);
   void CreateRendererHost(mojom::RendererHostAssociatedRequest request);
   void BindVideoDecoderService(media::mojom::InterfaceFactoryRequest request);
 
diff --git a/content/browser/renderer_host/web_database_host_impl_unittest.cc b/content/browser/renderer_host/web_database_host_impl_unittest.cc
index 230d0cb..f860bcc 100644
--- a/content/browser/renderer_host/web_database_host_impl_unittest.cc
+++ b/content/browser/renderer_host/web_database_host_impl_unittest.cc
@@ -10,12 +10,14 @@
 #include "content/browser/isolation_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "storage/common/database/database_identifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
 namespace {
+
 base::string16 ConstructVfsFileName(const url::Origin& origin,
                                     const base::string16& name,
                                     const base::string16& suffix) {
@@ -24,40 +26,18 @@
          base::ASCIIToUTF16("#") + suffix;
 }
 
-class BadMessageObserver {
+class FakeMojoMessageDispatchContext {
  public:
-  BadMessageObserver()
-      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {
-    mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
-        &BadMessageObserver::ReportBadMessage, base::Unretained(this)));
-  }
-
-  ~BadMessageObserver() {
-    mojo::core::SetDefaultProcessErrorCallback(
-        mojo::core::ProcessErrorCallback());
-  }
-
-  const std::string& WaitForError() {
-    if (last_error_.empty())
-      run_loop_.Run();
-
-    return last_error_;
-  }
+  FakeMojoMessageDispatchContext()
+      : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {}
 
  private:
-  void ReportBadMessage(const std::string& error) {
-    DCHECK(!error.empty());
-    last_error_ = error;
-    run_loop_.Quit();
-  }
-
   mojo::Message dummy_message_;
   mojo::internal::MessageDispatchContext context_;
-  std::string last_error_;
-  base::RunLoop run_loop_;
 
-  DISALLOW_COPY_AND_ASSIGN(BadMessageObserver);
+  DISALLOW_COPY_AND_ASSIGN(FakeMojoMessageDispatchContext);
 };
+
 }  // namespace
 
 class WebDatabaseHostImplTest : public ::testing::Test {
@@ -80,16 +60,18 @@
 
   template <typename Callable>
   void CheckUnauthorizedOrigin(const Callable& func) {
-    BadMessageObserver bad_message_observer;
+    FakeMojoMessageDispatchContext fake_dispatch_context;
+    mojo::test::BadMessageObserver bad_message_observer;
     func();
-    EXPECT_EQ("Unauthorized origin.", bad_message_observer.WaitForError());
+    EXPECT_EQ("Unauthorized origin.", bad_message_observer.WaitForBadMessage());
   }
 
   template <typename Callable>
   void CheckInvalidOrigin(const Callable& func) {
-    BadMessageObserver bad_message_observer;
+    FakeMojoMessageDispatchContext fake_dispatch_context;
+    mojo::test::BadMessageObserver bad_message_observer;
     func();
-    EXPECT_EQ("Invalid origin.", bad_message_observer.WaitForError());
+    EXPECT_EQ("Invalid origin.", bad_message_observer.WaitForBadMessage());
   }
 
   WebDatabaseHostImpl* host() { return host_.get(); }
diff --git a/content/browser/sandbox_host_linux.cc b/content/browser/sandbox_host_linux.cc
index db3b884c..8128968e 100644
--- a/content/browser/sandbox_host_linux.cc
+++ b/content/browser/sandbox_host_linux.cc
@@ -6,7 +6,7 @@
 
 #include <sys/socket.h>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/posix/eintr_wrapper.h"
 
 namespace content {
@@ -16,7 +16,8 @@
 
 // static
 SandboxHostLinux* SandboxHostLinux::GetInstance() {
-  return base::Singleton<SandboxHostLinux>::get();
+  static base::NoDestructor<SandboxHostLinux> instance;
+  return instance.get();
 }
 
 void SandboxHostLinux::Init() {
@@ -53,23 +54,4 @@
   ipc_thread_->Start();
 }
 
-bool SandboxHostLinux::ShutdownIPCChannel() {
-  return IGNORE_EINTR(close(childs_lifeline_fd_)) == 0;
-}
-
-SandboxHostLinux::~SandboxHostLinux() {
-  if (initialized_) {
-    if (!ShutdownIPCChannel())
-      LOG(ERROR) << "ShutdownIPCChannel failed";
-    if (IGNORE_EINTR(close(child_socket_)) < 0)
-      PLOG(ERROR) << "close";
-
-    // TODO(gab): We might be able to just delete this destructor:
-    // https://chromium-review.googlesource.com/c/chromium/src/+/1378683
-    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
-        allow_thread_join_on_shutdown;
-    ipc_thread_->Join();
-  }
-}
-
 }  // namespace content
diff --git a/content/browser/sandbox_host_linux.h b/content/browser/sandbox_host_linux.h
index cbaaaa6b..d7c29a4d 100644
--- a/content/browser/sandbox_host_linux.h
+++ b/content/browser/sandbox_host_linux.h
@@ -16,7 +16,7 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
 }
 
 namespace content {
@@ -41,12 +41,12 @@
   bool IsInitialized() const { return initialized_; }
 
  private:
-  friend struct base::DefaultSingletonTraits<SandboxHostLinux>;
-  // This object must be constructed on the main thread.
+  friend class base::NoDestructor<SandboxHostLinux>;
+  // This object must be constructed on the main thread. It then lives for the
+  // lifetime of the process (and resources are reclaimed by the OS when the
+  // process dies).
   SandboxHostLinux();
-  ~SandboxHostLinux();
-
-  bool ShutdownIPCChannel();
+  ~SandboxHostLinux() = delete;
 
   // Whether Init() has been called yet.
   bool initialized_ = false;
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 0da371f..8d01f4e 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -51,6 +51,7 @@
 #include "ipc/ipc_security_test_util.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/url_request/url_request_slow_download_job.h"
@@ -61,6 +62,8 @@
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "storage/browser/blob/blob_registry_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/web/web_triggering_event_info.h"
 
@@ -202,32 +205,6 @@
   GURL target_url_;
 };
 
-class MojoBadMessageObserver {
- public:
-  MojoBadMessageObserver() {
-    mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
-        &MojoBadMessageObserver::OnReportBadMessage, base::Unretained(this)));
-  }
-
-  ~MojoBadMessageObserver() {
-    mojo::core::SetDefaultProcessErrorCallback(
-        mojo::core::ProcessErrorCallback());
-  }
-
-  bool LastBadMessageStartingWith(const std::string& prefix) const {
-    return last_bad_message_.compare(0, prefix.size(), prefix) == 0;
-  }
-
- private:
-  void OnReportBadMessage(const std::string& message) {
-    last_bad_message_ = message;
-  }
-
-  std::string last_bad_message_;
-
-  DISALLOW_COPY_AND_ASSIGN(MojoBadMessageObserver);
-};
-
 }  // namespace
 
 // The goal of these tests will be to "simulate" exploited renderer processes,
@@ -346,15 +323,15 @@
       blink::mojom::FileChooserParams::New();
   params->default_file_name = path;
 
-  MojoBadMessageObserver bad_message_observer;
+  mojo::test::BadMessageObserver bad_message_observer;
   blink::mojom::FileChooserPtr factory =
       static_cast<RenderFrameHostImpl*>(compromised_renderer)
           ->BindFileChooserForTesting();
   factory->OpenFileChooser(
       std::move(params), blink::mojom::FileChooser::OpenFileChooserCallback());
   factory.FlushForTesting();
-  EXPECT_TRUE(bad_message_observer.LastBadMessageStartingWith(
-      "FileChooser: The default file name"));
+  EXPECT_THAT(bad_message_observer.WaitForBadMessage(),
+              ::testing::StartsWith("FileChooser: The default file name"));
 }
 
 // Ensure that we kill the renderer process if we try to give it WebUI
diff --git a/content/browser/service_worker/DEPS b/content/browser/service_worker/DEPS
index b90ca094..743a2f3b 100644
--- a/content/browser/service_worker/DEPS
+++ b/content/browser/service_worker/DEPS
@@ -1,4 +1,3 @@
 include_rules = [
   "+third_party/leveldatabase",
-  "+third_party/blink/public/web/worker_content_settings_proxy.mojom.h",
 ]
diff --git a/content/browser/service_worker/service_worker_content_settings_proxy_impl.h b/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
index c17eb1f..77f86b3f 100644
--- a/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
+++ b/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
@@ -9,7 +9,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index d6474f5..4c25e9d 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -480,6 +480,25 @@
                      std::move(failure_callback)));
 }
 
+void ServiceWorkerContextWrapper::StartServiceWorkerAndDispatchMessage(
+    const GURL& scope,
+    blink::TransferableMessage message,
+    ResultCallback result_callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (!context_core_) {
+    std::move(result_callback).Run(false);
+    return;
+  }
+
+  FindReadyRegistrationForScope(
+      net::SimplifyUrlForRequest(scope),
+      base::BindOnce(
+          &ServiceWorkerContextWrapper::DidFindRegistrationForMessageDispatch,
+          this, std::move(message), scope, std::move(result_callback),
+          false /* is_long_running_message */));
+}
+
 void ServiceWorkerContextWrapper::
     StartServiceWorkerAndDispatchLongRunningMessage(
         const GURL& scope,
@@ -500,16 +519,17 @@
 
   FindReadyRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
-      base::BindOnce(&ServiceWorkerContextWrapper::
-                         DidFindRegistrationForLongRunningMessage,
-                     this, std::move(message), scope,
-                     std::move(result_callback)));
+      base::BindOnce(
+          &ServiceWorkerContextWrapper::DidFindRegistrationForMessageDispatch,
+          this, std::move(message), scope, std::move(result_callback),
+          true /* is_long_running_message */));
 }
 
-void ServiceWorkerContextWrapper::DidFindRegistrationForLongRunningMessage(
+void ServiceWorkerContextWrapper::DidFindRegistrationForMessageDispatch(
     blink::TransferableMessage message,
     const GURL& source_origin,
     ResultCallback result_callback,
+    bool is_long_running_message,
     blink::ServiceWorkerStatusCode service_worker_status,
     scoped_refptr<ServiceWorkerRegistration> registration) {
   if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
@@ -519,18 +539,21 @@
     return;
   }
   registration->active_version()->StartWorker(
-      ServiceWorkerMetrics::EventType::LONG_RUNNING_MESSAGE,
-      base::BindOnce(&ServiceWorkerContextWrapper::
-                         DidStartServiceWorkerForLongRunningMessage,
-                     this, std::move(message), source_origin, registration,
-                     std::move(result_callback)));
+      is_long_running_message
+          ? ServiceWorkerMetrics::EventType::LONG_RUNNING_MESSAGE
+          : ServiceWorkerMetrics::EventType::MESSAGE,
+      base::BindOnce(
+          &ServiceWorkerContextWrapper::DidStartServiceWorkerForMessageDispatch,
+          this, std::move(message), source_origin, registration,
+          std::move(result_callback), is_long_running_message));
 }
 
-void ServiceWorkerContextWrapper::DidStartServiceWorkerForLongRunningMessage(
+void ServiceWorkerContextWrapper::DidStartServiceWorkerForMessageDispatch(
     blink::TransferableMessage message,
     const GURL& source_origin,
     scoped_refptr<ServiceWorkerRegistration> registration,
     ResultCallback result_callback,
+    bool is_long_running_message,
     blink::ServiceWorkerStatusCode status) {
   if (status != blink::ServiceWorkerStatusCode::kOk) {
     std::move(result_callback).Run(false);
@@ -539,12 +562,6 @@
 
   scoped_refptr<ServiceWorkerVersion> version = registration->active_version();
 
-  int request_id = version->StartRequestWithCustomTimeout(
-      ServiceWorkerMetrics::EventType::LONG_RUNNING_MESSAGE,
-      base::BindOnce(&MessageFinishedSending, std::move(result_callback)),
-      base::TimeDelta::FromDays(kActiveWorkerTimeoutDays),
-      ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
-
   blink::mojom::ExtendableMessageEventPtr event =
       blink::mojom::ExtendableMessageEvent::New();
   event->message = std::move(message);
@@ -554,9 +571,22 @@
           ->GetOrCreateServiceWorkerObjectHost(version)
           ->CreateCompleteObjectInfoToSend();
 
-  version->endpoint()->DispatchExtendableMessageEventWithCustomTimeout(
-      std::move(event), base::TimeDelta::FromDays(kActiveWorkerTimeoutDays),
-      version->CreateSimpleEventCallback(request_id));
+  if (is_long_running_message) {
+    int request_id = version->StartRequestWithCustomTimeout(
+        ServiceWorkerMetrics::EventType::LONG_RUNNING_MESSAGE,
+        base::BindOnce(&MessageFinishedSending, std::move(result_callback)),
+        base::TimeDelta::FromDays(kActiveWorkerTimeoutDays),
+        ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
+    version->endpoint()->DispatchExtendableMessageEventWithCustomTimeout(
+        std::move(event), base::TimeDelta::FromDays(kActiveWorkerTimeoutDays),
+        version->CreateSimpleEventCallback(request_id));
+  } else {
+    int request_id = version->StartRequest(
+        ServiceWorkerMetrics::EventType::MESSAGE,
+        base::BindOnce(&MessageFinishedSending, std::move(result_callback)));
+    version->endpoint()->DispatchExtendableMessageEvent(
+        std::move(event), version->CreateSimpleEventCallback(request_id));
+  }
 }
 
 void ServiceWorkerContextWrapper::StartServiceWorkerForNavigationHint(
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index af3e0e5..eedc20a 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -139,6 +139,10 @@
   void StartWorkerForScope(const GURL& scope,
                            StartWorkerCallback info_callback,
                            base::OnceClosure failure_callback) override;
+  void StartServiceWorkerAndDispatchMessage(
+      const GURL& scope,
+      blink::TransferableMessage message,
+      ResultCallback result_callback) override;
   void StartServiceWorkerAndDispatchLongRunningMessage(
       const GURL& scope,
       blink::TransferableMessage message,
@@ -373,18 +377,20 @@
       base::OnceClosure callback,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_callback);
 
-  void DidFindRegistrationForLongRunningMessage(
+  void DidFindRegistrationForMessageDispatch(
       blink::TransferableMessage message,
       const GURL& source_origin,
       ResultCallback result_callback,
+      bool is_long_running_message,
       blink::ServiceWorkerStatusCode service_worker_status,
       scoped_refptr<ServiceWorkerRegistration> registration);
 
-  void DidStartServiceWorkerForLongRunningMessage(
+  void DidStartServiceWorkerForMessageDispatch(
       blink::TransferableMessage message,
       const GURL& source_origin,
       scoped_refptr<ServiceWorkerRegistration> registration,
       ServiceWorkerContext::ResultCallback result_callback,
+      bool is_long_running_message,
       blink::ServiceWorkerStatusCode service_worker_status);
 
   void SendActiveWorkerMessage(
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 4304d89..33369d6 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -518,9 +518,8 @@
 
   // Grant the service worker's process access to files in the request body.
   if (request_->body) {
-    const scoped_refptr<network::ResourceRequestBody>& body = *request_->body;
     GrantFileAccessToProcess(version_->embedded_worker()->process_id(),
-                             body->GetReferencedFiles());
+                             request_->body->GetReferencedFiles());
   }
 
   // Run callback to say that the fetch event will be dispatched.
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index 25c2b6d..9880e7a 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -42,7 +42,7 @@
   if (!body)
     return true;
   for (const auto& elem : *body->elements()) {
-    if (elem.type() == network::DataElement::TYPE_DATA_PIPE)
+    if (elem.type() == network::mojom::DataElementType::kDataPipe)
       return false;
   }
   return true;
diff --git a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
index 521f764..6edf5a55 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
@@ -260,7 +260,7 @@
     // So far this test expects a single bytes element.
     ASSERT_EQ(1u, elements->size());
     const network::DataElement& element = elements->front();
-    ASSERT_EQ(network::DataElement::TYPE_BYTES, element.type());
+    ASSERT_EQ(network::mojom::DataElementType::kBytes, element.type());
     *out_string = std::string(element.bytes(), element.length());
   }
 
@@ -285,7 +285,7 @@
 
     has_received_fetch_event_ = true;
     if (request->body)
-      request_body_ = request->body.value();
+      request_body_ = request->body;
 
     switch (response_mode_) {
       case ResponseMode::kDefault:
diff --git a/content/browser/speech/speech_recognition_engine_unittest.cc b/content/browser/speech/speech_recognition_engine_unittest.cc
index 4b0db87..b6479b9 100644
--- a/content/browser/speech/speech_recognition_engine_unittest.cc
+++ b/content/browser/speech/speech_recognition_engine_unittest.cc
@@ -710,7 +710,7 @@
       EXPECT_TRUE(upstream_request->request.request_body);
       EXPECT_EQ(1u, upstream_request->request.request_body->elements()->size());
       EXPECT_EQ(
-          network::DataElement::TYPE_CHUNKED_DATA_PIPE,
+          network::mojom::DataElementType::kChunkedDataPipe,
           (*upstream_request->request.request_body->elements())[0].type());
       network::TestURLLoaderFactory::PendingRequest* mutable_upstream_request =
           const_cast<network::TestURLLoaderFactory::PendingRequest*>(
diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc
index b4b6067..ca29044 100644
--- a/content/browser/speech/speech_recognizer_impl_unittest.cc
+++ b/content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -423,7 +423,7 @@
       ASSERT_TRUE(upstream_request->request.request_body);
       ASSERT_EQ(1u, upstream_request->request.request_body->elements()->size());
       ASSERT_EQ(
-          network::DataElement::TYPE_CHUNKED_DATA_PIPE,
+          network::mojom::DataElementType::kChunkedDataPipe,
           (*upstream_request->request.request_body->elements())[0].type());
       network::TestURLLoaderFactory::PendingRequest* mutable_upstream_request =
           const_cast<network::TestURLLoaderFactory::PendingRequest*>(
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index a37a5c2..430e8d0c 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -455,8 +455,9 @@
                   partition->GetPath(), in_memory));
   }
 
+  // Arm the serviceworker cookie change observation API.
   partition->GetCookieStoreContext()->ListenToCookieChanges(
-      partition->GetNetworkContext(), base::DoNothing());
+      partition->GetNetworkContext(), /*success_callback=*/base::DoNothing());
 
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     // This needs to happen after SetURLRequestContext() since we need this
diff --git a/content/browser/url_loader_factory_getter.cc b/content/browser/url_loader_factory_getter.cc
index 6d27ff8c..4716569 100644
--- a/content/browser/url_loader_factory_getter.cc
+++ b/content/browser/url_loader_factory_getter.cc
@@ -199,9 +199,11 @@
   if (g_get_network_factory_callback.Get() && !test_factory_)
     g_get_network_factory_callback.Get().Run(this);
 
-  if (is_corb_enabled && test_factory_corb_enabled_)
-    return test_factory_corb_enabled_;
-
+  // The |is_corb_enabled| case will only be hit for AppCache scenarios, where
+  // the URLLoaderInterceptor's test hooks are sufficiently covered at the
+  // RenderFrameHostImpl level (e.g. intercepting requests coming from the
+  // renderer, rather than requests generated within
+  // AppCacheSubresourceURLFactory.
   if (!is_corb_enabled && test_factory_)
     return test_factory_;
 
@@ -215,16 +217,10 @@
 }
 
 void URLLoaderFactoryGetter::SetNetworkFactoryForTesting(
-    network::mojom::URLLoaderFactory* test_factory,
-    bool is_corb_enabled) {
+    network::mojom::URLLoaderFactory* test_factory) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (is_corb_enabled) {
-    DCHECK(!test_factory_corb_enabled_ || !test_factory);
-    test_factory_corb_enabled_ = test_factory;
-  } else {
-    DCHECK(!test_factory_ || !test_factory);
-    test_factory_ = test_factory;
-  }
+  DCHECK(!test_factory_ || !test_factory);
+  test_factory_ = test_factory;
 }
 
 void URLLoaderFactoryGetter::SetGetNetworkFactoryCallbackForTesting(
diff --git a/content/browser/url_loader_factory_getter.h b/content/browser/url_loader_factory_getter.h
index f7022f6..5f18912 100644
--- a/content/browser/url_loader_factory_getter.h
+++ b/content/browser/url_loader_factory_getter.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_URL_LOADER_FACTORY_GETTER_H_
 #define CONTENT_BROWSER_URL_LOADER_FACTORY_GETTER_H_
 
+#include <memory>
+
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -85,8 +87,7 @@
   // Overrides the network URLLoaderFactory for subsequent requests. Passing a
   // null pointer will restore the default behavior.
   CONTENT_EXPORT void SetNetworkFactoryForTesting(
-      network::mojom::URLLoaderFactory* test_factory,
-      bool is_corb_enabled = false);
+      network::mojom::URLLoaderFactory* test_factory);
 
   CONTENT_EXPORT network::mojom::URLLoaderFactoryPtr*
   original_network_factory_for_testing() {
@@ -148,7 +149,6 @@
   network::mojom::URLLoaderFactoryPtr network_factory_;
   network::mojom::URLLoaderFactoryPtr network_factory_corb_enabled_;
   network::mojom::URLLoaderFactory* test_factory_ = nullptr;
-  network::mojom::URLLoaderFactory* test_factory_corb_enabled_ = nullptr;
 
   // Used to re-create |network_factory_| when connection error happens. Can
   // only be accessed on UI thread. Must be cleared by |StoragePartitionImpl|
diff --git a/content/browser/web_package/signed_exchange_envelope_unittest.cc b/content/browser/web_package/signed_exchange_envelope_unittest.cc
index e61403a..2a36181 100644
--- a/content/browser/web_package/signed_exchange_envelope_unittest.cc
+++ b/content/browser/web_package/signed_exchange_envelope_unittest.cc
@@ -76,7 +76,7 @@
     : public ::testing::TestWithParam<SignedExchangeVersion> {};
 
 TEST_P(SignedExchangeEnvelopeTest, ParseGoldenFile) {
-  if (GetParam() != SignedExchangeVersion::kB2)
+  if (GetParam() != SignedExchangeVersion::kB3)
     return;
 
   base::FilePath test_sxg_path;
@@ -119,7 +119,7 @@
       prologue_b.cbor_header_length());
   const base::Optional<SignedExchangeEnvelope> envelope =
       SignedExchangeEnvelope::Parse(
-          SignedExchangeVersion::kB2, prologue_b.fallback_url(),
+          SignedExchangeVersion::kB3, prologue_b.fallback_url(),
           signature_header_field, cbor_bytes, nullptr /* devtools_proxy */);
   ASSERT_TRUE(envelope.has_value());
   EXPECT_EQ(envelope->request_url().url,
diff --git a/content/browser/web_package/signed_exchange_handler_unittest.cc b/content/browser/web_package/signed_exchange_handler_unittest.cc
index b5e9d03..a0d921c 100644
--- a/content/browser/web_package/signed_exchange_handler_unittest.cc
+++ b/content/browser/web_package/signed_exchange_handler_unittest.cc
@@ -169,7 +169,7 @@
             url::Origin::Create(GURL("https://sxg.example.com/test.sxg"))) {}
 
   virtual std::string ContentType() {
-    return "application/signed-exchange;v=b2";
+    return "application/signed-exchange;v=b3";
   }
 
   void SetUp() override {
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index d44de05..498167b 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -253,7 +253,6 @@
 device::CtapMakeCredentialRequest CreateCtapMakeCredentialRequest(
     const std::string& client_data_json,
     const blink::mojom::PublicKeyCredentialCreationOptionsPtr& options,
-    bool is_individual_attestation,
     bool is_incognito) {
   auto credential_params = mojo::ConvertTo<
       std::vector<device::PublicKeyCredentialParams::CredentialInfo>>(
@@ -271,7 +270,6 @@
           options->exclude_credentials);
 
   make_credential_param.SetExcludeList(std::move(exclude_list));
-  make_credential_param.SetIsIndividualAttestation(is_individual_attestation);
   make_credential_param.SetHmacSecret(options->hmac_create_secret);
   make_credential_param.set_is_incognito_mode(is_incognito);
   return make_credential_param;
@@ -657,13 +655,6 @@
         std::move(options->challenge));
   }
 
-  const bool individual_attestation =
-      options->attestation ==
-          blink::mojom::AttestationConveyancePreference::ENTERPRISE &&
-      request_delegate_->ShouldPermitIndividualAttestation(relying_party_id_);
-
-  attestation_preference_ = options->attestation;
-
   auto authenticator_selection_criteria =
       options->authenticator_selection
           ? mojo::ConvertTo<device::AuthenticatorSelectionCriteria>(
@@ -671,10 +662,22 @@
           : device::AuthenticatorSelectionCriteria();
 
   auto ctap_request = CreateCtapMakeCredentialRequest(
-      client_data_json_, options, individual_attestation,
-      browser_context()->IsOffTheRecord());
+      client_data_json_, options, browser_context()->IsOffTheRecord());
   ctap_request.set_is_u2f_only(OriginIsCryptoTokenExtension(caller_origin_));
 
+  // Compute the effective attestation conveyance preference and set
+  // |attestation_requested_| for showing the attestation consent prompt later.
+  auto attestation = mojo::ConvertTo<::device::AttestationConveyancePreference>(
+      options->attestation);
+  if (attestation == ::device::AttestationConveyancePreference::ENTERPRISE &&
+      !request_delegate_->ShouldPermitIndividualAttestation(
+          relying_party_id_)) {
+    attestation = ::device::AttestationConveyancePreference::DIRECT;
+  }
+  ctap_request.set_attestation_preference(attestation);
+  attestation_requested_ =
+      attestation != ::device::AttestationConveyancePreference::NONE;
+
   request_ = std::make_unique<device::MakeCredentialRequestHandler>(
       connector_, transports_, std::move(ctap_request),
       std::move(authenticator_selection_criteria),
@@ -908,8 +911,7 @@
         request_delegate_->UpdateLastTransportUsed(*transport_used);
       }
 
-      if (attestation_preference_ !=
-          blink::mojom::AttestationConveyancePreference::NONE) {
+      if (attestation_requested_) {
         // Cryptotoken requests may bypass the attestation prompt because the
         // extension implements its own. Invoking the attestation prompt code
         // here would not work anyway, because the WebContents associated with
@@ -975,8 +977,7 @@
     return;
   }
 
-  DCHECK(attestation_preference_ !=
-         blink::mojom::AttestationConveyancePreference::NONE);
+  DCHECK(attestation_requested_);
 
   if (!attestation_permitted) {
     UMA_HISTOGRAM_ENUMERATION("WebAuthentication.AttestationPromptResult",
@@ -1157,6 +1158,7 @@
   get_assertion_response_callback_.Reset();
   client_data_json_.clear();
   app_id_.reset();
+  attestation_requested_ = false;
 }
 
 BrowserContext* AuthenticatorImpl::browser_context() const {
diff --git a/content/browser/webauth/authenticator_impl.h b/content/browser/webauth/authenticator_impl.h
index 1f41fd9..85de240c 100644
--- a/content/browser/webauth/authenticator_impl.h
+++ b/content/browser/webauth/authenticator_impl.h
@@ -183,7 +183,7 @@
   MakeCredentialCallback make_credential_response_callback_;
   GetAssertionCallback get_assertion_response_callback_;
   std::string client_data_json_;
-  blink::mojom::AttestationConveyancePreference attestation_preference_;
+  bool attestation_requested_;
   url::Origin caller_origin_;
   std::string relying_party_id_;
   std::unique_ptr<base::OneShotTimer> timer_;
diff --git a/content/browser/webauth/authenticator_type_converters.cc b/content/browser/webauth/authenticator_type_converters.cc
index 90a1118..c032346 100644
--- a/content/browser/webauth/authenticator_type_converters.cc
+++ b/content/browser/webauth/authenticator_type_converters.cc
@@ -13,16 +13,17 @@
 
 namespace mojo {
 
-using ::blink::mojom::PublicKeyCredentialUserEntityPtr;
-using ::blink::mojom::PublicKeyCredentialRpEntityPtr;
-using ::blink::mojom::AuthenticatorTransport;
-using ::blink::mojom::PublicKeyCredentialType;
-using ::blink::mojom::PublicKeyCredentialParametersPtr;
-using ::blink::mojom::PublicKeyCredentialDescriptorPtr;
-using ::blink::mojom::AuthenticatorSelectionCriteriaPtr;
+using ::blink::mojom::AttestationConveyancePreference;
 using ::blink::mojom::AuthenticatorAttachment;
-using ::blink::mojom::UserVerificationRequirement;
+using ::blink::mojom::AuthenticatorSelectionCriteriaPtr;
+using ::blink::mojom::AuthenticatorTransport;
 using ::blink::mojom::CableAuthenticationPtr;
+using ::blink::mojom::PublicKeyCredentialDescriptorPtr;
+using ::blink::mojom::PublicKeyCredentialParametersPtr;
+using ::blink::mojom::PublicKeyCredentialRpEntityPtr;
+using ::blink::mojom::PublicKeyCredentialType;
+using ::blink::mojom::PublicKeyCredentialUserEntityPtr;
+using ::blink::mojom::UserVerificationRequirement;
 
 // static
 ::device::FidoTransportProtocol
@@ -218,4 +219,23 @@
   return discovery_data;
 }
 
+// static
+::device::AttestationConveyancePreference
+TypeConverter<::device::AttestationConveyancePreference,
+              AttestationConveyancePreference>::
+    Convert(const AttestationConveyancePreference& input) {
+  switch (input) {
+    case AttestationConveyancePreference::NONE:
+      return ::device::AttestationConveyancePreference::NONE;
+    case AttestationConveyancePreference::INDIRECT:
+      return ::device::AttestationConveyancePreference::INDIRECT;
+    case AttestationConveyancePreference::DIRECT:
+      return ::device::AttestationConveyancePreference::DIRECT;
+    case AttestationConveyancePreference::ENTERPRISE:
+      return ::device::AttestationConveyancePreference::ENTERPRISE;
+  }
+  NOTREACHED();
+  return ::device::AttestationConveyancePreference::NONE;
+}
+
 }  // namespace mojo
diff --git a/content/browser/webauth/authenticator_type_converters.h b/content/browser/webauth/authenticator_type_converters.h
index 6a6c8f9..ef27077 100644
--- a/content/browser/webauth/authenticator_type_converters.h
+++ b/content/browser/webauth/authenticator_type_converters.h
@@ -104,6 +104,13 @@
       const std::vector<::blink::mojom::CableAuthenticationPtr>& input);
 };
 
+template <>
+struct TypeConverter<::device::AttestationConveyancePreference,
+                     ::blink::mojom::AttestationConveyancePreference> {
+  static ::device::AttestationConveyancePreference Convert(
+      const ::blink::mojom::AttestationConveyancePreference& input);
+};
+
 }  // namespace mojo
 
 #endif  // CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_
diff --git a/content/browser/worker_host/DEPS b/content/browser/worker_host/DEPS
index 749142b..fab9ec6 100644
--- a/content/browser/worker_host/DEPS
+++ b/content/browser/worker_host/DEPS
@@ -1,4 +1,3 @@
 include_rules = [
   "+third_party/blink/public/platform/web_feature.mojom.h",
-  "+third_party/blink/public/web/worker_content_settings_proxy.mojom.h",
 ]
diff --git a/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
index 0b68858..c58bc18 100644
--- a/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
+++ b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
@@ -10,7 +10,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/resource_context.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 2f193894..a81b137f 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -31,8 +31,8 @@
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 #include "third_party/blink/public/platform/web_feature.mojom.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom.h"
 
 namespace content {
 namespace {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index f13acba0..43c30e3 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -181,8 +181,6 @@
     "mac/font_loader.mm",
     "media/aec_dump_messages.h",
     "media/cdm_info.cc",
-    "media/media_devices.cc",
-    "media/media_devices.h",
     "media/media_player_delegate_messages.h",
     "media/media_player_messages_android.h",
     "media/peer_connection_tracker_messages.h",
diff --git a/content/common/bluetooth/web_bluetooth_device_id.cc b/content/common/bluetooth/web_bluetooth_device_id.cc
index 8dbae425..4d6f095 100644
--- a/content/common/bluetooth/web_bluetooth_device_id.cc
+++ b/content/common/bluetooth/web_bluetooth_device_id.cc
@@ -16,11 +16,10 @@
 
 }  // namespace
 
-WebBluetoothDeviceId::WebBluetoothDeviceId() : is_mac_address_(false) {}
+WebBluetoothDeviceId::WebBluetoothDeviceId() {}
 
-WebBluetoothDeviceId::WebBluetoothDeviceId(std::string device_id,
-                                           bool is_mac_address)
-    : device_id_(std::move(device_id)), is_mac_address_(is_mac_address) {
+WebBluetoothDeviceId::WebBluetoothDeviceId(std::string device_id)
+    : device_id_(std::move(device_id)) {
   CHECK(IsValid());
 }
 
@@ -43,17 +42,11 @@
 
   base::Base64Encode(bytes, &bytes);
 
-  return WebBluetoothDeviceId(std::move(bytes), false);
+  return WebBluetoothDeviceId(std::move(bytes));
 }
 
 // static
-bool WebBluetoothDeviceId::IsValid(const std::string& device_id,
-                                   bool is_mac_address) {
-  if (is_mac_address) {
-    // TODO(dougt) We should validate this as a MAC address.
-    return true;
-  }
-
+bool WebBluetoothDeviceId::IsValid(const std::string& device_id) {
   std::string decoded;
   if (!base::Base64Decode(device_id, &decoded)) {
     return false;
@@ -77,7 +70,7 @@
 }
 
 bool WebBluetoothDeviceId::IsValid() const {
-  return WebBluetoothDeviceId::IsValid(device_id_, is_mac_address_);
+  return WebBluetoothDeviceId::IsValid(device_id_);
 }
 
 bool WebBluetoothDeviceId::operator==(
diff --git a/content/common/bluetooth/web_bluetooth_device_id.h b/content/common/bluetooth/web_bluetooth_device_id.h
index cba30bc..87445e8 100644
--- a/content/common/bluetooth/web_bluetooth_device_id.h
+++ b/content/common/bluetooth/web_bluetooth_device_id.h
@@ -22,9 +22,8 @@
   // resulting object will DCHECK-fail.
   WebBluetoothDeviceId();
 
-  // CHECKS that |device_id| is valid if |is_mac_address| is false.
-  explicit WebBluetoothDeviceId(std::string device_id,
-                                bool is_mac_address = false);
+  // CHECKS that |device_id| is valid.
+  explicit WebBluetoothDeviceId(std::string device_id);
   ~WebBluetoothDeviceId();
 
   // Returns the string that represents this WebBluetoothDeviceId.
@@ -34,12 +33,9 @@
   // string and base64-encoding it.
   static WebBluetoothDeviceId Create();
 
-  // If |is_mac_address| is true, the method will return true if |device_id| is
-  // a MAC address.  If |is_mac_address| is false, this method will return true
-  // if |device_id| results in a 128bit base64-encoding string. Otherwise
-  // returns false.
-  static bool IsValid(const std::string& device_id,
-                      bool is_mac_address = false);
+  // This method will return true. if |device_id| results in a 128bit
+  // base64-encoding string. Otherwise returns false.
+  static bool IsValid(const std::string& device_id);
 
   bool IsValid() const;
 
@@ -48,7 +44,6 @@
 
  private:
   std::string device_id_;
-  bool is_mac_address_;
 };
 
 // This is required by gtest to print a readable output on test failures.
diff --git a/content/common/mac/font_loader.mm b/content/common/mac/font_loader.mm
index 1fafe0e..6b6a8a71 100644
--- a/content/common/mac/font_loader.mm
+++ b/content/common/mac/font_loader.mm
@@ -19,7 +19,7 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
-#include "base/threading/thread_restrictions.h"
+#include "base/threading/scoped_blocking_call.h"
 
 namespace content {
 namespace {
@@ -27,7 +27,7 @@
 std::unique_ptr<FontLoader::ResultInternal> LoadFontOnFileThread(
     const base::string16& font_name,
     const float font_point_size) {
-  base::AssertBlockingAllowedDeprecated();
+  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
 
   NSString* font_name_ns = base::SysUTF16ToNSString(font_name);
   NSFont* font_to_encode =
diff --git a/content/common/media/media_devices.cc b/content/common/media/media_devices.cc
deleted file mode 100644
index b2ed9e4..0000000
--- a/content/common/media/media_devices.cc
+++ /dev/null
@@ -1,55 +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.
-
-#include "content/common/media/media_devices.h"
-#include "media/audio/audio_device_description.h"
-#include "media/capture/video/video_capture_device_descriptor.h"
-
-namespace content {
-
-MediaDeviceInfo::MediaDeviceInfo()
-    : video_facing(media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {}
-
-MediaDeviceInfo::MediaDeviceInfo(const MediaDeviceInfo& other) = default;
-
-MediaDeviceInfo::MediaDeviceInfo(MediaDeviceInfo&& other) = default;
-
-MediaDeviceInfo::MediaDeviceInfo(const std::string& device_id,
-                                 const std::string& label,
-                                 const std::string& group_id,
-                                 media::VideoFacingMode video_facing)
-    : device_id(device_id),
-      label(label),
-      group_id(group_id),
-      video_facing(video_facing) {}
-
-MediaDeviceInfo::MediaDeviceInfo(
-    const media::AudioDeviceDescription& device_description)
-    : device_id(device_description.unique_id),
-      label(device_description.device_name),
-      group_id(device_description.group_id),
-      video_facing(media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {}
-
-MediaDeviceInfo::MediaDeviceInfo(
-    const media::VideoCaptureDeviceDescriptor& descriptor)
-    : device_id(descriptor.device_id),
-      label(descriptor.GetNameAndModel()),
-      video_facing(descriptor.facing) {}
-
-MediaDeviceInfo::~MediaDeviceInfo() = default;
-
-MediaDeviceInfo& MediaDeviceInfo::operator=(const MediaDeviceInfo& other) =
-    default;
-
-MediaDeviceInfo& MediaDeviceInfo::operator=(MediaDeviceInfo&& other) = default;
-
-bool operator==(const MediaDeviceInfo& first, const MediaDeviceInfo& second) {
-  // Do not use the |group_id| and |video_facing| fields for equality comparison
-  // since they are currently not fully supported by the video-capture layer.
-  // The modification of those fields by heuristics in upper layers does not
-  // result in a different device.
-  return first.device_id == second.device_id && first.label == second.label;
-}
-
-}  // namespace content
diff --git a/content/common/media/media_devices.h b/content/common/media/media_devices.h
deleted file mode 100644
index 0e3de71..0000000
--- a/content/common/media/media_devices.h
+++ /dev/null
@@ -1,60 +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.
-
-#ifndef CONTENT_COMMON_MEDIA_MEDIA_DEVICES_H_
-#define CONTENT_COMMON_MEDIA_MEDIA_DEVICES_H_
-
-#include <string>
-#include <vector>
-
-#include "content/common/content_export.h"
-#include "media/base/video_facing.h"
-
-namespace media {
-struct AudioDeviceDescription;
-struct VideoCaptureDeviceDescriptor;
-}  // namespace media
-
-namespace content {
-
-enum MediaDeviceType {
-  MEDIA_DEVICE_TYPE_AUDIO_INPUT,
-  MEDIA_DEVICE_TYPE_VIDEO_INPUT,
-  MEDIA_DEVICE_TYPE_AUDIO_OUTPUT,
-  NUM_MEDIA_DEVICE_TYPES,
-};
-
-struct CONTENT_EXPORT MediaDeviceInfo {
-  MediaDeviceInfo();
-  MediaDeviceInfo(const MediaDeviceInfo& other);
-  MediaDeviceInfo(MediaDeviceInfo&& other);
-  MediaDeviceInfo(
-      const std::string& device_id,
-      const std::string& label,
-      const std::string& group_id,
-      media::VideoFacingMode video_facing = media::MEDIA_VIDEO_FACING_NONE);
-  explicit MediaDeviceInfo(const media::AudioDeviceDescription& description);
-  explicit MediaDeviceInfo(
-      const media::VideoCaptureDeviceDescriptor& descriptor);
-  ~MediaDeviceInfo();
-  MediaDeviceInfo& operator=(const MediaDeviceInfo& other);
-  MediaDeviceInfo& operator=(MediaDeviceInfo&& other);
-
-  std::string device_id;
-  std::string label;
-  std::string group_id;
-  media::VideoFacingMode video_facing;
-};
-
-using MediaDeviceInfoArray = std::vector<MediaDeviceInfo>;
-
-bool operator==(const MediaDeviceInfo& first, const MediaDeviceInfo& second);
-
-inline bool IsValidMediaDeviceType(MediaDeviceType type) {
-  return type >= 0 && type < NUM_MEDIA_DEVICE_TYPES;
-}
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_MEDIA_MEDIA_DEVICES_H_
diff --git a/content/common/media/media_devices.typemap b/content/common/media/media_devices.typemap
deleted file mode 100644
index eaf7eb51..0000000
--- a/content/common/media/media_devices.typemap
+++ /dev/null
@@ -1,15 +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.
-
-mojom = "//third_party/blink/public/platform/modules/mediastream/media_devices.mojom"
-public_headers = [ "//content/common/media/media_devices.h" ]
-traits_headers = [ "//content/common/media/media_devices_mojom_traits.h" ]
-sources = [
-  "//content/common/media/media_devices_mojom_traits.cc",
-]
-type_mappings = [
-  "blink.mojom.MediaDeviceType=content::MediaDeviceType",
-  "blink.mojom.MediaDeviceInfo=content::MediaDeviceInfo",
-  "blink.mojom.FacingMode=media::VideoFacingMode",
-]
diff --git a/content/common/media/media_devices_mojom_traits.h b/content/common/media/media_devices_mojom_traits.h
deleted file mode 100644
index 8274b11..0000000
--- a/content/common/media/media_devices_mojom_traits.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_COMMON_MEDIA_MEDIA_DEVICES_MOJOM_TRAITS_H_
-#define CONTENT_COMMON_MEDIA_MEDIA_DEVICES_MOJOM_TRAITS_H_
-
-#include "content/common/media/media_devices.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<blink::mojom::MediaDeviceType, content::MediaDeviceType> {
-  static blink::mojom::MediaDeviceType ToMojom(content::MediaDeviceType type);
-
-  static bool FromMojom(blink::mojom::MediaDeviceType input,
-                        content::MediaDeviceType* out);
-};
-
-template <>
-struct EnumTraits<blink::mojom::FacingMode, media::VideoFacingMode> {
-  static blink::mojom::FacingMode ToMojom(media::VideoFacingMode facing_mode);
-
-  static bool FromMojom(blink::mojom::FacingMode input,
-                        media::VideoFacingMode* out);
-};
-
-template <>
-struct StructTraits<blink::mojom::MediaDeviceInfoDataView,
-                    content::MediaDeviceInfo> {
-  static const std::string& device_id(const content::MediaDeviceInfo& info) {
-    return info.device_id;
-  }
-
-  static const std::string& label(const content::MediaDeviceInfo& info) {
-    return info.label;
-  }
-
-  static const std::string& group_id(const content::MediaDeviceInfo& info) {
-    return info.group_id;
-  }
-
-  static bool Read(blink::mojom::MediaDeviceInfoDataView input,
-                   content::MediaDeviceInfo* out);
-};
-
-}  // namespace mojo
-
-#endif  // CONTENT_COMMON_MEDIA_MEDIA_DEVICES_MOJOM_TRAITS_H_
diff --git a/content/common/media/media_devices_unittest.cc b/content/common/media/media_devices_unittest.cc
deleted file mode 100644
index d6fd624..0000000
--- a/content/common/media/media_devices_unittest.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/common/media/media_devices.h"
-#include "media/audio/audio_device_description.h"
-#include "media/capture/video/video_capture_device_descriptor.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-TEST(MediaDevicesTest, MediaDeviceInfoFromAudioDescription) {
-  const std::string kFakeDeviceID = "fake_device_id";
-  const std::string kFakeLabel = "fake_label";
-  const std::string kFakeGroupID = "fake_group_id";
-
-  media::AudioDeviceDescription description(kFakeLabel, kFakeDeviceID,
-                                            kFakeGroupID);
-  MediaDeviceInfo device_info(description);
-  EXPECT_EQ(kFakeDeviceID, device_info.device_id);
-  EXPECT_EQ(kFakeLabel, device_info.label);
-  EXPECT_EQ(kFakeGroupID, device_info.group_id);
-}
-
-TEST(MediaDevicesTest, MediaDeviceInfoFromVideoDescriptor) {
-  media::VideoCaptureDeviceDescriptor descriptor(
-      "display_name", "device_id", "model_id", media::VideoCaptureApi::UNKNOWN);
-
-  // TODO(guidou): Add test for group ID when supported. See crbug.com/627793.
-  MediaDeviceInfo device_info(descriptor);
-  EXPECT_EQ(descriptor.device_id, device_info.device_id);
-  EXPECT_EQ(descriptor.GetNameAndModel(), device_info.label);
-}
-
-}  // namespace content
diff --git a/content/common/page_state_serialization.cc b/content/common/page_state_serialization.cc
index 1b671604..d717926 100644
--- a/content/common/page_state_serialization.cc
+++ b/content/common/page_state_serialization.cc
@@ -75,7 +75,7 @@
     const std::vector<network::DataElement>& elements,
     std::vector<base::Optional<base::string16>>* referenced_files) {
   for (size_t i = 0; i < elements.size(); ++i) {
-    if (elements[i].type() == network::DataElement::TYPE_FILE)
+    if (elements[i].type() == network::mojom::DataElementType::kFile)
       referenced_files->emplace_back(elements[i].path().AsUTF16Unsafe());
   }
 }
@@ -413,22 +413,22 @@
   WriteAndValidateVectorSize(*request_body.elements(), obj);
   for (const auto& element : *request_body.elements()) {
     switch (element.type()) {
-      case network::DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         WriteInteger(blink::WebHTTPBody::Element::kTypeData, obj);
         WriteData(element.bytes(), static_cast<int>(element.length()), obj);
         break;
-      case network::DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         WriteInteger(blink::WebHTTPBody::Element::kTypeFile, obj);
         WriteString(element.path().AsUTF16Unsafe(), obj);
         WriteInteger64(static_cast<int64_t>(element.offset()), obj);
         WriteInteger64(static_cast<int64_t>(element.length()), obj);
         WriteReal(element.expected_modification_time().ToDoubleT(), obj);
         break;
-      case network::DataElement::TYPE_BLOB:
+      case network::mojom::DataElementType::kBlob:
         WriteInteger(blink::WebHTTPBody::Element::kTypeBlob, obj);
         WriteStdString(element.blob_uuid(), obj);
         break;
-      case network::DataElement::TYPE_RAW_FILE:
+      case network::mojom::DataElementType::kRawFile:
       default:
         NOTREACHED();
         continue;
@@ -687,28 +687,28 @@
   for (const auto& element : *request_body.elements()) {
     history::mojom::ElementPtr data_element = history::mojom::Element::New();
     switch (element.type()) {
-      case network::DataElement::TYPE_BYTES: {
+      case network::mojom::DataElementType::kBytes: {
         data_element->set_bytes(std::vector<unsigned char>(
             reinterpret_cast<const char*>(element.bytes()),
             element.bytes() + element.length()));
         break;
       }
-      case network::DataElement::TYPE_FILE: {
+      case network::mojom::DataElementType::kFile: {
         history::mojom::FilePtr file = history::mojom::File::New(
             element.path().AsUTF16Unsafe(), element.offset(), element.length(),
             element.expected_modification_time());
         data_element->set_file(std::move(file));
         break;
       }
-      case network::DataElement::TYPE_BLOB:
+      case network::mojom::DataElementType::kBlob:
         data_element->set_blob_uuid(element.blob_uuid());
         break;
-      case network::DataElement::TYPE_DATA_PIPE:
+      case network::mojom::DataElementType::kDataPipe:
         NOTIMPLEMENTED();
         break;
-      case network::DataElement::TYPE_RAW_FILE:
-      case network::DataElement::TYPE_CHUNKED_DATA_PIPE:
-      case network::DataElement::TYPE_UNKNOWN:
+      case network::mojom::DataElementType::kRawFile:
+      case network::mojom::DataElementType::kChunkedDataPipe:
+      case network::mojom::DataElementType::kUnknown:
         NOTREACHED();
         continue;
     }
diff --git a/content/common/page_state_serialization_unittest.cc b/content/common/page_state_serialization_unittest.cc
index 70a47c1a..80cd7d6 100644
--- a/content/common/page_state_serialization_unittest.cc
+++ b/content/common/page_state_serialization_unittest.cc
@@ -40,8 +40,8 @@
 void ExpectEquality(const network::DataElement& expected,
                     const network::DataElement& actual) {
   EXPECT_EQ(expected.type(), actual.type());
-  if (expected.type() == network::DataElement::TYPE_BYTES &&
-      actual.type() == network::DataElement::TYPE_BYTES) {
+  if (expected.type() == network::mojom::DataElementType::kBytes &&
+      actual.type() == network::mojom::DataElementType::kBytes) {
     EXPECT_EQ(std::string(expected.bytes(), expected.length()),
               std::string(actual.bytes(), actual.length()));
   }
diff --git a/content/common/service_worker/embedded_worker.mojom b/content/common/service_worker/embedded_worker.mojom
index 4270b107..97ef7142 100644
--- a/content/common/service_worker/embedded_worker.mojom
+++ b/content/common/service_worker/embedded_worker.mojom
@@ -11,6 +11,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/service_manager/public/mojom/interface_provider.mojom";
+import "third_party/blink/public/mojom/devtools/devtools_agent.mojom";
 import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
 import "third_party/blink/public/mojom/script/script_type.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
@@ -19,10 +20,9 @@
 import "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom";
+import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
 import "third_party/blink/public/platform/web_feature.mojom";
 import "third_party/blink/public/web/console_message.mojom";
-import "third_party/blink/public/mojom/devtools/devtools_agent.mojom";
-import "third_party/blink/public/web/worker_content_settings_proxy.mojom";
 import "url/mojom/url.mojom";
 
 // Parameters to launch a service worker. This is passed from the browser to the
diff --git a/content/common/shared_worker/shared_worker_factory.mojom b/content/common/shared_worker/shared_worker_factory.mojom
index c3ce2e29..1639bd9 100644
--- a/content/common/shared_worker/shared_worker_factory.mojom
+++ b/content/common/shared_worker/shared_worker_factory.mojom
@@ -15,8 +15,8 @@
 import "third_party/blink/public/mojom/worker/shared_worker.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_host.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_info.mojom";
+import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
 import "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom";
-import "third_party/blink/public/web/worker_content_settings_proxy.mojom";
 
 // The name of the InterfaceProviderSpec in service manifests used by the
 // frame tree to expose shared-worker-specific interfaces between renderer and
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index 502eb932..bf88eca 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -377,6 +377,10 @@
 
   network::mojom::URLLoaderClientPtr client;
   client_binding_.Bind(mojo::MakeRequest(&client), start_info_->task_runner);
+
+  // TODO(https://crbug.com/919736): Remove this call.
+  client_binding_.EnableBatchDispatch();
+
   client_binding_.set_connection_error_handler(base::BindOnce(
       &ThrottlingURLLoader::OnClientConnectionError, base::Unretained(this)));
 
diff --git a/content/common/typemaps.gni b/content/common/typemaps.gni
index 31b5ea284..5476d2d 100644
--- a/content/common/typemaps.gni
+++ b/content/common/typemaps.gni
@@ -6,7 +6,6 @@
   "//content/common/frame.typemap",
   "//content/common/frame_messages.typemap",
   "//content/common/input/synchronous_compositor.typemap",
-  "//content/common/media/media_devices.typemap",
   "//content/common/native_types.typemap",
   "//content/common/native_types_mac.typemap",
   "//content/common/navigation_params.typemap",
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 01298f3..5d811b8 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -259,6 +259,7 @@
     "java/src/org/chromium/content_public/browser/MotionEventSynthesizer.java",
     "java/src/org/chromium/content_public/browser/NavigationController.java",
     "java/src/org/chromium/content_public/browser/NavigationEntry.java",
+    "java/src/org/chromium/content_public/browser/NavigationHandleProxy.java",
     "java/src/org/chromium/content_public/browser/NavigationHistory.java",
     "java/src/org/chromium/content_public/browser/RenderCoordinates.java",
     "java/src/org/chromium/content_public/browser/RenderFrameHost.java",
@@ -405,6 +406,7 @@
     "java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java",
     "java/src/org/chromium/content/common/ServiceManagerConnectionImpl.java",
     "java/src/org/chromium/content_public/browser/LoadUrlParams.java",
+    "java/src/org/chromium/content_public/browser/NavigationHandleProxy.java",
     "java/src/org/chromium/content_public/common/ResourceRequestBody.java",
     "java/src/org/chromium/content_public/common/UseZoomForDSFPolicy.java",
   ]
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
index ff10448..79909bd4 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -78,10 +78,20 @@
     @Override
     @CalledByNative
     public void didStartNavigation(
-            String url, boolean isInMainFrame, boolean isSameDocument, boolean isErrorPage) {
+            String url, boolean isInMainFrame, boolean isSameDocument, long navigationHandleProxy) {
         for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
             mObserversIterator.next().didStartNavigation(
-                    url, isInMainFrame, isSameDocument, isErrorPage);
+                    url, isInMainFrame, isSameDocument, navigationHandleProxy);
+        }
+    }
+
+    @Override
+    @CalledByNative
+    public void didRedirectNavigation(
+            String url, boolean isInMainFrame, long navigationHandleProxy) {
+        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
+            mObserversIterator.next().didRedirectNavigation(
+                    url, isInMainFrame, navigationHandleProxy);
         }
     }
 
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandleProxy.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandleProxy.java
new file mode 100644
index 0000000..3b9be3c3
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandleProxy.java
@@ -0,0 +1,18 @@
+// 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.
+
+package org.chromium.content_public.browser;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * JNI bridge with content::NavigationHandleProxy.
+ */
+@JNINamespace("content")
+public class NavigationHandleProxy {
+    public static native void nativeSetRequestHeader(
+            long nativeNavigationHandleProxy, String headerName, String headerValue);
+    public static native void nativeRemoveRequestHeader(
+            long nativeNavigationHandleProxy, String headerName);
+}
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
index 6eb9f49c..649a54d 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -42,10 +42,23 @@
      * @param isInMainFrame Whether the navigation is for the main frame.
      * @param isSameDocument Whether the main frame navigation did not cause changes to the
      *                   document (for example scrolling to a named anchor or PopState).
-     * @param isErrorPage Whether the navigation shows an error page.
+     * @param navigationHandleProxy Pointer to a NavigationHandleProxy representing the navigation.
+     *                              Its lifetime is bound to this function. Do not store it. It can
+     *                              be used to modify headers.
      */
-    public void didStartNavigation(
-            String url, boolean isInMainFrame, boolean isSameDocument, boolean isErrorPage) {}
+    public void didStartNavigation(String url, boolean isInMainFrame, boolean isSameDocument,
+            long navigationHandleProxy) {}
+
+    /**
+     * Called when the browser process redirect a navigation.
+     * @param url The validated URL for the loading page.
+     * @param isInMainFrame Whether the navigation is for the main frame.
+     * @param navigationHandleProxy Pointer to a NavigationHandleProxy representing the navigation.
+     *                              Its lifetime is bound to this function. Do not store it. It can
+     *                              be used to modify headers.
+     */
+    public void didRedirectNavigation(
+            String url, boolean isInMainFrame, long navigationHandleProxy) {}
 
     /**
      * Called when the current navigation is finished. This happens when a navigation is committed,
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
index 325c1c1..ac2f6df 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
@@ -22,7 +22,6 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.task.SchedulerTestHelpers;
 import org.chromium.base.test.task.TaskSchedulerTestHelpers;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content.app.ContentMain;
 import org.chromium.content_public.browser.test.NativeLibraryTestRule;
@@ -86,10 +85,8 @@
         SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
     }
 
-    // TODO(crbug.com/920678) Disabled for flakiness.
     @Test
     @MediumTest
-    @DisabledTest
     public void testCreateSequencedTaskRunner() throws Exception {
         startNativeScheduler();
         TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
@@ -102,10 +99,8 @@
         assertThat(orderList, contains(1, 2, 3));
     }
 
-    // TODO(crbug.com/920678) Disabled for flakiness.
     @Test
     @MediumTest
-    @DisabledTest
     public void testCreateSingleThreadSequencedTaskRunner() throws Exception {
         startNativeScheduler();
         TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
diff --git a/content/public/browser/browser_main_runner.h b/content/public/browser/browser_main_runner.h
index c01be95..744eba0 100644
--- a/content/public/browser/browser_main_runner.h
+++ b/content/public/browser/browser_main_runner.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_PUBLIC_BROWSER_BROWSER_MAIN_RUNNER_H_
 #define CONTENT_PUBLIC_BROWSER_BROWSER_MAIN_RUNNER_H_
 
+#include <memory>
+
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 
@@ -18,7 +20,7 @@
   virtual ~BrowserMainRunner() {}
 
   // Create a new BrowserMainRunner object.
-  static BrowserMainRunner* Create();
+  static std::unique_ptr<BrowserMainRunner> Create();
 
   // Returns true if the BrowserMainRunner has exited the main loop.
   static bool ExitedMainMessageLoop();
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index dc653fc3..bce9b68 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -912,4 +912,9 @@
   return std::string();
 }
 
+bool ContentBrowserClient::IsBuiltinComponent(BrowserContext* browser_context,
+                                              const url::Origin& origin) {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 2e5e199..92425b0 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1473,6 +1473,11 @@
 
   // Returns the user agent.  Content may cache this value.
   virtual std::string GetUserAgent() const;
+
+  // Returns whether |origin| should be considered a integral component similar
+  // to native code, and as such whether its log messages should be recorded.
+  virtual bool IsBuiltinComponent(BrowserContext* browser_context,
+                                  const url::Origin& origin);
 };
 
 }  // namespace content
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index c33548f..d20c820 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -237,6 +237,17 @@
   // Returns the headers used for this request.
   virtual const net::HttpRequestHeaders& GetRequestHeaders() = 0;
 
+  // Remove a request's header. If the header is not present, it has no effect.
+  // Must be called during a redirect.
+  virtual void RemoveRequestHeader(const std::string& header_name) = 0;
+
+  // Set a request's header. If the header is already present, its value is
+  // overwritten. When modified during a navigation start, the headers will be
+  // applied to the initial network request. When modified during a redirect,
+  // the headers will be applied to the redirected request.
+  virtual void SetRequestHeader(const std::string& header_name,
+                                const std::string& header_value) = 0;
+
   // Returns the response headers for the request, or nullptr if there aren't
   // any response headers or they have not been received yet. The response
   // headers may change during the navigation (e.g. after encountering a server
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index d28cad1..c2044686 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -180,6 +180,17 @@
                                    StartWorkerCallback info_callback,
                                    base::OnceClosure failure_callback) = 0;
 
+  // Starts the active worker of the registration for the given |scope| and
+  // dispatches the given |message| to the service worker. |result_callback|
+  // is passed a success boolean indicating whether the message was dispatched
+  // successfully.
+  //
+  // Must be called on IO thread.
+  virtual void StartServiceWorkerAndDispatchMessage(
+      const GURL& scope,
+      blink::TransferableMessage message,
+      ResultCallback result_callback) = 0;
+
   // Deprecated: DO NOT USE
   // This is a temporary addition only to be used for the Android Messages
   // integration with ChromeOS (http://crbug.com/823256).  The removal is
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 7c7d0211..db06fa6f 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -161,7 +161,7 @@
 // font name or postscript name. Rolling out behind a flag, as enabling this
 // enables a font indexer on Android which we need to test in the field first.
 const base::Feature kFontSrcLocalMatching{"FontSrcLocalMatching",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables a security restriction on iframes navigating their top frame.
 // When enabled, the navigation will only be permitted if the iframe is
@@ -317,7 +317,7 @@
 // Whether document level wheel and mousewheel event listeners should default
 // 'passive' to true.
 const base::Feature kPassiveDocumentWheelEventListeners{
-    "PassiveDocumentWheelEventListeners", base::FEATURE_DISABLED_BY_DEFAULT};
+    "PassiveDocumentWheelEventListeners", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Whether we should force a touchstart and first touchmove per scroll event
 // listeners to be non-blocking during fling.
diff --git a/content/public/test/fake_service_worker_context.cc b/content/public/test/fake_service_worker_context.cc
index 65c39008..0ee205d 100644
--- a/content/public/test/fake_service_worker_context.cc
+++ b/content/public/test/fake_service_worker_context.cc
@@ -83,6 +83,12 @@
   start_service_worker_for_navigation_hint_called_ = true;
 }
 
+void FakeServiceWorkerContext::StartServiceWorkerAndDispatchMessage(
+    const GURL& scope,
+    blink::TransferableMessage message,
+    ResultCallback result_callback) {
+  NOTREACHED();
+}
 void FakeServiceWorkerContext::StartServiceWorkerAndDispatchLongRunningMessage(
     const GURL& scope,
     blink::TransferableMessage message,
diff --git a/content/public/test/fake_service_worker_context.h b/content/public/test/fake_service_worker_context.h
index 31b02ae..3528e8a 100644
--- a/content/public/test/fake_service_worker_context.h
+++ b/content/public/test/fake_service_worker_context.h
@@ -56,6 +56,10 @@
       const GURL& scope,
       ServiceWorkerContext::StartWorkerCallback info_callback,
       base::OnceClosure failure_callback) override;
+  void StartServiceWorkerAndDispatchMessage(
+      const GURL& scope,
+      blink::TransferableMessage message,
+      FakeServiceWorkerContext::ResultCallback result_callback) override;
   void StartServiceWorkerAndDispatchLongRunningMessage(
       const GURL& scope,
       blink::TransferableMessage message,
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index f075b583..cd3fd88 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -69,6 +69,8 @@
   const net::HttpRequestHeaders& GetRequestHeaders() override {
     return request_headers_;
   }
+  MOCK_METHOD1(RemoveRequestHeader, void(const std::string&));
+  MOCK_METHOD2(SetRequestHeader, void(const std::string&, const std::string&));
   const net::HttpResponseHeaders* GetResponseHeaders() override {
     return response_headers_;
   }
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index cbe32d83..1b40b434 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -92,9 +92,9 @@
     // Simulate the Widget receiving a close message. This should result on
     // releasing the internal reference counts and destroying the internal
     // state.
-    WidgetMsg_Close msg(render_view->GetRoutingID());
     RenderWidget* render_widget =
         static_cast<RenderViewImpl*>(render_view)->GetWidget();
+    WidgetMsg_Close msg(render_widget->routing_id());
     render_widget->OnMessageReceived(msg);
     return true;
   }
diff --git a/content/public/test/url_loader_interceptor.cc b/content/public/test/url_loader_interceptor.cc
index 5e14fd3..c13443d 100644
--- a/content/public/test/url_loader_interceptor.cc
+++ b/content/public/test/url_loader_interceptor.cc
@@ -249,28 +249,16 @@
               ->get();
         }));
     url_loader_factory_getter_->SetNetworkFactoryForTesting(
-        frame_interceptor_.get(), false);
-
-    frame_interceptor_corb_enabled_ = std::make_unique<Interceptor>(
-        parent, base::BindRepeating([]() { return 0; }),
-        base::BindLambdaForTesting([=]() -> network::mojom::URLLoaderFactory* {
-          return url_loader_factory_getter
-              ->original_network_factory__corb_enabled_for_testing()
-              ->get();
-        }));
-    url_loader_factory_getter_->SetNetworkFactoryForTesting(
-        frame_interceptor_corb_enabled_.get(), true);
+        frame_interceptor_.get());
   }
 
   ~URLLoaderFactoryGetterWrapper() {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr, false);
-    url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr, true);
+    url_loader_factory_getter_->SetNetworkFactoryForTesting(nullptr);
   }
 
  private:
   std::unique_ptr<Interceptor> frame_interceptor_;
-  std::unique_ptr<Interceptor> frame_interceptor_corb_enabled_;
   URLLoaderFactoryGetter* url_loader_factory_getter_;
 };
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 9ae2806..db697be 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -673,7 +673,6 @@
     "//storage/common",
     "//third_party/blink/public:blink",
     "//third_party/blink/public:buildflags",
-    "//third_party/blink/public:media_devices_mojo_bindings",
     "//third_party/blink/public:mojo_bindings",
     "//third_party/blink/public/common",
     "//third_party/boringssl",
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index e6be9e7b..1cc10b7 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -520,41 +520,6 @@
   return layer_tree_host_->GetId();
 }
 
-void LayerTreeView::SetShowFPSCounter(bool show) {
-  cc::LayerTreeDebugState debug_state = layer_tree_host_->GetDebugState();
-  debug_state.show_fps_counter = show;
-  layer_tree_host_->SetDebugState(debug_state);
-}
-
-void LayerTreeView::SetShowPaintRects(bool show) {
-  cc::LayerTreeDebugState debug_state = layer_tree_host_->GetDebugState();
-  debug_state.show_paint_rects = show;
-  layer_tree_host_->SetDebugState(debug_state);
-}
-
-void LayerTreeView::SetShowDebugBorders(bool show) {
-  cc::LayerTreeDebugState debug_state = layer_tree_host_->GetDebugState();
-  if (show)
-    debug_state.show_debug_borders.set();
-  else
-    debug_state.show_debug_borders.reset();
-  layer_tree_host_->SetDebugState(debug_state);
-}
-
-void LayerTreeView::SetShowScrollBottleneckRects(bool show) {
-  cc::LayerTreeDebugState debug_state = layer_tree_host_->GetDebugState();
-  debug_state.show_touch_event_handler_rects = show;
-  debug_state.show_wheel_event_handler_rects = show;
-  debug_state.show_non_fast_scrollable_rects = show;
-  layer_tree_host_->SetDebugState(debug_state);
-}
-
-void LayerTreeView::SetShowHitTestBorders(bool show) {
-  cc::LayerTreeDebugState debug_state = layer_tree_host_->GetDebugState();
-  debug_state.show_hit_test_borders = show;
-  layer_tree_host_->SetDebugState(debug_state);
-}
-
 void LayerTreeView::UpdateBrowserControlsState(
     cc::BrowserControlsState constraints,
     cc::BrowserControlsState current,
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 17f56d7..1dc6c373 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -169,11 +169,6 @@
   void SetHaveScrollEventHandlers(bool) override;
   bool HaveScrollEventHandlers() const override;
   int LayerTreeId() const override;
-  void SetShowFPSCounter(bool show) override;
-  void SetShowPaintRects(bool show) override;
-  void SetShowDebugBorders(bool show) override;
-  void SetShowScrollBottleneckRects(bool show) override;
-  void SetShowHitTestBorders(bool show) override;
   void NotifySwapTime(ReportTimeCallback callback) override;
 
   void UpdateBrowserControlsState(cc::BrowserControlsState constraints,
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index 3727d6d..e598f56 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -230,6 +230,7 @@
       enable_fling_passive_listener_flag_(base::FeatureList::IsEnabled(
           features::kPassiveEventListenersDueToFling)),
       needs_low_latency_(false),
+      needs_unbuffered_input_for_debugger_(false),
       allow_raf_aligned_input_(allow_raf_aligned_input),
       main_task_runner_(main_task_runner),
       main_thread_scheduler_(main_thread_scheduler),
@@ -573,7 +574,8 @@
     case blink::WebInputEvent::kMouseWheel:
     case blink::WebInputEvent::kTouchMove:
       return allow_raf_aligned_input_ && !needs_low_latency_ &&
-             !needs_low_latency_until_pointer_up_;
+             !needs_low_latency_until_pointer_up_ &&
+             !needs_unbuffered_input_for_debugger_;
     default:
       return false;
   }
@@ -639,6 +641,10 @@
   needs_low_latency_ = low_latency;
 }
 
+void MainThreadEventQueue::SetNeedsUnbufferedInputForDebugger(bool unbuffered) {
+  needs_unbuffered_input_for_debugger_ = unbuffered;
+}
+
 void MainThreadEventQueue::HasPointerRawMoveEventHandlers(bool has_handlers) {
   has_pointerrawmove_handlers_ = has_handlers;
 }
diff --git a/content/renderer/input/main_thread_event_queue.h b/content/renderer/input/main_thread_event_queue.h
index a5fadd611..37cedf6 100644
--- a/content/renderer/input/main_thread_event_queue.h
+++ b/content/renderer/input/main_thread_event_queue.h
@@ -101,6 +101,8 @@
 
   void ClearClient();
   void SetNeedsLowLatency(bool low_latency);
+  void SetNeedsUnbufferedInputForDebugger(bool unbuffered);
+
   void HasPointerRawMoveEventHandlers(bool has_handlers);
 
   // Request unbuffered input events until next pointerup.
@@ -147,6 +149,7 @@
   bool last_touch_start_forced_nonblocking_due_to_fling_;
   bool enable_fling_passive_listener_flag_;
   bool needs_low_latency_;
+  bool needs_unbuffered_input_for_debugger_;
   bool allow_raf_aligned_input_;
   bool needs_low_latency_until_pointer_up_ = false;
   bool has_pointerrawmove_handlers_ = false;
diff --git a/content/renderer/loader/web_url_request_util.cc b/content/renderer/loader/web_url_request_util.cc
index 32e127e..8d327bd 100644
--- a/content/renderer/loader/web_url_request_util.cc
+++ b/content/renderer/loader/web_url_request_util.cc
@@ -313,10 +313,10 @@
   auto blob_ptr_iter = blob_ptrs.begin();
   for (auto& element : *input.elements()) {
     switch (element.type()) {
-      case network::DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         http_body.AppendData(WebData(element.bytes(), element.length()));
         break;
-      case network::DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         http_body.AppendFileRange(
             blink::FilePathToWebString(element.path()), element.offset(),
             (element.length() != std::numeric_limits<uint64_t>::max())
@@ -324,7 +324,7 @@
                 : -1,
             element.expected_modification_time().ToDoubleT());
         break;
-      case network::DataElement::TYPE_BLOB:
+      case network::mojom::DataElementType::kBlob:
         if (blob_ptrs.empty()) {
           http_body.AppendBlob(WebString::FromASCII(element.blob_uuid()));
         } else {
@@ -334,14 +334,14 @@
                                element.length(), blob.PassHandle());
         }
         break;
-      case network::DataElement::TYPE_DATA_PIPE: {
+      case network::mojom::DataElementType::kDataPipe: {
         http_body.AppendDataPipe(
             element.CloneDataPipeGetter().PassInterface().PassHandle());
         break;
       }
-      case network::DataElement::TYPE_UNKNOWN:
-      case network::DataElement::TYPE_RAW_FILE:
-      case network::DataElement::TYPE_CHUNKED_DATA_PIPE:
+      case network::mojom::DataElementType::kUnknown:
+      case network::mojom::DataElementType::kRawFile:
+      case network::mojom::DataElementType::kChunkedDataPipe:
         NOTREACHED();
         break;
     }
@@ -354,7 +354,7 @@
   std::vector<blink::mojom::BlobPtrInfo> blob_ptrs;
   blink::mojom::BlobRegistryPtr blob_registry;
   for (auto& element : *input.elements()) {
-    if (element.type() == network::DataElement::TYPE_BLOB) {
+    if (element.type() == network::mojom::DataElementType::kBlob) {
       blink::mojom::BlobPtrInfo blob_ptr;
       if (!blob_registry) {
         blink::Platform::Current()->GetInterfaceProvider()->GetInterface(
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index 1afa430..edf23f6 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -27,6 +27,7 @@
 #include "content/renderer/loader/web_url_loader_impl.h"
 #include "content/renderer/loader/web_url_request_util.h"
 #include "content/renderer/service_worker/controller_service_worker_connector.h"
+#include "content/renderer/service_worker/service_worker_network_provider.h"
 #include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "content/renderer/service_worker/service_worker_subresource_loader.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -151,12 +152,13 @@
 };
 
 scoped_refptr<WebWorkerFetchContextImpl> WebWorkerFetchContextImpl::Create(
-    ServiceWorkerProviderContext* provider_context,
+    ServiceWorkerNetworkProvider* network_provider,
     RendererPreferences renderer_preferences,
     mojom::RendererPreferenceWatcherRequest watcher_request,
     std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
     std::unique_ptr<network::SharedURLLoaderFactoryInfo>
         fallback_factory_info) {
+  DCHECK(network_provider);
   blink::mojom::ServiceWorkerWorkerClientRequest service_worker_client_request;
   blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo
       service_worker_worker_client_registry_ptr_info;
@@ -164,6 +166,7 @@
 
   // Some sandboxed iframes are not allowed to use service worker so don't have
   // a real service worker provider, so the provider context is null.
+  ServiceWorkerProviderContext* provider_context = network_provider->context();
   if (provider_context) {
     provider_context->CloneWorkerClientRegistry(
         mojo::MakeRequest(&service_worker_worker_client_registry_ptr_info));
@@ -176,19 +179,27 @@
       container_host_ptr_info = provider_context->CloneContainerHostPtrInfo();
   }
 
-  return base::AdoptRef(new WebWorkerFetchContextImpl(
-      std::move(renderer_preferences), std::move(watcher_request),
-      std::move(service_worker_client_request),
-      std::move(service_worker_worker_client_registry_ptr_info),
-      std::move(container_host_ptr_info), std::move(loader_factory_info),
-      std::move(fallback_factory_info),
-      GetContentClient()->renderer()->CreateURLLoaderThrottleProvider(
-          URLLoaderThrottleProviderType::kWorker),
-      GetContentClient()
-          ->renderer()
-          ->CreateWebSocketHandshakeThrottleProvider(),
-      ChildThreadImpl::current()->thread_safe_sender(),
-      ChildThreadImpl::current()->GetConnector()->Clone()));
+  scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context =
+      base::AdoptRef(new WebWorkerFetchContextImpl(
+          std::move(renderer_preferences), std::move(watcher_request),
+          std::move(service_worker_client_request),
+          std::move(service_worker_worker_client_registry_ptr_info),
+          std::move(container_host_ptr_info), std::move(loader_factory_info),
+          std::move(fallback_factory_info),
+          GetContentClient()->renderer()->CreateURLLoaderThrottleProvider(
+              URLLoaderThrottleProviderType::kWorker),
+          GetContentClient()
+              ->renderer()
+              ->CreateWebSocketHandshakeThrottleProvider(),
+          ChildThreadImpl::current()->thread_safe_sender(),
+          ChildThreadImpl::current()->GetConnector()->Clone()));
+  worker_fetch_context->set_service_worker_provider_id(
+      network_provider->provider_id());
+  worker_fetch_context->set_is_controlled_by_service_worker(
+      network_provider->IsControlledByServiceWorker());
+  if (provider_context)
+    worker_fetch_context->set_client_id(provider_context->client_id());
+  return worker_fetch_context;
 }
 
 WebWorkerFetchContextImpl::WebWorkerFetchContextImpl(
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h
index 5720ca2..608786a 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.h
+++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -34,7 +34,7 @@
 
 class FrameRequestBlocker;
 class ResourceDispatcher;
-class ServiceWorkerProviderContext;
+class ServiceWorkerNetworkProvider;
 class ThreadSafeSender;
 class URLLoaderThrottleProvider;
 class WebSocketHandshakeThrottleProvider;
@@ -50,11 +50,8 @@
  public:
   // Creates a new fetch context for a worker.
   //
-  // |provider_context| is the ServiceWorkerProviderContext of the worker and is
-  // used to route requests to its controller service worker. It can be null if
-  // this worker is never associated with service workers, for example, when a
-  // dedicated worker is created from a sandboxed iframe that doesn't have a
-  // service worker provider context.
+  // |network_provider| is the ServiceWorkerNetworkProvider of the worker and is
+  // used to route requests to its controller service worker.
   // |loader_factory_info| is used for regular loading by the worker.
   //
   // S13nServiceWorker:
@@ -70,7 +67,7 @@
   // because it might additionally support non-NetworkService schemes (e.g.,
   // chrome-extension://).
   static scoped_refptr<WebWorkerFetchContextImpl> Create(
-      ServiceWorkerProviderContext* provider_context,
+      ServiceWorkerNetworkProvider* network_provider,
       RendererPreferences renderer_preferences,
       mojom::RendererPreferenceWatcherRequest watcher_request,
       std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
diff --git a/content/renderer/media/stream/apply_constraints_processor.h b/content/renderer/media/stream/apply_constraints_processor.h
index a7182c23..943989f 100644
--- a/content/renderer/media/stream/apply_constraints_processor.h
+++ b/content/renderer/media/stream/apply_constraints_processor.h
@@ -13,7 +13,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_constraints_util.h"
 #include "media/capture/video_capture_types.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/web/web_apply_constraints_request.h"
 
 namespace blink {
diff --git a/content/renderer/media/stream/audio_service_audio_processor_proxy.h b/content/renderer/media/stream/audio_service_audio_processor_proxy.h
index 29a9082..374d0b3 100644
--- a/content/renderer/media/stream/audio_service_audio_processor_proxy.h
+++ b/content/renderer/media/stream/audio_service_audio_processor_proxy.h
@@ -16,7 +16,7 @@
 #include "content/renderer/media/stream/aec_dump_message_filter.h"
 #include "media/audio/audio_processing.h"
 #include "media/webrtc/audio_processor_controls.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
 #include "third_party/webrtc/rtc_base/task_queue.h"
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor.cc b/content/renderer/media/stream/media_stream_audio_processor.cc
index 8f99a3a..18aba2a 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor.cc
@@ -36,7 +36,7 @@
 #include "third_party/webrtc/api/audio/echo_canceller3_config.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_config_json.h"
 #include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
-#include "third_party/webrtc/api/mediaconstraintsinterface.h"
+#include "third_party/webrtc/api/media_constraints_interface.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing_statistics.h"
 #include "third_party/webrtc/modules/audio_processing/typing_detection.h"
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor.h b/content/renderer/media/stream/media_stream_audio_processor.h
index dc97f3d..b5f9256 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.h
+++ b/content/renderer/media/stream/media_stream_audio_processor.h
@@ -24,7 +24,7 @@
 #include "media/base/audio_converter.h"
 #include "media/webrtc/audio_delay_stats_reporter.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
 #include "third_party/webrtc/rtc_base/task_queue.h"
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.h b/content/renderer/media/stream/media_stream_audio_processor_options.h
index 45d6769..ff011f3 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.h
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.h
@@ -18,8 +18,8 @@
 #include "media/base/audio_point.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
-#include "third_party/webrtc/media/base/mediachannel.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
+#include "third_party/webrtc/media/base/media_channel.h"
 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
 #include "third_party/webrtc/rtc_base/task_queue.h"
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
index 8e4a291..14b3cb1 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
@@ -30,8 +30,8 @@
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 using ::testing::_;
 using ::testing::AnyNumber;
diff --git a/content/renderer/media/stream/media_stream_constraints_util.h b/content/renderer/media/stream/media_stream_constraints_util.h
index 15434db..975baa37 100644
--- a/content/renderer/media/stream/media_stream_constraints_util.h
+++ b/content/renderer/media/stream/media_stream_constraints_util.h
@@ -14,7 +14,7 @@
 #include "content/renderer/media/stream/video_track_adapter.h"
 #include "media/base/video_facing.h"
 #include "media/capture/video_capture_types.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.h b/content/renderer/media/stream/media_stream_constraints_util_audio.h
index c7d7fe9..b9dbe85b 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.h
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.h
@@ -10,7 +10,7 @@
 
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_constraints_util.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 
 namespace blink {
 class WebMediaConstraints;
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index eb1d5aa7..a301dfaf 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -20,7 +20,6 @@
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "media/base/audio_parameters.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_string.h"
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util_video_device.h b/content/renderer/media/stream/media_stream_constraints_util_video_device.h
index 25b611f..484ffc6 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_video_device.h
+++ b/content/renderer/media/stream/media_stream_constraints_util_video_device.h
@@ -12,7 +12,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_constraints_util.h"
 #include "media/capture/video_capture_types.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 
 namespace blink {
 class WebString;
diff --git a/content/renderer/media/stream/media_stream_renderer_factory_impl.cc b/content/renderer/media/stream/media_stream_renderer_factory_impl.cc
index 65151b9..49db4fc 100644
--- a/content/renderer/media/stream/media_stream_renderer_factory_impl.cc
+++ b/content/renderer/media/stream/media_stream_renderer_factory_impl.cc
@@ -15,7 +15,7 @@
 #include "content/renderer/media/webrtc_logging.h"
 #include "content/renderer/render_thread_impl.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/stream/mock_constraint_factory.cc b/content/renderer/media/stream/mock_constraint_factory.cc
index 7f07e50..adda05d 100644
--- a/content/renderer/media/stream/mock_constraint_factory.cc
+++ b/content/renderer/media/stream/mock_constraint_factory.cc
@@ -9,7 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/renderer/media/stream/media_stream_audio_processor_options.h"
 #include "content/renderer/media/stream/mock_constraint_factory.h"
-#include "third_party/webrtc/api/mediaconstraintsinterface.h"
+#include "third_party/webrtc/api/media_constraints_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/stream/processed_local_audio_source.cc b/content/renderer/media/stream/processed_local_audio_source.cc
index b2b18c7..235f0bd 100644
--- a/content/renderer/media/stream/processed_local_audio_source.cc
+++ b/content/renderer/media/stream/processed_local_audio_source.cc
@@ -22,8 +22,8 @@
 #include "media/base/channel_layout.h"
 #include "media/base/sample_rates.h"
 #include "media/webrtc/webrtc_switches.h"
-#include "third_party/webrtc/api/mediaconstraintsinterface.h"
-#include "third_party/webrtc/media/base/mediachannel.h"
+#include "third_party/webrtc/api/media_constraints_interface.h"
+#include "third_party/webrtc/media/base/media_channel.h"
 
 namespace content {
 
diff --git a/content/renderer/media/stream/remote_media_stream_track_adapter.h b/content/renderer/media/stream/remote_media_stream_track_adapter.h
index a7fda935..6bf047c 100644
--- a/content/renderer/media/stream/remote_media_stream_track_adapter.h
+++ b/content/renderer/media/stream/remote_media_stream_track_adapter.h
@@ -14,7 +14,7 @@
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/stream/user_media_client_impl.h b/content/renderer/media/stream/user_media_client_impl.h
index 778eeea..b325c56 100644
--- a/content/renderer/media/stream/user_media_client_impl.h
+++ b/content/renderer/media/stream/user_media_client_impl.h
@@ -14,10 +14,10 @@
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/renderer/media/stream/user_media_processor.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/web/web_apply_constraints_request.h"
 #include "third_party/blink/public/web/web_user_media_client.h"
 #include "third_party/blink/public/web/web_user_media_request.h"
diff --git a/content/renderer/media/stream/user_media_client_impl_unittest.cc b/content/renderer/media/stream/user_media_client_impl_unittest.cc
index 05db904..ea6f2382 100644
--- a/content/renderer/media/stream/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/stream/user_media_client_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/child/child_process.h"
-#include "content/common/media/media_devices.h"
 #include "content/renderer/media/stream/media_stream_audio_processor_options.h"
 #include "content/renderer/media/stream/media_stream_audio_source.h"
 #include "content/renderer/media/stream/media_stream_audio_track.h"
@@ -30,6 +29,7 @@
 #include "media/audio/audio_device_description.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
diff --git a/content/renderer/media/stream/user_media_processor.h b/content/renderer/media/stream/user_media_processor.h
index 39b19615..4b1cd6c 100644
--- a/content/renderer/media/stream/user_media_processor.h
+++ b/content/renderer/media/stream/user_media_processor.h
@@ -18,8 +18,8 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_dispatcher_eventhandler.h"
 #include "content/renderer/media/stream/media_stream_source.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_user_media_request.h"
 
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index ac1f4604..0bff40c 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -20,7 +20,7 @@
 #include "media/base/video_util.h"
 #include "third_party/webrtc/api/video/i420_buffer.h"
 #include "third_party/webrtc/api/video/video_sink_interface.h"
-#include "third_party/webrtc/rtc_base/timeutils.h"  // for TimeMicros
+#include "third_party/webrtc/rtc_base/time_utils.h"  // for TimeMicros
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.h b/content/renderer/media/webrtc/media_stream_remote_video_source.h
index f07b411..3657eb0 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.h
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.h
@@ -13,7 +13,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_video_source.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics.h b/content/renderer/media/webrtc/media_stream_track_metrics.h
index 9c6f278..0125c07 100644
--- a/content/renderer/media/webrtc/media_stream_track_metrics.h
+++ b/content/renderer/media/webrtc/media_stream_track_metrics.h
@@ -13,7 +13,7 @@
 #include "base/sequence_checker.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
index 4fb4ff3..ad902987 100644
--- a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
@@ -12,7 +12,7 @@
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 using webrtc::AudioSourceInterface;
 using webrtc::AudioTrackInterface;
diff --git a/content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc b/content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc
index e93f565a..3d4a903 100644
--- a/content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc
+++ b/content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc
@@ -22,7 +22,7 @@
 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/webrtc_video_track_source.h"
 #include "media/base/limits.h"
-#include "third_party/webrtc/api/videosourceproxy.h"
+#include "third_party/webrtc/api/video_track_source_proxy.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/media_stream_video_webrtc_sink.h b/content/renderer/media/webrtc/media_stream_video_webrtc_sink.h
index 4c0161f..a07c240 100644
--- a/content/renderer/media/webrtc/media_stream_video_webrtc_sink.h
+++ b/content/renderer/media/webrtc/media_stream_video_webrtc_sink.h
@@ -10,7 +10,7 @@
 #include "base/threading/thread_checker.h"
 #include "content/public/renderer/media_stream_video_sink.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace base {
 class SingleThreadTaskRunner;
diff --git a/content/renderer/media/webrtc/mock_data_channel_impl.h b/content/renderer/media/webrtc/mock_data_channel_impl.h
index ba74a94..13a8ad2 100644
--- a/content/renderer/media/webrtc/mock_data_channel_impl.h
+++ b/content/renderer/media/webrtc/mock_data_channel_impl.h
@@ -10,7 +10,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
index d1489c4..ea7dedc 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
@@ -10,7 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
 using webrtc::AudioSourceInterface;
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
index 1736c90..abb273e1 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
@@ -13,8 +13,8 @@
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
-#include "third_party/webrtc/api/mediaconstraintsinterface.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_constraints_interface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index 1b43545..198a637 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -13,8 +13,8 @@
 #include "content/renderer/media/webrtc/mock_data_channel_impl.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/webrtc_util.h"
-#include "third_party/webrtc/api/rtpreceiverinterface.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/api/rtp_receiver_interface.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 using testing::_;
 using webrtc::AudioTrackInterface;
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index 7ec82a8..42e9a8f 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -13,9 +13,9 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/webrtc/api/dtlstransportinterface.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/stats/rtcstatsreport.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/stats/rtc_stats_report.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index 56cc00ac..86352eb8 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -61,12 +61,12 @@
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/webrtc/api/create_peerconnection_factory.h"
-#include "third_party/webrtc/api/mediaconstraintsinterface.h"
-#include "third_party/webrtc/api/videosourceproxy.h"
-#include "third_party/webrtc/media/engine/multiplexcodecfactory.h"
+#include "third_party/webrtc/api/media_constraints_interface.h"
+#include "third_party/webrtc/api/video_track_source_proxy.h"
+#include "third_party/webrtc/media/engine/multiplex_codec_factory.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
-#include "third_party/webrtc/rtc_base/ssladapter.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
+#include "third_party/webrtc/rtc_base/ssl_adapter.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.h b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
index 720c39087..0d9671b 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.h
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
@@ -17,8 +17,8 @@
 #include "content/renderer/media/webrtc/stun_field_trial.h"
 #include "content/renderer/p2p/socket_dispatcher.h"
 #include "ipc/ipc_platform_file.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/p2p/stunprober/stunprober.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/p2p/stunprober/stun_prober.h"
 
 namespace base {
 class WaitableEvent;
diff --git a/content/renderer/media/webrtc/peer_connection_remote_audio_source.h b/content/renderer/media/webrtc/peer_connection_remote_audio_source.h
index da18498..12e5fa9 100644
--- a/content/renderer/media/webrtc/peer_connection_remote_audio_source.h
+++ b/content/renderer/media/webrtc/peer_connection_remote_audio_source.h
@@ -11,7 +11,7 @@
 #include "base/synchronization/lock.h"
 #include "content/renderer/media/stream/media_stream_audio_source.h"
 #include "content/renderer/media/stream/media_stream_audio_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace media {
 class AudioBus;
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.h b/content/renderer/media/webrtc/peer_connection_tracker.h
index 2fe3082ee..b0eeccf 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.h
+++ b/content/renderer/media/webrtc/peer_connection_tracker.h
@@ -18,7 +18,7 @@
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_session_description.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace blink {
 class WebLocalFrame;
diff --git a/content/renderer/media/webrtc/rtc_certificate_generator.cc b/content/renderer/media/webrtc/rtc_certificate_generator.cc
index 022b7bc..e14f632 100644
--- a/content/renderer/media/webrtc/rtc_certificate_generator.cc
+++ b/content/renderer/media/webrtc/rtc_certificate_generator.cc
@@ -15,8 +15,8 @@
 #include "content/renderer/render_thread_impl.h"
 #include "media/media_buildflags.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
-#include "third_party/webrtc/rtc_base/rtccertificategenerator.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate_generator.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/renderer/media/webrtc/rtc_certificate_generator.h b/content/renderer/media/webrtc/rtc_certificate_generator.h
index f3fa2ce..9d520f7 100644
--- a/content/renderer/media/webrtc/rtc_certificate_generator.h
+++ b/content/renderer/media/webrtc/rtc_certificate_generator.h
@@ -9,7 +9,7 @@
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/web_rtc_certificate_generator.h"
 #include "third_party/blink/public/platform/web_rtc_key_params.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_data_channel_handler.h b/content/renderer/media/webrtc/rtc_data_channel_handler.h
index 32ee717..949bc486 100644
--- a/content/renderer/media/webrtc/rtc_data_channel_handler.h
+++ b/content/renderer/media/webrtc/rtc_data_channel_handler.h
@@ -16,7 +16,7 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/platform/web_rtc_data_channel_handler.h"
 #include "third_party/blink/public/platform/web_rtc_data_channel_handler_client.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
index 8f59a39..52fa7561 100644
--- a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
+++ b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
@@ -14,7 +14,7 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler_client.h"
-#include "third_party/webrtc/api/dtmfsenderinterface.h"
+#include "third_party/webrtc/api/dtmf_sender_interface.h"
 
 namespace base {
 class SingleThreadTaskRunner;
diff --git a/content/renderer/media/webrtc/rtc_event_log_output_sink_proxy.h b/content/renderer/media/webrtc/rtc_event_log_output_sink_proxy.h
index 25e5a3eb1..a3412d60 100644
--- a/content/renderer/media/webrtc/rtc_event_log_output_sink_proxy.h
+++ b/content/renderer/media/webrtc/rtc_event_log_output_sink_proxy.h
@@ -6,7 +6,7 @@
 #define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_EVENT_LOG_OUTPUT_SINK_PROXY_H_
 
 #include "content/renderer/media/webrtc/rtc_event_log_output_sink.h"
-#include "third_party/webrtc/api/rtceventlogoutput.h"
+#include "third_party/webrtc/api/rtc_event_log_output.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index aefa96a2..6e5146c5 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -54,8 +54,8 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_url.h"
-#include "third_party/webrtc/api/rtceventlogoutput.h"
-#include "third_party/webrtc/pc/mediasession.h"
+#include "third_party/webrtc/api/rtc_event_log_output.h"
+#include "third_party/webrtc/pc/media_session.h"
 
 using webrtc::DataChannelInterface;
 using webrtc::IceCandidateInterface;
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index 279b849..29774cf 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -57,9 +57,9 @@
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/web/web_heap.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/rtpreceiverinterface.h"
-#include "third_party/webrtc/stats/test/rtcteststats.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtp_receiver_interface.h"
+#include "third_party/webrtc/stats/test/rtc_test_stats.h"
 
 static const char kDummySdp[] = "dummy sdp";
 static const char kDummySdpType[] = "dummy type";
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver.h b/content/renderer/media/webrtc/rtc_rtp_receiver.h
index c9dca61..f93ea71 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver.h
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver.h
@@ -15,9 +15,9 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/rtpreceiverinterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtp_receiver_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
index 17ebb4ee..b7135961 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
@@ -22,8 +22,8 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/webrtc/api/stats/rtc_stats_report.h"
 #include "third_party/webrtc/api/stats/rtcstats_objects.h"
-#include "third_party/webrtc/api/stats/rtcstatsreport.h"
 #include "third_party/webrtc/api/test/mock_rtpreceiver.h"
 
 namespace content {
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/content/renderer/media/webrtc/rtc_rtp_sender.h
index 0ee9742..4a00277 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.h
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.h
@@ -16,8 +16,8 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/rtpsenderinterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtp_sender_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
 namespace content {
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
index b7fca13..e99e6cb 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
@@ -25,8 +25,8 @@
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/webrtc/api/stats/rtc_stats_report.h"
 #include "third_party/webrtc/api/stats/rtcstats_objects.h"
-#include "third_party/webrtc/api/stats/rtcstatsreport.h"
 #include "third_party/webrtc/api/test/mock_rtpsender.h"
 
 using ::testing::_;
diff --git a/content/renderer/media/webrtc/rtc_rtp_source.h b/content/renderer/media/webrtc/rtc_rtp_source.h
index 33410017..353d4de26 100644
--- a/content/renderer/media/webrtc/rtc_rtp_source.h
+++ b/content/renderer/media/webrtc/rtc_rtp_source.h
@@ -9,7 +9,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_source.h"
-#include "third_party/webrtc/api/rtpreceiverinterface.h"
+#include "third_party/webrtc/api/rtp_receiver_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver.h b/content/renderer/media/webrtc/rtc_rtp_transceiver.h
index 5eadedf..cb83879 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver.h
+++ b/content/renderer/media/webrtc/rtc_rtp_transceiver.h
@@ -12,7 +12,7 @@
 #include "content/renderer/media/webrtc/rtc_rtp_sender.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_stats.h b/content/renderer/media/webrtc/rtc_stats.h
index 8caef8a2..3f6e49e 100644
--- a/content/renderer/media/webrtc/rtc_stats.h
+++ b/content/renderer/media/webrtc/rtc_stats.h
@@ -9,9 +9,9 @@
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
-#include "third_party/webrtc/api/stats/rtcstats.h"
-#include "third_party/webrtc/api/stats/rtcstatscollectorcallback.h"
-#include "third_party/webrtc/api/stats/rtcstatsreport.h"
+#include "third_party/webrtc/api/stats/rtc_stats.h"
+#include "third_party/webrtc/api/stats/rtc_stats_collector_callback.h"
+#include "third_party/webrtc/api/stats/rtc_stats_report.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_stats_unittest.cc b/content/renderer/media/webrtc/rtc_stats_unittest.cc
index f369400..1e1d39c 100644
--- a/content/renderer/media/webrtc/rtc_stats_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_stats_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
+#include "third_party/webrtc/api/stats/rtc_stats_report.h"
 #include "third_party/webrtc/api/stats/rtcstats_objects.h"
-#include "third_party/webrtc/api/stats/rtcstatsreport.h"
-#include "third_party/webrtc/stats/test/rtcteststats.h"
+#include "third_party/webrtc/stats/test/rtc_test_stats.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_video_decoder.cc b/content/renderer/media/webrtc/rtc_video_decoder.cc
index 8c6cf63..7a71afae 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder.cc
@@ -23,8 +23,8 @@
 #include "third_party/webrtc/media/base/vp9_profile.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "third_party/webrtc/rtc_base/bind.h"
-#include "third_party/webrtc/rtc_base/refcount.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_count.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 #if defined(OS_WIN)
 #include "base/command_line.h"
@@ -925,7 +925,7 @@
 void RTCVideoDecoder::ClearPendingBuffers() {
   // Delete WebRTC input buffers.
   for (const auto& pending_buffer : pending_buffers_)
-    delete[] pending_buffer.first._buffer;
+    delete[] pending_buffer.first.data();
   pending_buffers_.clear();
 }
 
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
index 1b1e6d72..1dc4927c 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -31,8 +31,8 @@
 #include "third_party/webrtc/media/base/vp9_profile.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "third_party/webrtc/rtc_base/bind.h"
-#include "third_party/webrtc/rtc_base/refcount.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_count.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 #include "ui/gfx/color_space.h"
 
 #if defined(OS_WIN)
diff --git a/content/renderer/media/webrtc/rtc_video_encoder.cc b/content/renderer/media/webrtc/rtc_video_encoder.cc
index d6ef5a6..ca77a74 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_encoder.cc
@@ -37,7 +37,7 @@
 #include "third_party/libyuv/include/libyuv.h"
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "third_party/webrtc/modules/video_coding/include/video_error_codes.h"
-#include "third_party/webrtc/rtc_base/timeutils.h"
+#include "third_party/webrtc/rtc_base/time_utils.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/rtc_video_encoder_unittest.cc b/content/renderer/media/webrtc/rtc_video_encoder_unittest.cc
index ae4a1ac..2690892 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_video_encoder_unittest.cc
@@ -16,7 +16,7 @@
 #include "third_party/webrtc/api/video/i420_buffer.h"
 #include "third_party/webrtc/api/video_codecs/video_encoder.h"
 #include "third_party/webrtc/modules/video_coding/include/video_codec_interface.h"
-#include "third_party/webrtc/rtc_base/timeutils.h"
+#include "third_party/webrtc/rtc_base/time_utils.h"
 
 using ::testing::_;
 using ::testing::AtLeast;
diff --git a/content/renderer/media/webrtc/stun_field_trial.cc b/content/renderer/media/webrtc/stun_field_trial.cc
index 4f810949..183d8e8 100644
--- a/content/renderer/media/webrtc/stun_field_trial.cc
+++ b/content/renderer/media/webrtc/stun_field_trial.cc
@@ -16,12 +16,12 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "third_party/webrtc/p2p/base/packetsocketfactory.h"
-#include "third_party/webrtc/rtc_base/asyncpacketsocket.h"
-#include "third_party/webrtc/rtc_base/asyncresolverinterface.h"
-#include "third_party/webrtc/rtc_base/ipaddress.h"
+#include "third_party/webrtc/p2p/base/packet_socket_factory.h"
+#include "third_party/webrtc/rtc_base/async_packet_socket.h"
+#include "third_party/webrtc/rtc_base/async_resolver_interface.h"
+#include "third_party/webrtc/rtc_base/ip_address.h"
 #include "third_party/webrtc/rtc_base/network.h"
-#include "third_party/webrtc/rtc_base/socketaddress.h"
+#include "third_party/webrtc/rtc_base/socket_address.h"
 #include "third_party/webrtc/rtc_base/thread.h"
 
 using stunprober::StunProber;
diff --git a/content/renderer/media/webrtc/stun_field_trial.h b/content/renderer/media/webrtc/stun_field_trial.h
index 5843731..81b7419 100644
--- a/content/renderer/media/webrtc/stun_field_trial.h
+++ b/content/renderer/media/webrtc/stun_field_trial.h
@@ -16,7 +16,7 @@
 #include "content/common/content_export.h"
 #include "content/renderer/p2p/network_list_manager.h"
 #include "content/renderer/p2p/network_list_observer.h"
-#include "third_party/webrtc/p2p/stunprober/stunprober.h"
+#include "third_party/webrtc/p2p/stunprober/stun_prober.h"
 #include "third_party/webrtc/rtc_base/network.h"
 #include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
 
diff --git a/content/renderer/media/webrtc/stun_field_trial_unittest.cc b/content/renderer/media/webrtc/stun_field_trial_unittest.cc
index 3495981..67c3962a 100644
--- a/content/renderer/media/webrtc/stun_field_trial_unittest.cc
+++ b/content/renderer/media/webrtc/stun_field_trial_unittest.cc
@@ -5,7 +5,7 @@
 #include "content/renderer/media/webrtc/stun_field_trial.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/webrtc/rtc_base/socketaddress.h"
+#include "third_party/webrtc/rtc_base/socket_address.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/track_observer.h b/content/renderer/media/webrtc/track_observer.h
index 2a542b8d..760fc98 100644
--- a/content/renderer/media/webrtc/track_observer.h
+++ b/content/renderer/media/webrtc/track_observer.h
@@ -10,7 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 #include "content/common/content_export.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.cc b/content/renderer/media/webrtc/transceiver_state_surfacer.cc
index abd9ecc..68b57de 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.cc
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer.cc
@@ -5,7 +5,7 @@
 #include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
 
 #include "content/renderer/media/webrtc/webrtc_util.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer.h b/content/renderer/media/webrtc/transceiver_state_surfacer.h
index b3bc9b75..d49de68 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer.h
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer.h
@@ -7,9 +7,9 @@
 
 #include "content/renderer/media/webrtc/rtc_rtp_transceiver.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
-#include "third_party/webrtc/rtc_base/refcount.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
+#include "third_party/webrtc/rtc_base/ref_count.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/video_codec_factory.cc b/content/renderer/media/webrtc/video_codec_factory.cc
index 9be9ec9..cb0f8bc 100644
--- a/content/renderer/media/webrtc/video_codec_factory.cc
+++ b/content/renderer/media/webrtc/video_codec_factory.cc
@@ -17,8 +17,8 @@
 #include "third_party/webrtc/api/video_codecs/video_encoder_software_fallback_wrapper.h"
 #include "third_party/webrtc/media/base/codec.h"
 #include "third_party/webrtc/media/engine/encoder_simulcast_proxy.h"
-#include "third_party/webrtc/media/engine/internaldecoderfactory.h"
-#include "third_party/webrtc/media/engine/internalencoderfactory.h"
+#include "third_party/webrtc/media/engine/internal_decoder_factory.h"
+#include "third_party/webrtc/media/engine/internal_encoder_factory.h"
 #include "third_party/webrtc/media/engine/simulcast_encoder_adapter.h"
 
 #if defined(OS_ANDROID)
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer.cc b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
index ad5ad908..ad21cd7 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer.cc
@@ -25,7 +25,7 @@
 #include "media/base/audio_parameters.h"
 #include "media/base/sample_rates.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
index 9dbb3b78..8753de7 100644
--- a/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_renderer_unittest.cc
@@ -25,7 +25,7 @@
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 using testing::Return;
 using testing::_;
diff --git a/content/renderer/media/webrtc/webrtc_audio_sink.h b/content/renderer/media/webrtc/webrtc_audio_sink.h
index c2a6c381..c6afaa32 100644
--- a/content/renderer/media/webrtc/webrtc_audio_sink.h
+++ b/content/renderer/media/webrtc/webrtc_audio_sink.h
@@ -22,8 +22,8 @@
 #include "content/renderer/media/stream/media_stream_audio_processor.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_push_fifo.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
-#include "third_party/webrtc/pc/mediastreamtrack.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
+#include "third_party/webrtc/pc/media_stream_track.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h
index fbf4210..cd8a9c8 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h
@@ -16,7 +16,7 @@
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h
index 8c9cbf1..713506b0 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h
@@ -14,7 +14,7 @@
 #include "content/renderer/media/webrtc/two_keys_adapter_map.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer.h b/content/renderer/media/webrtc/webrtc_set_description_observer.h
index 71c98a8dd..1dd218b8 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer.h
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer.h
@@ -20,13 +20,13 @@
 #include "content/renderer/media/webrtc/transceiver_state_surfacer.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/webrtc/api/jsep.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/rtcerror.h"
-#include "third_party/webrtc/api/rtpreceiverinterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtc_error.h"
+#include "third_party/webrtc/api/rtp_receiver_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
-#include "third_party/webrtc/api/setremotedescriptionobserverinterface.h"
-#include "third_party/webrtc/rtc_base/refcount.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/api/set_remote_description_observer_interface.h"
+#include "third_party/webrtc/rtc_base/ref_count.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index 80530a8..2200fd1 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -22,9 +22,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/web_heap.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 #include "third_party/webrtc/api/test/mock_peerconnectioninterface.h"
-#include "third_party/webrtc/media/base/fakemediaengine.h"
+#include "third_party/webrtc/media/base/fake_media_engine.h"
 
 using ::testing::Return;
 
diff --git a/content/renderer/media/webrtc/webrtc_video_frame_adapter.cc b/content/renderer/media/webrtc/webrtc_video_frame_adapter.cc
index bfa7ba1..b8ab52f 100644
--- a/content/renderer/media/webrtc/webrtc_video_frame_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_video_frame_adapter.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "third_party/webrtc/common_video/include/video_frame_buffer.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 namespace {
 
diff --git a/content/renderer/media/webrtc/webrtc_video_track_source.cc b/content/renderer/media/webrtc/webrtc_video_track_source.cc
index 46ea7b5..271768f3 100644
--- a/content/renderer/media/webrtc/webrtc_video_track_source.cc
+++ b/content/renderer/media/webrtc/webrtc_video_track_source.cc
@@ -7,7 +7,7 @@
 #include "base/trace_event/trace_event.h"
 #include "content/renderer/media/webrtc/webrtc_video_frame_adapter.h"
 #include "third_party/libyuv/include/libyuv/scale.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_video_track_source.h b/content/renderer/media/webrtc/webrtc_video_track_source.h
index c414172..b8746e2 100644
--- a/content/renderer/media/webrtc/webrtc_video_track_source.h
+++ b/content/renderer/media/webrtc/webrtc_video_track_source.h
@@ -9,8 +9,8 @@
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
 #include "media/base/video_frame_pool.h"
-#include "third_party/webrtc/media/base/adaptedvideotracksource.h"
-#include "third_party/webrtc/rtc_base/timestampaligner.h"
+#include "third_party/webrtc/media/base/adapted_video_track_source.h"
+#include "third_party/webrtc/rtc_base/timestamp_aligner.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc/webrtc_video_track_source_unittest.cc b/content/renderer/media/webrtc/webrtc_video_track_source_unittest.cc
index e62c137..c046923 100644
--- a/content/renderer/media/webrtc/webrtc_video_track_source_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_video_track_source_unittest.cc
@@ -10,7 +10,7 @@
 #include "content/renderer/media/webrtc/webrtc_video_track_source.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 namespace content {
 
diff --git a/content/renderer/media/webrtc_local_audio_source_provider.cc b/content/renderer/media/webrtc_local_audio_source_provider.cc
index 1a3c58f..99d7b0d2 100644
--- a/content/renderer/media/webrtc_local_audio_source_provider.cc
+++ b/content/renderer/media/webrtc_local_audio_source_provider.cc
@@ -64,14 +64,11 @@
 
 void WebRtcLocalAudioSourceProvider::OnSetFormat(
     const media::AudioParameters& params) {
-  // We need detach the thread here because it will be a new capture thread
-  // calling OnSetFormat() and OnData() if the source is restarted.
-  capture_thread_checker_.DetachFromThread();
-  DCHECK(capture_thread_checker_.CalledOnValidThread());
   DCHECK(params.IsValid());
-  DCHECK(sink_params_.IsValid());
 
   base::AutoLock auto_lock(lock_);
+  DCHECK(sink_params_.IsValid());
+
   source_params_ = params;
   // Create the audio converter with |disable_fifo| as false so that the
   // converter will request source_params.frames_per_buffer() each time.
@@ -87,6 +84,7 @@
 
 void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
       blink::WebMediaStreamSource::ReadyState state) {
+  NON_REENTRANT_SCOPE(ready_state_reentrancy_checker_);
   if (state == blink::WebMediaStreamSource::kReadyStateEnded)
     track_stopped_ = true;
 }
@@ -94,9 +92,7 @@
 void WebRtcLocalAudioSourceProvider::OnData(
     const media::AudioBus& audio_bus,
     base::TimeTicks estimated_capture_time) {
-  DCHECK(capture_thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(audio_bus.channels(), source_params_.channels());
-  DCHECK_EQ(audio_bus.frames(), source_params_.frames_per_buffer());
+  NON_REENTRANT_SCOPE(capture_reentrancy_checker_);
   DCHECK(!estimated_capture_time.is_null());
 
   base::AutoLock auto_lock(lock_);
@@ -104,6 +100,8 @@
     return;
 
   DCHECK(fifo_.get());
+  DCHECK_EQ(audio_bus.channels(), source_params_.channels());
+  DCHECK_EQ(audio_bus.frames(), source_params_.frames_per_buffer());
 
   if (fifo_->frames() + audio_bus.frames() <= fifo_->max_frames()) {
     fifo_->Push(&audio_bus);
@@ -122,7 +120,9 @@
 void WebRtcLocalAudioSourceProvider::ProvideInput(
     const WebVector<float*>& audio_data,
     size_t number_of_frames) {
+  NON_REENTRANT_SCOPE(provide_input_reentrancy_checker_);
   DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize);
+
   if (!output_wrapper_ ||
       static_cast<size_t>(output_wrapper_->channels()) != audio_data.size()) {
     output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
@@ -140,8 +140,14 @@
   audio_converter_->Convert(output_wrapper_.get());
 }
 
+// |lock_| needs to be acquired before this function is called. It's called by
+// AudioConverter which in turn is called by the above ProvideInput() function.
+// Thus thread safety analysis is disabled here and |lock_| acquire manually
+// asserted.
 double WebRtcLocalAudioSourceProvider::ProvideInput(media::AudioBus* audio_bus,
-                                                    uint32_t frames_delayed) {
+                                                    uint32_t frames_delayed)
+    NO_THREAD_SAFETY_ANALYSIS {
+  lock_.AssertAcquired();
   if (fifo_->frames() >= audio_bus->frames()) {
     fifo_->Consume(audio_bus, 0, audio_bus->frames());
   } else {
@@ -156,6 +162,7 @@
 
 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
     const media::AudioParameters& sink_params) {
+  base::AutoLock auto_lock(lock_);
   sink_params_ = sink_params;
 }
 
diff --git a/content/renderer/media/webrtc_local_audio_source_provider.h b/content/renderer/media/webrtc_local_audio_source_provider.h
index 44392806..89e92b01 100644
--- a/content/renderer/media/webrtc_local_audio_source_provider.h
+++ b/content/renderer/media/webrtc_local_audio_source_provider.h
@@ -12,11 +12,12 @@
 
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
+#include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "content/public/renderer/media_stream_audio_sink.h"
 #include "media/base/audio_converter.h"
+#include "media/base/reentrancy_checker.h"
 #include "third_party/blink/public/platform/web_audio_source_provider.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_vector.h"
@@ -47,7 +48,7 @@
 // MediaStreamAudioSourceNode will periodically call provideInput() to get the
 // data from the FIFO.
 //
-// All calls are protected by a lock.
+// Most calls are protected by a lock.
 class CONTENT_EXPORT WebRtcLocalAudioSourceProvider
     : public blink::WebAudioSourceProvider,
       public media::AudioConverter::InputCallback,
@@ -71,12 +72,6 @@
   void ProvideInput(const blink::WebVector<float*>& audio_data,
                     size_t number_of_frames) override;
 
-  // media::AudioConverter::Inputcallback implementation.
-  // This function is triggered by provideInput()on the WebAudio audio thread,
-  // so it has been under the protection of |lock_|.
-  double ProvideInput(media::AudioBus* audio_bus,
-                      uint32_t frames_delayed) override;
-
   // Method to allow the unittests to inject its own sink parameters to avoid
   // query the hardware.
   // TODO(xians,tommi): Remove and instead offer a way to inject the sink
@@ -87,28 +82,45 @@
   void SetSinkParamsForTesting(const media::AudioParameters& sink_params);
 
  private:
-  // Used to DCHECK that some methods are called on the capture audio thread.
-  base::ThreadChecker capture_thread_checker_;
+  // media::AudioConverter::InputCallback implementation.
+  // This function is triggered by the above ProvideInput() on the WebAudio
+  // audio thread, so it has be called under the protection of |lock_|.
+  double ProvideInput(media::AudioBus* audio_bus,
+                      uint32_t frames_delayed) override;
 
-  std::unique_ptr<media::AudioConverter> audio_converter_;
-  std::unique_ptr<media::AudioFifo> fifo_;
-  std::unique_ptr<media::AudioBus> output_wrapper_;
-  bool is_enabled_;
-  media::AudioParameters source_params_;
-  media::AudioParameters sink_params_;
+  std::unique_ptr<media::AudioConverter> audio_converter_ GUARDED_BY(lock_);
+  std::unique_ptr<media::AudioFifo> fifo_ GUARDED_BY(lock_);
+  bool is_enabled_ GUARDED_BY(lock_);
+  media::AudioParameters source_params_ GUARDED_BY(lock_);
+  media::AudioParameters sink_params_ GUARDED_BY(lock_);
 
-  // Protects all the member variables above.
+  // Protects the above variables.
   base::Lock lock_;
 
-  // Used to report the correct delay to |webaudio_source_|.
-  base::TimeTicks last_fill_;
+  // No lock protection needed since only accessed in WebVector version of
+  // ProvideInput().
+  std::unique_ptr<media::AudioBus> output_wrapper_;
 
   // The audio track that this source provider is connected to.
+  // No lock protection needed since only accessed in constructor and
+  // destructor.
   blink::WebMediaStreamTrack track_;
 
   // Flag to tell if the track has been stopped or not.
+  // No lock protection needed since only accessed in constructor, destructor
+  // and OnReadyStateChanged().
   bool track_stopped_;
 
+  // Used to assert that OnData() is only accessed by one thread at a time. We
+  // can't use a thread checker since thread may change.
+  REENTRANCY_CHECKER(capture_reentrancy_checker_);
+
+  // Used to assert that ProvideInput() is not accessed concurrently.
+  REENTRANCY_CHECKER(provide_input_reentrancy_checker_);
+
+  // Used to assert that OnReadyStateChanged() is not accessed concurrently.
+  REENTRANCY_CHECKER(ready_state_reentrancy_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(WebRtcLocalAudioSourceProvider);
 };
 
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index ec6bf155..7b939ec 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -335,24 +335,27 @@
 
 void RendererWindowTreeClient::OnDragEnter(ws::Id window_id,
                                            uint32_t event_flags,
-                                           const gfx::Point& position,
+                                           const gfx::PointF& location_in_root,
+                                           const gfx::PointF& location,
                                            uint32_t effect_bitmask,
                                            OnDragEnterCallback callback) {}
 
 void RendererWindowTreeClient::OnDragOver(ws::Id window_id,
                                           uint32_t event_flags,
-                                          const gfx::Point& position,
+                                          const gfx::PointF& location_in_root,
+                                          const gfx::PointF& location,
                                           uint32_t effect_bitmask,
                                           OnDragOverCallback callback) {}
 
 void RendererWindowTreeClient::OnDragLeave(ws::Id window_id) {}
 
-void RendererWindowTreeClient::OnCompleteDrop(ws::Id window_id,
-                                              uint32_t event_flags,
-                                              const gfx::Point& position,
-                                              uint32_t effect_bitmask,
-                                              OnCompleteDropCallback callback) {
-}
+void RendererWindowTreeClient::OnCompleteDrop(
+    ws::Id window_id,
+    uint32_t event_flags,
+    const gfx::PointF& location_in_root,
+    const gfx::PointF& location,
+    uint32_t effect_bitmask,
+    OnCompleteDropCallback callback) {}
 
 void RendererWindowTreeClient::OnPerformDragDropCompleted(
     uint32_t change_id,
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h
index 4f14a0c..302dd183 100644
--- a/content/renderer/mus/renderer_window_tree_client.h
+++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -178,18 +178,21 @@
                            mime_data) override;
   void OnDragEnter(ws::Id window_id,
                    uint32_t event_flags,
-                   const gfx::Point& position,
+                   const gfx::PointF& location_in_root,
+                   const gfx::PointF& location,
                    uint32_t effect_bitmask,
                    OnDragEnterCallback callback) override;
   void OnDragOver(ws::Id window_id,
                   uint32_t event_flags,
-                  const gfx::Point& position,
+                  const gfx::PointF& location_in_root,
+                  const gfx::PointF& location,
                   uint32_t effect_bitmask,
                   OnDragOverCallback callback) override;
   void OnDragLeave(ws::Id window_id) override;
   void OnCompleteDrop(ws::Id window_id,
                       uint32_t event_flags,
-                      const gfx::Point& position,
+                      const gfx::PointF& location_in_root,
+                      const gfx::PointF& location,
                       uint32_t effect_bitmask,
                       OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc
index d8d34c9..fb20b9c 100644
--- a/content/renderer/pepper/pepper_media_device_manager.cc
+++ b/content/renderer/pepper/pepper_media_device_manager.cc
@@ -29,13 +29,13 @@
     "secure origin, such as HTTPS. See https://goo.gl/rStTGz for more "
     "details.";
 
-PP_DeviceType_Dev FromMediaDeviceType(MediaDeviceType type) {
+PP_DeviceType_Dev FromMediaDeviceType(blink::MediaDeviceType type) {
   switch (type) {
-    case MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
       return PP_DEVICETYPE_DEV_AUDIOCAPTURE;
-    case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+    case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       return PP_DEVICETYPE_DEV_VIDEOCAPTURE;
-    case MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
+    case blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
       return PP_DEVICETYPE_DEV_AUDIOOUTPUT;
     default:
       NOTREACHED();
@@ -43,22 +43,23 @@
   }
 }
 
-MediaDeviceType ToMediaDeviceType(PP_DeviceType_Dev type) {
+blink::MediaDeviceType ToMediaDeviceType(PP_DeviceType_Dev type) {
   switch (type) {
     case PP_DEVICETYPE_DEV_AUDIOCAPTURE:
-      return MEDIA_DEVICE_TYPE_AUDIO_INPUT;
+      return blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
     case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
-      return MEDIA_DEVICE_TYPE_VIDEO_INPUT;
+      return blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
     case PP_DEVICETYPE_DEV_AUDIOOUTPUT:
-      return MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
+      return blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
     default:
       NOTREACHED();
-      return MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
+      return blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
   }
 }
 
-ppapi::DeviceRefData FromMediaDeviceInfo(MediaDeviceType type,
-                                         const MediaDeviceInfo& info) {
+ppapi::DeviceRefData FromMediaDeviceInfo(
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfo& info) {
   ppapi::DeviceRefData data;
   data.id = info.device_id;
   // Some Flash content can't handle an empty string, so stick a space in to
@@ -69,8 +70,8 @@
 }
 
 std::vector<ppapi::DeviceRefData> FromMediaDeviceInfoArray(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& device_infos) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& device_infos) {
   std::vector<ppapi::DeviceRefData> devices;
   devices.reserve(device_infos.size());
   for (const auto& device_info : device_infos)
@@ -219,8 +220,8 @@
 }
 
 void PepperMediaDeviceManager::OnDevicesChanged(
-    MediaDeviceType type,
-    const MediaDeviceInfoArray& device_infos) {
+    blink::MediaDeviceType type,
+    const blink::WebMediaDeviceInfoArray& device_infos) {
   std::vector<ppapi::DeviceRefData> devices =
       FromMediaDeviceInfoArray(type, device_infos);
   SubscriptionList& subscriptions = device_change_subscriptions_[type];
@@ -250,8 +251,8 @@
 
 void PepperMediaDeviceManager::DevicesEnumerated(
     const DevicesCallback& client_callback,
-    MediaDeviceType type,
-    const std::vector<MediaDeviceInfoArray>& enumeration,
+    blink::MediaDeviceType type,
+    const std::vector<blink::WebMediaDeviceInfoArray>& enumeration,
     std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr>
         video_input_capabilities) {
   client_callback.Run(FromMediaDeviceInfoArray(type, enumeration[type]));
diff --git a/content/renderer/pepper/pepper_media_device_manager.h b/content/renderer/pepper/pepper_media_device_manager.h
index dce0cefb..0d8f2f3 100644
--- a/content/renderer/pepper/pepper_media_device_manager.h
+++ b/content/renderer/pepper/pepper_media_device_manager.h
@@ -12,14 +12,14 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "content/common/media/media_devices.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
 #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "ppapi/c/pp_instance.h"
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
 
 namespace content {
 class MediaStreamDeviceObserver;
@@ -44,8 +44,9 @@
                              size_t subscription_id) override;
 
   // blink::mojom::MediaDevicesListener implementation.
-  void OnDevicesChanged(MediaDeviceType type,
-                        const MediaDeviceInfoArray& device_infos) override;
+  void OnDevicesChanged(
+      blink::MediaDeviceType type,
+      const blink::WebMediaDeviceInfoArray& device_infos) override;
 
   typedef base::Callback<void(int /* request_id */,
                               bool /* succeeded */,
@@ -88,8 +89,8 @@
 
   void DevicesEnumerated(
       const DevicesCallback& callback,
-      MediaDeviceType type,
-      const std::vector<MediaDeviceInfoArray>& enumeration,
+      blink::MediaDeviceType type,
+      const std::vector<blink::WebMediaDeviceInfoArray>& enumeration,
       std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr>
           video_input_capabilities);
 
@@ -105,7 +106,7 @@
 
   using Subscription = std::pair<size_t, DevicesCallback>;
   using SubscriptionList = std::vector<Subscription>;
-  SubscriptionList device_change_subscriptions_[NUM_MEDIA_DEVICE_TYPES];
+  SubscriptionList device_change_subscriptions_[blink::NUM_MEDIA_DEVICE_TYPES];
 
   blink::mojom::MediaStreamDispatcherHostPtr dispatcher_host_;
   blink::mojom::MediaDevicesDispatcherHostPtr media_devices_dispatcher_;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index c1e6a1a4..34b59df 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -142,7 +142,6 @@
 #include "content/renderer/resource_timing_info_conversions.h"
 #include "content/renderer/savable_resources.h"
 #include "content/renderer/service_worker/service_worker_network_provider.h"
-#include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "content/renderer/service_worker/web_service_worker_provider_impl.h"
 #include "content/renderer/skia_benchmarking_extension.h"
 #include "content/renderer/stats_collection_controller.h"
@@ -3762,7 +3761,6 @@
   ServiceWorkerNetworkProvider* provider =
       ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
           web_provider);
-  ServiceWorkerProviderContext* provider_context = provider->context();
 
   mojom::RendererPreferenceWatcherPtr watcher;
   mojom::RendererPreferenceWatcherRequest watcher_request =
@@ -3771,7 +3769,7 @@
 
   scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context =
       WebWorkerFetchContextImpl::Create(
-          provider_context, render_view_->renderer_preferences(),
+          provider, render_view_->renderer_preferences(),
           std::move(watcher_request), GetLoaderFactoryBundle()->Clone(),
           GetLoaderFactoryBundle()->CloneWithoutAppCacheFactory());
 
@@ -3781,17 +3779,12 @@
       frame_->GetDocument().SiteForCookies());
   worker_fetch_context->set_is_secure_context(
       frame_->GetDocument().IsSecureContext());
-  worker_fetch_context->set_service_worker_provider_id(provider->provider_id());
-  worker_fetch_context->set_is_controlled_by_service_worker(
-      provider->IsControlledByServiceWorker());
   worker_fetch_context->set_origin_url(
       GURL(frame_->GetDocument().Url()).GetOrigin());
-  if (provider_context)
-    worker_fetch_context->set_client_id(provider_context->client_id());
 
   for (auto& observer : observers_)
     observer.WillCreateWorkerFetchContext(worker_fetch_context.get());
-  return std::move(worker_fetch_context);
+  return worker_fetch_context;
 }
 
 WebExternalPopupMenu* RenderFrameImpl::CreateExternalPopupMenu(
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 4c968de4..fa6dc09 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -432,9 +432,12 @@
   // Closes a view created during the test, i.e. not the |view()|. Checks that
   // the main frame is detached and deleted, and makes sure the view does not
   // leak.
-  void CloseRenderView(RenderViewImpl* new_view) {
-    new_view->GetWidget()->Close();
-    EXPECT_FALSE(new_view->GetMainRenderFrame());
+  void CloseRenderWidget(RenderWidget* widget) {
+    WidgetMsg_Close msg(widget->routing_id());
+    widget->OnMessageReceived(msg);
+
+    // WidgetMsg_Close posts a task to do the actual closing. Let that run.
+    base::RunLoop().RunUntilIdle();
   }
 
  private:
@@ -546,23 +549,6 @@
   }
 };
 
-// Ensure that the main RenderFrame is deleted and cleared from the RenderView
-// after closing it.
-TEST_F(RenderViewImplTest, RenderFrameClearedAfterClose) {
-  // Create a new main frame RenderFrame so that we don't interfere with the
-  // shutdown of frame() in RenderViewTest.TearDown.
-  blink::WebURLRequest popup_request(GURL("http://foo.com"));
-  blink::WebView* new_web_view = view()->CreateView(
-      GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
-      blink::kWebNavigationPolicyNewForegroundTab, false,
-      blink::WebSandboxFlags::kNone,
-      blink::AllocateSessionStorageNamespaceId());
-  RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
-
-  // Checks that the frame is deleted properly and cleans up the view.
-  CloseRenderView(new_view);
-}
-
 // Test that we get form state change notifications when input fields change.
 TEST_F(RenderViewImplTest, OnNavStateChanged) {
   view()->set_send_content_state_immediately(true);
@@ -811,7 +797,8 @@
   EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
       FrameHostMsg_OpenURL::ID));
 
-  CloseRenderView(new_view);
+  RenderWidget* render_widget = new_view->GetWidget();
+  CloseRenderWidget(render_widget);
 }
 
 class AlwaysForkingRenderViewTest : public RenderViewImplTest {
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index ceb8568..ad23a90 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -380,8 +380,10 @@
   }
 }
 
-void ApplyCommandLineToSettings(const base::CommandLine& command_line,
-                                WebSettings* settings) {
+void ApplyCommandLineToSettings(WebSettings* settings) {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
   settings->SetThreadedScrollingEnabled(
       !command_line.HasSwitch(switches::kDisableThreadedScrolling));
 
@@ -503,7 +505,7 @@
 
   // Pass WidgetClient(), not |this|, as the WebWidgetClient. The method may
   // be overridden in web tests to inject a test-only WebWidgetClient.
-  webview_ = WebView::Create(this, WidgetClient(), params->hidden,
+  webview_ = WebView::Create(this, params->hidden,
                              /*compositing_enabled=*/true,
                              opener_frame ? opener_frame->View() : nullptr);
   GetWidget()->Init(std::move(show_callback), webview_->MainFrameWidget());
@@ -511,15 +513,10 @@
   g_view_map.Get().insert(std::make_pair(webview(), this));
   g_routing_id_view_map.Get().insert(std::make_pair(GetRoutingID(), this));
 
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-
   webview()->SetDisplayMode(params->visual_properties.display_mode);
-  webview()->SetShowFPSCounter(
-      command_line.HasSwitch(cc::switches::kShowFPSCounter));
 
   ApplyWebPreferences(webkit_preferences_, webview());
-  ApplyCommandLineToSettings(command_line, webview()->GetSettings());
+  ApplyCommandLineToSettings(webview()->GetSettings());
 
   // We have either a main frame or a proxy routing id.
   DCHECK_NE(params->main_frame_routing_id != MSG_ROUTING_NONE,
@@ -548,6 +545,9 @@
                                        opener_frame, MSG_ROUTING_NONE,
                                        params->replicated_frame_state,
                                        params->devtools_main_frame_token);
+    // TODO(danakj): Make WebViewImpl not need a WebWidgetClient when there is a
+    // remote main frame (when the RenderWidget is frozen).
+    webview_->SetWebWidgetClient(WidgetClient());
   }
 
   // TODO(davidben): Move this state from Blink into content.
@@ -1521,12 +1521,25 @@
   // The previous WebFrameWidget must already be detached by CloseForFrame().
   DCHECK(!frame_widget_);
   frame_widget_ = frame_widget;
+
+  // Initialization for the WebFrameWidget that should only occur for the main
+  // frame, and that uses types not allowed in blink. This should maybe be
+  // passed to the creation of the WebFrameWidget or the main RenderFrame.
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  WidgetClient()->SetShowFPSCounter(
+      command_line.HasSwitch(cc::switches::kShowFPSCounter));
 }
 
 void RenderViewImpl::DetachWebFrameWidget() {
+  DCHECK(GetWidget()->is_frozen() || GetWidget()->is_closing());
   DCHECK(frame_widget_);
   frame_widget_->Close();
   frame_widget_ = nullptr;
+
+  // TODO(danakj): Make WebViewImpl not need a WebWidgetClient when there is a
+  // remote main frame (when the RenderWidget is frozen).
+  webview_->SetWebWidgetClient(WidgetClient());
 }
 
 void RenderViewImpl::SetZoomLevel(double zoom_level) {
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index a03ed6d..8f0faa6a 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1047,6 +1047,46 @@
   layer_tree_view_->SetNeedsBeginFrame();
 }
 
+void RenderWidget::SetShowFPSCounter(bool show) {
+  cc::LayerTreeHost* host = layer_tree_view_->layer_tree_host();
+  cc::LayerTreeDebugState debug_state = host->GetDebugState();
+  debug_state.show_fps_counter = show;
+  host->SetDebugState(debug_state);
+}
+
+void RenderWidget::SetShowPaintRects(bool show) {
+  cc::LayerTreeHost* host = layer_tree_view_->layer_tree_host();
+  cc::LayerTreeDebugState debug_state = host->GetDebugState();
+  debug_state.show_paint_rects = show;
+  host->SetDebugState(debug_state);
+}
+
+void RenderWidget::SetShowDebugBorders(bool show) {
+  cc::LayerTreeHost* host = layer_tree_view_->layer_tree_host();
+  cc::LayerTreeDebugState debug_state = host->GetDebugState();
+  if (show)
+    debug_state.show_debug_borders.set();
+  else
+    debug_state.show_debug_borders.reset();
+  host->SetDebugState(debug_state);
+}
+
+void RenderWidget::SetShowScrollBottleneckRects(bool show) {
+  cc::LayerTreeHost* host = layer_tree_view_->layer_tree_host();
+  cc::LayerTreeDebugState debug_state = host->GetDebugState();
+  debug_state.show_touch_event_handler_rects = show;
+  debug_state.show_wheel_event_handler_rects = show;
+  debug_state.show_non_fast_scrollable_rects = show;
+  host->SetDebugState(debug_state);
+}
+
+void RenderWidget::SetShowHitTestBorders(bool show) {
+  cc::LayerTreeHost* host = layer_tree_view_->layer_tree_host();
+  cc::LayerTreeDebugState debug_state = host->GetDebugState();
+  debug_state.show_hit_test_borders = show;
+  host->SetDebugState(debug_state);
+}
+
 void RenderWidget::UpdateVisualState(bool record_main_frame_metrics) {
   if (!GetWebWidget())
     return;
@@ -3031,6 +3071,11 @@
     input_event_queue_->SetNeedsLowLatency(needs_low_latency);
 }
 
+void RenderWidget::SetNeedsUnbufferedInputForDebugger(bool unbuffered) {
+  if (input_event_queue_)
+    input_event_queue_->SetNeedsUnbufferedInputForDebugger(unbuffered);
+}
+
 void RenderWidget::AnimateDoubleTapZoomInMainFrame(
     const blink::WebPoint& point,
     const blink::WebRect& rect_to_zoom) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index cece012..35064ea 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -348,6 +348,11 @@
 
   // blink::WebWidgetClient
   void ScheduleAnimation() override;
+  void SetShowFPSCounter(bool show) override;
+  void SetShowPaintRects(bool) override;
+  void SetShowDebugBorders(bool) override;
+  void SetShowScrollBottleneckRects(bool) override;
+  void SetShowHitTestBorders(bool) override;
   void IntrinsicSizingInfoChanged(
       const blink::WebIntrinsicSizingInfo&) override;
   void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
@@ -385,6 +390,7 @@
   void HasPointerRawMoveEventHandlers(bool has_handlers) override;
   void HasTouchEventHandlers(bool has_handlers) override;
   void SetNeedsLowLatencyInput(bool) override;
+  void SetNeedsUnbufferedInputForDebugger(bool) override;
   void AnimateDoubleTapZoomInMainFrame(const blink::WebPoint& point,
                                        const blink::WebRect& bounds) override;
   void ZoomToFindInPageRectInMainFrame(
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
index 0e4a6072..336188c 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -14,7 +14,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/public/common/privacy_preferences.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 
 namespace blink {
 
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index fb7ad0e..12f030d0 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -562,7 +562,19 @@
                                      : "ResourceLoader"));
 }
 
-ServiceWorkerContextClient::~ServiceWorkerContextClient() {}
+ServiceWorkerContextClient::~ServiceWorkerContextClient() {
+  // TODO(crbug.com/907311): Remove this instrumentation after we identified
+  // the cause of crash.
+  if (report_debug_log_ && context_) {
+    std::string log;
+    for (const auto& entry : debug_log_) {
+      log += entry + " ";
+    }
+    DEBUG_ALIAS_FOR_CSTR(debug_log, log.c_str(), 1024);
+    CHECK(false) << "Destructing ServiceWorkerContextClient without calling "
+                    "WillDestroyWorkerContext()";
+  }
+}
 
 void ServiceWorkerContextClient::WorkerReadyForInspection() {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
@@ -572,6 +584,7 @@
 void ServiceWorkerContextClient::WorkerContextFailedToStart() {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!proxy_);
+  RecordDebugLog("WorkerContextFailedToStart");
 
   (*instance_host_)->OnStopped();
 
@@ -621,6 +634,7 @@
     blink::WebServiceWorkerContextProxy* proxy) {
   DCHECK(!worker_task_runner_.get());
   DCHECK_NE(0, WorkerThread::GetCurrentId());
+  RecordDebugLog("WorkerContextStarted");
   worker_task_runner_ = base::ThreadTaskRunnerHandle::Get();
   // g_worker_client_tls.Pointer()->Get() could return nullptr if this context
   // gets deleted before workerContextStarted() is called.
@@ -701,6 +715,7 @@
 
 void ServiceWorkerContextClient::DidInitializeWorkerContext(
     v8::Local<v8::Context> context) {
+  RecordDebugLog("DidInitializeWorkerContext");
   GetContentClient()
       ->renderer()
       ->DidInitializeServiceWorkerContextOnWorkerThread(
@@ -710,6 +725,7 @@
 
 void ServiceWorkerContextClient::WillDestroyWorkerContext(
     v8::Local<v8::Context> context) {
+  RecordDebugLog("WillDestroyWorkerContext");
   // At this point WillStopCurrentWorkerThread is already called, so
   // worker_task_runner_->RunsTasksInCurrentSequence() returns false
   // (while we're still on the worker thread).
@@ -731,6 +747,7 @@
 
 void ServiceWorkerContextClient::WorkerContextDestroyed() {
   DCHECK(g_worker_client_tls.Pointer()->Get() == nullptr);
+  RecordDebugLog("WorkerContextDestroyed");
 
   // TODO(shimazu): The signals to the browser should be in the order:
   // (1) WorkerStopped (via mojo call EmbeddedWorkerInstanceHost.OnStopped())
@@ -1317,18 +1334,17 @@
 
   // The body is provided in |request->body|.
   DCHECK(!request->blob);
-  if (request->body.has_value()) {
-    DCHECK(request->body.value());
+  if (request->body) {
     std::vector<blink::mojom::BlobPtrInfo> blob_ptrs;
     if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
       // We need this as GetBlobFromUUID is a sync IPC.
       // TODO(kinuko): Remove the friend for ScopedAllowBaseSyncPrimitives
       // in //base as well when we remove this code.
       base::ScopedAllowBaseSyncPrimitives allow_sync_primitives;
-      blob_ptrs = GetBlobPtrsForRequestBody(*(request->body.value()));
+      blob_ptrs = GetBlobPtrsForRequestBody(*request->body);
     }
     blink::WebHTTPBody body = GetWebHTTPBodyForRequestBodyWithBlobPtrs(
-        *(request->body.value()), std::move(blob_ptrs));
+        *request->body, std::move(blob_ptrs));
     body.SetUniqueBoundary();
     web_request->SetBody(body);
   }
@@ -1780,6 +1796,7 @@
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(context_);
   DCHECK(context_->timeout_timer);
+  RecordDebugLog("OnRequestedTermination");
 
   // This worker will be terminated soon. Ignore the message.
   if (will_be_terminated)
@@ -1796,6 +1813,7 @@
 }
 
 void ServiceWorkerContextClient::StopWorker() {
+  RecordDebugLog("StopWorker");
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   if (embedded_worker_client_)
     embedded_worker_client_->StopWorker();
@@ -1827,4 +1845,9 @@
   context_->timeout_timer = std::move(timeout_timer);
 }
 
+void ServiceWorkerContextClient::RecordDebugLog(const char* message) {
+  base::AutoLock lock(debug_log_lock_);
+  debug_log_.emplace_back(message);
+}
+
 }  // namespace content
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 1477d28..e6816c9 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
+#include "base/synchronization/lock.h"
 #include "base/time/time.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
@@ -209,6 +210,11 @@
       blink::mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
       DispatchFetchEventCallback callback);
 
+  // TODO(crbug.com/907311): Remove after we identified the cause of crash.
+  void SetReportDebugLogForTesting(bool report_debug_log) {
+    report_debug_log_ = report_debug_log;
+  }
+
  private:
   struct WorkerContextData;
   class NavigationPreloadRequest;
@@ -361,6 +367,9 @@
   void SetTimeoutTimerForTesting(
       std::unique_ptr<ServiceWorkerTimeoutTimer> timeout_timer);
 
+  // TODO(crbug.com/907311): Remove after we identified the cause of crash.
+  void RecordDebugLog(const char* message);
+
   const int embedded_worker_id_;
   const int64_t service_worker_version_id_;
   const GURL service_worker_scope_;
@@ -417,6 +426,11 @@
   network::mojom::URLLoaderFactoryPtr
       network_service_connection_error_handler_holder_;
 
+  // TODO(crbug.com/907311): Remove after we identified the cause of crash.
+  bool report_debug_log_ = true;
+  base::Lock debug_log_lock_;
+  std::vector<std::string> debug_log_ GUARDED_BY(debug_log_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextClient);
 };
 
diff --git a/content/renderer/service_worker/service_worker_context_client_unittest.cc b/content/renderer/service_worker/service_worker_context_client_unittest.cc
index fff5638..15119097 100644
--- a/content/renderer/service_worker/service_worker_context_client_unittest.cc
+++ b/content/renderer/service_worker/service_worker_context_client_unittest.cc
@@ -285,6 +285,7 @@
             nullptr /* preference_watcher_request */,
             nullptr /* subresource_loaders */,
             blink::scheduler::GetSingleThreadTaskRunnerForTesting());
+    context_client->SetReportDebugLogForTesting(false);
 
     context_client->WorkerContextStarted(proxy);
 
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 34c71c2..0848bce 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -221,9 +221,9 @@
     // So far this test expects a single element (bytes or data pipe).
     ASSERT_EQ(1u, elements->size());
     network::DataElement& element = elements->front();
-    if (element.type() == network::DataElement::TYPE_BYTES) {
+    if (element.type() == network::mojom::DataElementType::kBytes) {
       *out_string = std::string(element.bytes(), element.length());
-    } else if (element.type() == network::DataElement::TYPE_DATA_PIPE) {
+    } else if (element.type() == network::mojom::DataElementType::kDataPipe) {
       // Read the content into |data_pipe|.
       mojo::DataPipe data_pipe;
       network::mojom::DataPipeGetterPtr ptr(element.ReleaseDataPipeGetter());
@@ -253,7 +253,7 @@
       DispatchFetchEventCallback callback) override {
     EXPECT_FALSE(params->request->is_main_resource_load);
     if (params->request->body)
-      request_body_ = params->request->body.value();
+      request_body_ = params->request->body;
 
     fetch_event_count_++;
     fetch_event_request_ = std::move(params->request);
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index a43632b..79de948e 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -262,9 +262,8 @@
     blink::WebServiceWorkerNetworkProvider* web_network_provider,
     base::OnceClosure callback) {
   ServiceWorkerProviderContext* context =
-      static_cast<WebServiceWorkerNetworkProviderImplForWorker*>(
+      ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
           web_network_provider)
-          ->provider()
           ->context();
   context->PingContainerHost(std::move(callback));
 }
@@ -273,10 +272,9 @@
 EmbeddedSharedWorkerStub::CreateWorkerFetchContext(
     blink::WebServiceWorkerNetworkProvider* web_network_provider) {
   DCHECK(web_network_provider);
-  ServiceWorkerProviderContext* provider_context =
+  ServiceWorkerNetworkProvider* network_provider =
       ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
-          web_network_provider)
-          ->context();
+          web_network_provider);
 
   // Make the factory used for service worker network fallback (that should
   // skip AppCache if it is provided).
@@ -285,7 +283,7 @@
 
   scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context =
       WebWorkerFetchContextImpl::Create(
-          provider_context, std::move(renderer_preferences_),
+          network_provider, std::move(renderer_preferences_),
           std::move(preference_watcher_request_),
           subresource_loader_factories_->Clone(), std::move(fallback_factory));
 
@@ -301,11 +299,6 @@
   // https://w3c.github.io/webappsec-secure-contexts/#examples-shared-workers
   worker_fetch_context->set_is_secure_context(IsOriginSecure(url_));
   worker_fetch_context->set_origin_url(url_.GetOrigin());
-  worker_fetch_context->set_service_worker_provider_id(
-      provider_context->provider_id());
-  worker_fetch_context->set_is_controlled_by_service_worker(
-      provider_context->IsControlledByServiceWorker());
-  worker_fetch_context->set_client_id(provider_context->client_id());
 
   return worker_fetch_context;
 }
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.h b/content/renderer/shared_worker/embedded_shared_worker_stub.h
index 01ec7c8a..a533171 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.h
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.h
@@ -21,12 +21,12 @@
 #include "third_party/blink/public/mojom/worker/shared_worker.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_host.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 #include "third_party/blink/public/platform/web_content_security_policy.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_shared_worker_client.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom.h"
 #include "url/gurl.h"
 
 namespace blink {
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index 537e1a40..f9b341330 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -359,7 +359,7 @@
   base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
       kTraceEventBrowserProcessSortIndex);
 
-  browser_runner_.reset(BrowserMainRunner::Create());
+  browser_runner_ = BrowserMainRunner::Create();
   base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
   return command_line.HasSwitch(switches::kRunWebTests)
              ? WebTestBrowserMain(main_function_params, browser_runner_)
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 5d9e157..7b574c00 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -997,6 +997,7 @@
     "//media/webrtc",
     "//mojo/core/embedder",
     "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/test_support:test_utils",
     "//net:test_support",
     "//ppapi/buildflags",
     "//services/audio/public/cpp",
@@ -1021,6 +1022,7 @@
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/blink/public:blink",
+    "//third_party/blink/public:core_mojo_bindings",
     "//third_party/leveldatabase",
     "//third_party/libaom:av1_buildflags",
     "//third_party/mesa_headers",
@@ -1698,7 +1700,6 @@
     "../common/inter_process_time_ticks_converter_unittest.cc",
     "../common/mac/attributed_string_coder_unittest.mm",
     "../common/manifest_util_unittest.cc",
-    "../common/media/media_devices_unittest.cc",
     "../common/mime_sniffing_throttle_unittest.cc",
     "../common/origin_util_unittest.cc",
     "../common/page_state_serialization_unittest.cc",
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index b1044f5f..a359a7c2 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -60,8 +60,7 @@
     : ContentTestSuiteBase(argc, argv) {
 }
 
-ContentTestSuite::~ContentTestSuite() {
-}
+ContentTestSuite::~ContentTestSuite() = default;
 
 void ContentTestSuite::Initialize() {
 #if defined(OS_MACOSX)
@@ -78,6 +77,8 @@
     ContentClient client;
     ContentTestSuiteBase::RegisterContentSchemes(&client);
   }
+  base::DiscardableMemoryAllocator::SetInstance(&discardable_memory_allocator_);
+
   RegisterPathProvider();
   media::InitializeMediaLibrary();
   // When running in a child process for Mac sandbox tests, the sandbox exists
diff --git a/content/test/content_test_suite.h b/content/test/content_test_suite.h
index 4656e3d..f17b155 100644
--- a/content/test/content_test_suite.h
+++ b/content/test/content_test_suite.h
@@ -9,6 +9,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/test/test_discardable_memory_allocator.h"
 #include "build/build_config.h"
 #include "content/public/test/content_test_suite_base.h"
 
@@ -27,6 +28,8 @@
   void Initialize() override;
 
  private:
+  base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
+
 #if defined(OS_WIN)
   base::win::ScopedCOMInitializer com_initializer_;
 #endif
diff --git a/content/test/data/accessibility/aria/aria-disabled-expected-android.txt b/content/test/data/accessibility/aria/aria-disabled-expected-android.txt
index 66be0c5..8378b97 100644
--- a/content/test/data/accessibility/aria/aria-disabled-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-disabled-expected-android.txt
@@ -1,13 +1,13 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View
 ++++android.widget.EditText clickable editable_text focusable input_type=1
-++++android.widget.EditText clickable disabled editable_text focusable input_type=1
+++++android.widget.EditText disabled editable_text focusable input_type=1
 ++++android.widget.EditText clickable editable_text focusable input_type=1
 ++android.view.View disabled
-++++android.widget.EditText clickable disabled editable_text focusable input_type=1
-++++android.widget.EditText clickable disabled editable_text focusable input_type=1
+++++android.widget.EditText disabled editable_text focusable input_type=1
+++++android.widget.EditText disabled editable_text focusable input_type=1
 ++++android.widget.EditText clickable editable_text focusable input_type=1
 ++android.view.View
 ++++android.widget.EditText clickable editable_text focusable input_type=1
-++++android.widget.EditText clickable disabled editable_text focusable input_type=1
+++++android.widget.EditText disabled editable_text focusable input_type=1
 ++++android.widget.EditText clickable editable_text focusable input_type=1
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt b/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt
index f7b4ecb..2e7b947 100644
--- a/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt
+++ b/content/test/data/accessibility/event/add-hidden-attribute-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_HIDE on <div#item3> role=ROLE_SYSTEM_LISTITEM name="Item 3" PosInSet=3 SetSize=3
-EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=3
+EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=2
 IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_LIST SetSize=3 old_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt b/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt
index 42f10c1..e556781 100644
--- a/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt
+++ b/content/test/data/accessibility/event/add-hidden-attribute-subtree-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_HIDE on <li#item3> role=ROLE_SYSTEM_LISTITEM PosInSet=3 SetSize=3
-EVENT_OBJECT_REORDER on <ul> role=ROLE_SYSTEM_LIST SetSize=3
+EVENT_OBJECT_REORDER on <ul> role=ROLE_SYSTEM_LIST SetSize=2
 IA2_EVENT_TEXT_REMOVED on <ul> role=ROLE_SYSTEM_LIST SetSize=3 old_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/accessibility/event/remove-child-expected-win.txt b/content/test/data/accessibility/event/remove-child-expected-win.txt
index f7b4ecb..2e7b947 100644
--- a/content/test/data/accessibility/event/remove-child-expected-win.txt
+++ b/content/test/data/accessibility/event/remove-child-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_HIDE on <div#item3> role=ROLE_SYSTEM_LISTITEM name="Item 3" PosInSet=3 SetSize=3
-EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=3
+EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_LIST SetSize=2
 IA2_EVENT_TEXT_REMOVED on <div> role=ROLE_SYSTEM_LIST SetSize=3 old_text={'<obj>' start=2 end=3}
diff --git a/content/test/data/accessibility/html/disabled-expected-android.txt b/content/test/data/accessibility/html/disabled-expected-android.txt
new file mode 100644
index 0000000..1d4751f9
--- /dev/null
+++ b/content/test/data/accessibility/html/disabled-expected-android.txt
@@ -0,0 +1,13 @@
+android.webkit.WebView focusable focused scrollable
+++android.widget.CheckBox role_description='checkbox' checkable clickable focusable name='Enabled'
+++android.widget.CheckBox role_description='checkbox' checkable disabled name='Disabled'
+++android.widget.Button role_description='button' clickable focusable name='Enabled'
+++android.widget.Button role_description='button' disabled name='Disabled'
+++android.view.View
+++++android.view.View name='Enabled form '
+++++android.widget.CheckBox role_description='checkbox' checkable clickable focusable name='Checkbox'
+++++android.widget.Button role_description='button' clickable focusable name='Button'
+++android.view.View disabled
+++++android.view.View name='Disabled form '
+++++android.widget.CheckBox role_description='checkbox' checkable disabled focusable name='Checkbox'
+++++android.widget.Button role_description='button' disabled focusable name='Button'
diff --git a/content/test/data/accessibility/html/disabled-expected-blink.txt b/content/test/data/accessibility/html/disabled-expected-blink.txt
new file mode 100644
index 0000000..39f3e37
--- /dev/null
+++ b/content/test/data/accessibility/html/disabled-expected-blink.txt
@@ -0,0 +1,15 @@
+rootWebArea
+++checkBox name='Enabled' checkedState=false
+++checkBox name='Disabled' restriction=disabled checkedState=false
+++button name='Enabled'
+++button name='Disabled' restriction=disabled
+++form
+++++staticText name='Enabled form '
+++++++inlineTextBox name='Enabled form '
+++++checkBox name='Checkbox' checkedState=false
+++++button name='Button'
+++form restriction=disabled
+++++staticText name='Disabled form '
+++++++inlineTextBox name='Disabled form '
+++++checkBox name='Checkbox' restriction=disabled checkedState=false
+++++button name='Button' restriction=disabled
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/disabled.html b/content/test/data/accessibility/html/disabled.html
new file mode 100644
index 0000000..b3d27599
--- /dev/null
+++ b/content/test/data/accessibility/html/disabled.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type="checkbox" aria-label="Enabled">
+    <input type="checkbox" disabled aria-label="Disabled">
+
+    <button>Enabled</button>
+    <button disabled>Disabled</button>
+
+    <form>
+      Enabled form
+      <input type="checkbox" aria-label="Checkbox">
+      <button>Button</button>
+    </form>
+    <form aria-disabled="true">
+      Disabled form
+      <input type="checkbox" aria-label="Checkbox">
+      <button>Button</button>
+    </form>
+  </body>
+</html>
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
index 1de731f..e4bdf1104 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-auralinux.txt
@@ -1,11 +1,11 @@
 [document web]
 ++[table]
-++++[table row] posinset:1 setsize:2
+++++[table row]
 ++++++[column header] name='Firstname' table-cell-index:0
 ++++++++[text] name='Firstname'
 ++++++[column header] name='Lastname' table-cell-index:1
 ++++++++[text] name='Lastname'
-++++[table row] posinset:2 setsize:2
+++++[table row]
 ++++++[table cell] name='Jill' table-cell-index:2
 ++++++++[text] name='Jill'
 ++++++[table cell] name='Smith' table-cell-index:3
diff --git a/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-blink.txt b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-blink.txt
new file mode 100644
index 0000000..db989ad
--- /dev/null
+++ b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-blink.txt
@@ -0,0 +1,12 @@
+rootWebArea language='en-US'
+++paragraph language='en'
+++++staticText language='en' name='In the morning, I sometimes eat breakfast.'
+++++++inlineTextBox language='en' name='In the morning, I sometimes eat breakfast.'
+++paragraph language='fr'
+++++staticText language='fr' name='Dans l'apres-midi, je dejeune.'
+++++++inlineTextBox language='fr' name='Dans l'apres-midi, je dejeune.'
+++paragraph language='en'
+++++staticText language='en' name='Hello it's a pleasure to meet you. '
+++++++inlineTextBox language='en' name='Hello it's a pleasure to meet you. '
+++++staticText language='fr' name='Comment ca va?'
+++++++inlineTextBox language='fr' name='Comment ca va?'
diff --git a/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-mac.txt b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-mac.txt
new file mode 100644
index 0000000..ae8315c
--- /dev/null
+++ b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-mac.txt
@@ -0,0 +1,8 @@
+AXWebArea AXLanguage='en-US'
+++AXGroup AXLanguage='en'
+++++AXStaticText AXValue='In the morning, I sometimes eat breakfast.'
+++AXGroup AXLanguage='fr'
+++++AXStaticText AXValue='Dans l'apres-midi, je dejeune.'
+++AXGroup AXLanguage='en'
+++++AXStaticText AXValue='Hello it's a pleasure to meet you. '
+++++AXStaticText AXValue='Comment ca va?' AXLanguage='fr'
diff --git a/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-win.txt b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-win.txt
new file mode 100644
index 0000000..26921d5f1
--- /dev/null
+++ b/content/test/data/accessibility/language-detection/lang-attribute-switching-expected-win.txt
@@ -0,0 +1,8 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE language:en language:fr language:en
+++IA2_ROLE_PARAGRAPH language:en
+++++ROLE_SYSTEM_STATICTEXT name='In the morning, I sometimes eat breakfast.' language:en
+++IA2_ROLE_PARAGRAPH language:fr
+++++ROLE_SYSTEM_STATICTEXT name='Dans l'apres-midi, je dejeune.' language:fr
+++IA2_ROLE_PARAGRAPH language:en language:fr
+++++ROLE_SYSTEM_STATICTEXT name='Hello it's a pleasure to meet you. ' language:en
+++++ROLE_SYSTEM_STATICTEXT name='Comment ca va?' language:fr
diff --git a/content/test/data/accessibility/language-detection/lang-attribute-switching.html b/content/test/data/accessibility/language-detection/lang-attribute-switching.html
new file mode 100644
index 0000000..ac7b7de
--- /dev/null
+++ b/content/test/data/accessibility/language-detection/lang-attribute-switching.html
@@ -0,0 +1,22 @@
+<!--
+@BLINK-ALLOW:language=*
+@WIN-ALLOW:language:*
+@MAC-ALLOW:AXLanguage*
+-->
+<!DOCTYPE html>
+<html lang="en">
+<body>
+  <!-- Unspecified elements inherit language from parent. -->
+  <p>
+    In the morning, I sometimes eat breakfast.
+  </p>
+  <p lang="fr">
+    Dans l'apres-midi, je dejeune.
+  </p>
+  <!-- Handle changing language in middle of paragraph. -->
+  <p>
+    Hello it's a pleasure to meet you. <span lang="fr">Comment ca va?</span>
+  </p>
+
+</body>
+</html>
diff --git a/content/test/data/scrollable_page.html b/content/test/data/scrollable_page.html
new file mode 100644
index 0000000..b9e6f49
--- /dev/null
+++ b/content/test/data/scrollable_page.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+  <meta name='viewport' content='width=device-width, minimum-scale=1'>
+</head>
+<style>
+body {
+  height: 10000px;
+  overflow: scroll;
+}
+</style>
diff --git a/content/test/data/sxg/generate-test-sxgs.sh b/content/test/data/sxg/generate-test-sxgs.sh
index e1b7920..0fac1c8 100755
--- a/content/test/data/sxg/generate-test-sxgs.sh
+++ b/content/test/data/sxg/generate-test-sxgs.sh
@@ -36,7 +36,7 @@
 
 # Generate the signed exchange file.
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.org/test/ \
   -status 200 \
   -content test.html \
@@ -59,20 +59,19 @@
 
 # Generate the signed exchange file with invalid magic string
 xxd -p test.example.org_test.sxg |
-  sed '1s/^737867312d623200/737867312d787800/' |
+  sed '1s/^737867312d62..00/737867312d787800/' |
   xxd -r -p > test.example.org_test_invalid_magic_string.sxg
 
 # Generate the signed exchange file with invalid cbor header.
-# 0x82 : start array of 2 elements.
-# 0xa1 : start map of 1 element -> 0xa4 : 4 elements.
+# 0xa4 : start map of 4 element -> 0xa5 : 5 elements.
 xxd -p test.example.org_test.sxg |
   tr -d '\n' |
-  sed 's/82a1/82a4/' |
+  sed 's/a44664/a54664/' |
   xxd -r -p > test.example.org_test_invalid_cbor_header.sxg
 
 # Generate the signed exchange file with noext certificate
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.org/test/ \
   -status 200 \
   -content test.html \
@@ -86,7 +85,7 @@
 
 # Generate the signed exchange file with invalid URL.
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.com/test/ \
   -status 200 \
   -content test.html \
@@ -100,7 +99,7 @@
 
 # Generate the signed exchange for a plain text file.
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.org/hello.txt \
   -status 200 \
   -content hello.txt \
@@ -117,7 +116,7 @@
 echo "===="
 
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.org/test/ \
   -content test.html \
   -certificate ./prime256v1-sha256.public.pem \
@@ -132,7 +131,7 @@
 xxd --include $tmpdir/out.cborheader | sed '1d;$d'
 
 gen-signedexchange \
-  -version 1b2 \
+  -version 1b3 \
   -uri https://test.example.org/test/ \
   -content test.html \
   -certificate ./secp384r1-sha256.public.pem \
diff --git a/content/test/data/sxg/test.example.com_invalid_test.sxg b/content/test/data/sxg/test.example.com_invalid_test.sxg
index 227935d..b63856b 100644
--- a/content/test/data/sxg/test.example.com_invalid_test.sxg
+++ b/content/test/data/sxg/test.example.com_invalid_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.com_invalid_test.sxg.mock-http-headers b/content/test/data/sxg/test.example.com_invalid_test.sxg.mock-http-headers
index 9617855..bded4e9 100644
--- a/content/test/data/sxg/test.example.com_invalid_test.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.com_invalid_test.sxg.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
diff --git a/content/test/data/sxg/test.example.org_hello.txt.sxg b/content/test/data/sxg/test.example.org_hello.txt.sxg
index 6f729e0..227dab6d 100644
--- a/content/test/data/sxg/test.example.org_hello.txt.sxg
+++ b/content/test/data/sxg/test.example.org_hello.txt.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_noext_test.sxg b/content/test/data/sxg/test.example.org_noext_test.sxg
index 57e3e2d..9439a4c 100644
--- a/content/test/data/sxg/test.example.org_noext_test.sxg
+++ b/content/test/data/sxg/test.example.org_noext_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test.sxg b/content/test/data/sxg/test.example.org_test.sxg
index 9d8d772..cefc173 100644
--- a/content/test/data/sxg/test.example.org_test.sxg
+++ b/content/test/data/sxg/test.example.org_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test.sxg.mock-http-headers b/content/test/data/sxg/test.example.org_test.sxg.mock-http-headers
index f481dcf3..5833ecf 100644
--- a/content/test/data/sxg/test.example.org_test.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.org_test.sxg.mock-http-headers
@@ -1,3 +1,3 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
 X-Content-Type-Options: nosniff
diff --git a/content/test/data/sxg/test.example.org_test_download.sxg b/content/test/data/sxg/test.example.org_test_download.sxg
index 9d8d772..cefc173 100644
--- a/content/test/data/sxg/test.example.org_test_download.sxg
+++ b/content/test/data/sxg/test.example.org_test_download.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_download.sxg.mock-http-headers b/content/test/data/sxg/test.example.org_test_download.sxg.mock-http-headers
index 4bb0ce56..5583488 100644
--- a/content/test/data/sxg/test.example.org_test_download.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.org_test_download.sxg.mock-http-headers
@@ -1,4 +1,4 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
 Content-Disposition: attachment; filename=test.sxg
 X-Content-Type-Options: nosniff
diff --git a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
index fea6add..1ab62c913 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg.mock-http-headers b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg.mock-http-headers
index f481dcf3..5833ecf 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.org_test_invalid_cbor_header.sxg.mock-http-headers
@@ -1,3 +1,3 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
 X-Content-Type-Options: nosniff
diff --git a/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg b/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
index 9d8d772..cefc173 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_content_type.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
index ee9dcff..db141c9 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
+++ b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg.mock-http-headers b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg.mock-http-headers
index f481dcf3..5833ecf 100644
--- a/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.org_test_invalid_magic_string.sxg.mock-http-headers
@@ -1,3 +1,3 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
 X-Content-Type-Options: nosniff
diff --git a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
index 9d8d772..cefc173 100644
--- a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
+++ b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg
Binary files differ
diff --git a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg.mock-http-headers b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg.mock-http-headers
index 9617855..bded4e9 100644
--- a/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg.mock-http-headers
+++ b/content/test/data/sxg/test.example.org_test_missing_nosniff.sxg.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
diff --git a/content/test/gpu/gpu_tests/context_lost_expectations.py b/content/test/gpu/gpu_tests/context_lost_expectations.py
index 5eb0134..cc735d9 100644
--- a/content/test/gpu/gpu_tests/context_lost_expectations.py
+++ b/content/test/gpu/gpu_tests/context_lost_expectations.py
@@ -58,12 +58,6 @@
     self.Skip('ContextLost_WebGLContextLostInHiddenTab',
               ['android'], bug=609629)
 
-    # Timing out on Nexus 5.
-    self.Skip('ContextLost_WebGLBlockedAfterJSNavigation',
-              ['android', ('qualcomm', 'Adreno (TM) 330')], bug=911678)
-    self.Skip('ContextLost_WebGLUnblockedAfterUserInitiatedReload',
-              ['android', ('qualcomm', 'Adreno (TM) 330')], bug=911678)
-
     # Flaking on Nexus 5X
     self.Flaky('ContextLost_WebGLUnblockedAfterUserInitiatedReload',
               ['android', ('qualcomm', 'Adreno (TM) 418')], bug=879423)
@@ -78,8 +72,3 @@
               ['android', ('qualcomm', 'Adreno (TM) 420')], bug=611906)
     self.Fail('ContextLost_WebGLContextLostFromQuantity',
               ['android', ('qualcomm', 'Adreno (TM) 420')], bug=611906)
-    # The following two tests time out.
-    self.Skip('ContextLost_WebGLBlockedAfterJSNavigation',
-              ['android', ('qualcomm', 'Adreno (TM) 420')], bug=911678)
-    self.Skip('ContextLost_WebGLUnblockedAfterUserInitiatedReload',
-              ['android', ('qualcomm', 'Adreno (TM) 420')], bug=911678)
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index 09da641c..fd88996 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -147,3 +147,7 @@
         ['mac', ('amd', 0x679e)], bug=911413)
     self.Fail('Pixel_Video_MP4_FourColors_Rot_270',
         ['mac', ('amd', 0x679e)], bug=911413)
+
+    # Flaky on Windows: crbug.com/921279
+    self.Flaky('Pixel_DirectComposition_ComplexOverlays',
+        ['win', 'nvidia'], bug=921279)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 742adbb7..3219940d3 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -748,6 +748,8 @@
     # This test is skipped because it is crashing the GPU process.
     self.Skip('conformance/glsl/bugs/init-array-with-loop.html',
         ['android', ('qualcomm', 'Adreno (TM) 418')], bug=784817)
+    self.Flaky('conformance/glsl/bugs/loop-if-loop-gradient.html',
+        ['android', ('qualcomm', 'Adreno (TM) 418')], bug=920737)
     self.Fail('conformance/glsl/bugs/sampler-struct-function-arg.html',
         ['android', ('qualcomm', 'Adreno (TM) 418')], bug=609883)
     self.Flaky('conformance/glsl/constructors/glsl-construct-ivec4.html',
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index 651a591..edaef49 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -49,7 +49,7 @@
 #include "gin/v8_initializer.h"  // nogncheck
 #endif
 
-#include "third_party/webrtc/rtc_base/rtccertificate.h"  // nogncheck
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"  // nogncheck
 
 using blink::WebString;
 
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 777b6d1..5c0a5dc 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -264,60 +264,11 @@
     ui::PageTransition transition,
     int response_code,
     const ModificationCallback& callback) {
-  FrameHostMsg_DidCommitProvisionalLoad_Params params;
-  params.nav_entry_id = nav_entry_id;
-  params.url = url;
-  params.origin = url::Origin::Create(url);
-  params.transition = transition;
-  params.should_update_history = true;
-  params.did_create_new_entry = did_create_new_entry;
-  params.should_replace_current_entry = should_replace_entry;
-  params.gesture = NavigationGestureUser;
-  params.contents_mime_type = contents_mime_type_;
-  params.method = "GET";
-  params.http_status_code = response_code;
-  params.socket_address.set_host("2001:db8::1");
-  params.socket_address.set_port(80);
-  params.history_list_was_cleared = simulate_history_list_was_cleared_;
-  params.original_request_url = url;
-
-  // Simulate Blink assigning an item and document sequence number to the
-  // navigation.
-  params.item_sequence_number = base::Time::Now().ToDoubleT() * 1000000;
-  params.document_sequence_number = params.item_sequence_number + 1;
-
-  // When the user hits enter in the Omnibox without changing the URL, Blink
-  // behaves similarly to a reload and does not change the item and document
-  // sequence numbers. Simulate this behavior here too.
-  if (PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
-    NavigationEntryImpl* entry =
-        static_cast<NavigationEntryImpl*>(frame_tree_node()
-                                              ->navigator()
-                                              ->GetController()
-                                              ->GetLastCommittedEntry());
-    if (entry && entry->GetURL() == url) {
-      FrameNavigationEntry* frame_entry =
-          entry->GetFrameEntry(frame_tree_node());
-      if (frame_entry) {
-        params.item_sequence_number = frame_entry->item_sequence_number();
-        params.document_sequence_number =
-            frame_entry->document_sequence_number();
-      }
-    }
-  }
-
-  // In most cases, the origin will match the URL's origin.  Tests that need to
-  // check corner cases (like about:blank) should specify the origin param
-  // manually.
-  url::Origin origin = url::Origin::Create(url);
-  params.origin = origin;
-
-  url::Replacements<char> replacements;
-  replacements.ClearRef();
-
   // This approach to determining whether a navigation is to be treated as
   // same document is not robust, as it will not handle pushState type
   // navigation. Do not use elsewhere!
+  url::Replacements<char> replacements;
+  replacements.ClearRef();
   bool was_within_same_document =
       !ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD) &&
       !ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) &&
@@ -325,51 +276,28 @@
        url.ReplaceComponents(replacements) ==
            GetLastCommittedURL().ReplaceComponents(replacements));
 
-  params.page_state = PageState::CreateForTestingWithSequenceNumbers(
-      url, params.item_sequence_number, params.document_sequence_number);
+  auto params = BuildDidCommitParams(nav_entry_id, did_create_new_entry,
+                                     should_replace_entry, url, transition,
+                                     response_code);
 
   if (!callback.is_null())
-    callback.Run(&params);
+    callback.Run(params.get());
 
-  SendNavigateWithParams(&params, was_within_same_document);
+  SendNavigateWithParams(params.get(), was_within_same_document);
 }
 
 void TestRenderFrameHost::SendNavigateWithParams(
     FrameHostMsg_DidCommitProvisionalLoad_Params* params,
     bool was_within_same_document) {
-  service_manager::mojom::InterfaceProviderPtr interface_provider;
-  service_manager::mojom::InterfaceProviderRequest interface_provider_request;
-
-  blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content;
-  blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_blink;
-  blink::mojom::DocumentInterfaceBrokerRequest
-      document_interface_broker_content_request;
-  blink::mojom::DocumentInterfaceBrokerRequest
-      document_interface_broker_blink_request;
-
-  if (!was_within_same_document) {
-    interface_provider_request = mojo::MakeRequest(&interface_provider);
-
-    document_interface_broker_content_request =
-        mojo::MakeRequest(&document_interface_broker_content);
-    document_interface_broker_blink_request =
-        mojo::MakeRequest(&document_interface_broker_blink);
-  }
-
-  SendNavigateWithParamsAndInterfaceProvider(
-      params, std::move(interface_provider_request),
-      std::move(document_interface_broker_content_request),
-      std::move(document_interface_broker_blink_request),
+  SendNavigateWithParamsAndInterfaceParams(
+      std::move(params),
+      BuildDidCommitInterfaceParams(was_within_same_document),
       was_within_same_document);
 }
 
-void TestRenderFrameHost::SendNavigateWithParamsAndInterfaceProvider(
+void TestRenderFrameHost::SendNavigateWithParamsAndInterfaceParams(
     FrameHostMsg_DidCommitProvisionalLoad_Params* params,
-    service_manager::mojom::InterfaceProviderRequest request,
-    blink::mojom::DocumentInterfaceBrokerRequest
-        document_interface_broker_content_request,
-    blink::mojom::DocumentInterfaceBrokerRequest
-        document_interface_broker_blink_request,
+    mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
     bool was_within_same_document) {
   if (GetNavigationHandle()) {
     scoped_refptr<net::HttpResponseHeaders> response_headers =
@@ -386,10 +314,7 @@
   } else {
     DidCommitProvisionalLoad(
         std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>(*params),
-        mojom::DidCommitProvisionalLoadInterfaceParams::New(
-            std::move(request),
-            std::move(document_interface_broker_content_request),
-            std::move(document_interface_broker_blink_request)));
+        std::move(interface_params));
   }
   last_commit_was_error_page_ = params->url_is_unreachable;
 }
@@ -559,10 +484,13 @@
   }
 
   if (params) {
-    SendNavigateWithParamsAndInterfaceProvider(
-        params.release(), std::move(interface_provider_request),
-        std::move(document_interface_broker_content_request),
-        std::move(document_interface_broker_blink_request), same_document);
+    SendNavigateWithParamsAndInterfaceParams(
+        params.release(),
+        mojom::DidCommitProvisionalLoadInterfaceParams::New(
+            std::move(interface_provider_request),
+            std::move(document_interface_broker_content_request),
+            std::move(document_interface_broker_blink_request)),
+        same_document);
   }
 }
 
@@ -620,6 +548,94 @@
   }
 }
 
+std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
+TestRenderFrameHost::BuildDidCommitParams(int nav_entry_id,
+                                          bool did_create_new_entry,
+                                          bool should_replace_entry,
+                                          const GURL& url,
+                                          ui::PageTransition transition,
+                                          int response_code) {
+  auto params =
+      std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>();
+  params->nav_entry_id = nav_entry_id;
+  params->url = url;
+  params->transition = transition;
+  params->should_update_history = true;
+  params->did_create_new_entry = did_create_new_entry;
+  params->should_replace_current_entry = should_replace_entry;
+  params->gesture = NavigationGestureUser;
+  params->contents_mime_type = contents_mime_type_;
+  params->method = "GET";
+  params->http_status_code = response_code;
+  params->socket_address.set_host("2001:db8::1");
+  params->socket_address.set_port(80);
+  params->history_list_was_cleared = simulate_history_list_was_cleared_;
+  params->original_request_url = url;
+
+  // Simulate Blink assigning an item and document sequence number to the
+  // navigation.
+  params->item_sequence_number = base::Time::Now().ToDoubleT() * 1000000;
+  params->document_sequence_number = params->item_sequence_number + 1;
+
+  // When the user hits enter in the Omnibox without changing the URL, Blink
+  // behaves similarly to a reload and does not change the item and document
+  // sequence numbers. Simulate this behavior here too.
+  if (PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
+    NavigationEntryImpl* entry =
+        static_cast<NavigationEntryImpl*>(frame_tree_node()
+                                              ->navigator()
+                                              ->GetController()
+                                              ->GetLastCommittedEntry());
+    if (entry && entry->GetURL() == url) {
+      FrameNavigationEntry* frame_entry =
+          entry->GetFrameEntry(frame_tree_node());
+      if (frame_entry) {
+        params->item_sequence_number = frame_entry->item_sequence_number();
+        params->document_sequence_number =
+            frame_entry->document_sequence_number();
+      }
+    }
+  }
+
+  // In most cases, the origin will match the URL's origin.  Tests that need to
+  // check corner cases (like about:blank) should specify the origin param
+  // manually.
+  url::Origin origin = url::Origin::Create(url);
+  params->origin = origin;
+
+  params->page_state = PageState::CreateForTestingWithSequenceNumbers(
+      url, params->item_sequence_number, params->document_sequence_number);
+
+  return params;
+}
+
+mojom::DidCommitProvisionalLoadInterfaceParamsPtr
+TestRenderFrameHost::BuildDidCommitInterfaceParams(bool is_same_document) {
+  service_manager::mojom::InterfaceProviderPtr interface_provider;
+  service_manager::mojom::InterfaceProviderRequest interface_provider_request;
+
+  blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content;
+  blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_blink;
+  blink::mojom::DocumentInterfaceBrokerRequest
+      document_interface_broker_content_request;
+  blink::mojom::DocumentInterfaceBrokerRequest
+      document_interface_broker_blink_request;
+
+  if (!is_same_document) {
+    interface_provider_request = mojo::MakeRequest(&interface_provider);
+    document_interface_broker_content_request =
+        mojo::MakeRequest(&document_interface_broker_content);
+    document_interface_broker_blink_request =
+        mojo::MakeRequest(&document_interface_broker_blink);
+  }
+
+  auto interface_params = mojom::DidCommitProvisionalLoadInterfaceParams::New(
+      std::move(interface_provider_request),
+      std::move(document_interface_broker_content_request),
+      std::move(document_interface_broker_blink_request));
+  return interface_params;
+}
+
 // static
 service_manager::mojom::InterfaceProviderRequest
 TestRenderFrameHost::CreateStubInterfaceProviderRequest() {
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index f47538b..a8e202bc 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -99,13 +99,9 @@
   void SendNavigateWithParams(
       FrameHostMsg_DidCommitProvisionalLoad_Params* params,
       bool was_within_same_document);
-  void SendNavigateWithParamsAndInterfaceProvider(
+  void SendNavigateWithParamsAndInterfaceParams(
       FrameHostMsg_DidCommitProvisionalLoad_Params* params,
-      service_manager::mojom::InterfaceProviderRequest request,
-      blink::mojom::DocumentInterfaceBrokerRequest
-          document_interface_broker_content_request,
-      blink::mojom::DocumentInterfaceBrokerRequest
-          document_interface_broker_blink_request,
+      mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
       bool was_within_same_document);
 
   // With the current navigation logic this method is a no-op.
@@ -253,6 +249,17 @@
   // Computes the page ID for a pending navigation in this RenderFrameHost;
   int32_t ComputeNextPageID();
 
+  std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
+  BuildDidCommitParams(int nav_entry_id,
+                       bool did_create_new_entry,
+                       bool should_replace_entry,
+                       const GURL& url,
+                       ui::PageTransition transition,
+                       int response_code);
+
+  mojom::DidCommitProvisionalLoadInterfaceParamsPtr
+  BuildDidCommitInterfaceParams(bool is_same_document);
+
   // Keeps a running vector of messages sent to AddMessageToConsole.
   std::vector<std::string> console_messages_;
 
diff --git a/device/fido/ctap_make_credential_request.cc b/device/fido/ctap_make_credential_request.cc
index 256aa28cc..b01aff6f 100644
--- a/device/fido/ctap_make_credential_request.cc
+++ b/device/fido/ctap_make_credential_request.cc
@@ -133,13 +133,6 @@
   return *this;
 }
 
-CtapMakeCredentialRequest&
-CtapMakeCredentialRequest::SetIsIndividualAttestation(
-    bool is_individual_attestation) {
-  is_individual_attestation_ = is_individual_attestation;
-  return *this;
-}
-
 CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetHmacSecret(
     bool hmac_secret) {
   hmac_secret_ = hmac_secret;
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h
index d639b0ad..80500544 100644
--- a/device/fido/ctap_make_credential_request.h
+++ b/device/fido/ctap_make_credential_request.h
@@ -55,8 +55,6 @@
       std::vector<PublicKeyCredentialDescriptor> exclude_list);
   CtapMakeCredentialRequest& SetPinAuth(std::vector<uint8_t> pin_auth);
   CtapMakeCredentialRequest& SetPinProtocol(uint8_t pin_protocol);
-  CtapMakeCredentialRequest& SetIsIndividualAttestation(
-      bool is_individual_attestation);
   CtapMakeCredentialRequest& SetHmacSecret(bool hmac_secret);
 
   const std::string& client_data_json() const { return client_data_json_; }
@@ -75,7 +73,6 @@
     return authenticator_attachment_;
   }
   bool resident_key_required() const { return resident_key_required_; }
-  bool is_individual_attestation() const { return is_individual_attestation_; }
   bool hmac_secret() const { return hmac_secret_; }
   const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
   exclude_list() const {
@@ -93,6 +90,14 @@
     is_incognito_mode_ = is_incognito_mode;
   }
 
+  AttestationConveyancePreference attestation_preference() const {
+    return attestation_preference_;
+  }
+  void set_attestation_preference(
+      AttestationConveyancePreference attestation_preference) {
+    attestation_preference_ = attestation_preference;
+  }
+
  private:
   std::string client_data_json_;
   std::array<uint8_t, kClientDataHashLength> client_data_hash_;
@@ -104,7 +109,6 @@
   AuthenticatorAttachment authenticator_attachment_ =
       AuthenticatorAttachment::kAny;
   bool resident_key_required_ = false;
-  bool is_individual_attestation_ = false;
   // hmac_secret_ indicates whether the "hmac-secret" extension should be
   // asserted to CTAP2 authenticators.
   bool hmac_secret_ = false;
@@ -117,6 +121,8 @@
   base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list_;
   base::Optional<std::vector<uint8_t>> pin_auth_;
   base::Optional<uint8_t> pin_protocol_;
+  AttestationConveyancePreference attestation_preference_ =
+      AttestationConveyancePreference::NONE;
 };
 
 }  // namespace device
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index a356d1e..02cc5b4 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -386,6 +386,16 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 extern const base::TimeDelta kBleDevicePairingModeWaitingInterval;
 
+// https://w3c.github.io/webauthn/#attestation-convey
+enum class AttestationConveyancePreference : uint8_t {
+  NONE,
+  INDIRECT,
+  DIRECT,
+  // Non-standard value for individual attestation that we hope to end up in
+  // the standard eventually.
+  ENTERPRISE,
+};
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FIDO_CONSTANTS_H_
diff --git a/device/fido/u2f_command_constructor.cc b/device/fido/u2f_command_constructor.cc
index 733d99c..22311068 100644
--- a/device/fido/u2f_command_constructor.cc
+++ b/device/fido/u2f_command_constructor.cc
@@ -41,9 +41,12 @@
   if (!IsConvertibleToU2fRegisterCommand(request))
     return base::nullopt;
 
+  const bool is_invidual_attestation =
+      request.attestation_preference() ==
+      AttestationConveyancePreference::ENTERPRISE;
   return ConstructU2fRegisterCommand(
       fido_parsing_utils::CreateSHA256Hash(request.rp().rp_id()),
-      request.client_data_hash(), request.is_individual_attestation());
+      request.client_data_hash(), is_invidual_attestation);
 }
 
 base::Optional<std::vector<uint8_t>> ConvertToU2fCheckOnlySignCommand(
diff --git a/device/fido/u2f_register_operation_unittest.cc b/device/fido/u2f_register_operation_unittest.cc
index dacdfd3f..567ce61 100644
--- a/device/fido/u2f_register_operation_unittest.cc
+++ b/device/fido/u2f_register_operation_unittest.cc
@@ -39,7 +39,10 @@
       PublicKeyCredentialParams(
           std::vector<PublicKeyCredentialParams::CredentialInfo>(1)));
   request.SetExcludeList(std::move(registered_keys));
-  request.SetIsIndividualAttestation(is_individual_attestation);
+  if (is_individual_attestation)
+    request.set_attestation_preference(
+        AttestationConveyancePreference::ENTERPRISE);
+
   return request;
 }
 
diff --git a/device/fido/win/authenticator.cc b/device/fido/win/authenticator.cc
index eca65dab..c26df07 100644
--- a/device/fido/win/authenticator.cc
+++ b/device/fido/win/authenticator.cc
@@ -122,7 +122,9 @@
           WEBAUTHN_EXTENSIONS{0, nullptr},  // will be set later
           authenticator_attachment, request.resident_key_required(),
           ToWinUserVerificationRequirement(request.user_verification()),
-          WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT, 0 /* flags */,
+          ToWinAttestationConveyancePreference(
+              request.attestation_preference()),
+          0 /* flags */,
           nullptr,  // pCancellationId -- will be set later
           nullptr,  // pExcludeCredentialList -- will be set later
       },
diff --git a/device/fido/win/type_conversions.cc b/device/fido/win/type_conversions.cc
index e7db776..c381fc7 100644
--- a/device/fido/win/type_conversions.cc
+++ b/device/fido/win/type_conversions.cc
@@ -226,4 +226,21 @@
              : CtapDeviceResponseCode::kCtap2ErrOther;
 }
 
+uint32_t ToWinAttestationConveyancePreference(
+    const AttestationConveyancePreference& value) {
+  switch (value) {
+    case AttestationConveyancePreference::NONE:
+      return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
+    case AttestationConveyancePreference::INDIRECT:
+      return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
+    case AttestationConveyancePreference::DIRECT:
+      return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
+    case AttestationConveyancePreference::ENTERPRISE:
+      // Windows does not support enterprise attestation.
+      return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
+  }
+  NOTREACHED();
+  return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
+}
+
 }  // namespace device
diff --git a/device/fido/win/type_conversions.h b/device/fido/win/type_conversions.h
index b0656ae..740979b 100644
--- a/device/fido/win/type_conversions.h
+++ b/device/fido/win/type_conversions.h
@@ -49,6 +49,10 @@
 CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode(
     const base::string16& error_name);
 
+COMPONENT_EXPORT(DEVICE_FIDO)
+uint32_t ToWinAttestationConveyancePreference(
+    const AttestationConveyancePreference&);
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_WIN_TYPE_CONVERSIONS_H_
diff --git a/docs/accessibility/perf.md b/docs/accessibility/perf.md
index e0a59092..e6a714a 100644
--- a/docs/accessibility/perf.md
+++ b/docs/accessibility/perf.md
@@ -37,6 +37,10 @@
 
 ```tools/perf/run_benchmark system_health.common_desktop --story-filter="accessibility.*" --browser canary```
 
+To run the same set of tests on your own compiled version of Chrome:
+
+```tools/perf/run_benchmark system_health.common_desktop --story-filter="accessibility.*" --browser=exact --browser-executable=out/Release/chrome```
+
 See the [documentation](https://github.com/catapult-project/catapult/blob/master/telemetry/docs/run_benchmarks_locally.md)
 or command-line help for tools/perf/run_benchmark for
 more command-line arguments.
@@ -61,6 +65,10 @@
 
 ```third_party/blink/perf_tests/accessibility/```
 
+Example command line to run these tests locally on your own compiled Chrome:
+
+```tools/perf/run_benchmark blink_perf.accessibility --browser=exact --browser-executable=out/Release/chrome```
+
 ## Results
 
 The results can be found at
diff --git a/extensions/browser/api/dns/dns_api.h b/extensions/browser/api/dns/dns_api.h
index 4eb429e..e2ca207 100644
--- a/extensions/browser/api/dns/dns_api.h
+++ b/extensions/browser/api/dns/dns_api.h
@@ -11,12 +11,13 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
 namespace extensions {
 
 class DnsResolveFunction : public UIThreadExtensionFunction,
-                           public network::mojom::ResolveHostClient {
+                           public network::ResolveHostClientBase {
  public:
   DECLARE_EXTENSION_FUNCTION("dns.resolve", DNS_RESOLVE)
 
diff --git a/extensions/browser/api/media_perception_private/conversion_utils.cc b/extensions/browser/api/media_perception_private/conversion_utils.cc
index 397bcf1..d35285f 100644
--- a/extensions/browser/api/media_perception_private/conversion_utils.cc
+++ b/extensions/browser/api/media_perception_private/conversion_utils.cc
@@ -536,6 +536,8 @@
       return FEATURE_OCCUPANCY_DETECTION;
     case mri::State::FEATURE_EDGE_EMBEDDINGS:
       return FEATURE_EDGE_EMBEDDINGS;
+    case mri::State::FEATURE_SOFTWARE_CROPPING:
+      return FEATURE_SOFTWARE_CROPPING;
     case mri::State::FEATURE_UNSET:
       return FEATURE_NONE;
   }
@@ -553,6 +555,8 @@
       return mri::State::FEATURE_OCCUPANCY_DETECTION;
     case FEATURE_EDGE_EMBEDDINGS:
       return mri::State::FEATURE_EDGE_EMBEDDINGS;
+    case FEATURE_SOFTWARE_CROPPING:
+      return mri::State::FEATURE_SOFTWARE_CROPPING;
     case FEATURE_NONE:
       return mri::State::FEATURE_UNSET;
   }
diff --git a/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc b/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
index 22172dc..43cc8cb 100644
--- a/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
+++ b/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
@@ -528,6 +528,7 @@
   state.add_features(mri::State::FEATURE_HOTWORD_DETECTION);
   state.add_features(mri::State::FEATURE_OCCUPANCY_DETECTION);
   state.add_features(mri::State::FEATURE_EDGE_EMBEDDINGS);
+  state.add_features(mri::State::FEATURE_SOFTWARE_CROPPING);
   state.add_features(mri::State::FEATURE_UNSET);
 
   // Number NamedTemplateArgument.
@@ -564,7 +565,7 @@
 
   ASSERT_TRUE(state_result.features);
   ASSERT_TRUE(state_result.features.get());
-  ASSERT_EQ(state_result.features.get()->size(), 4u);
+  ASSERT_EQ(state_result.features.get()->size(), 5u);
   EXPECT_EQ(state_result.features.get()->at(0),
             media_perception::FEATURE_AUTOZOOM);
   EXPECT_EQ(state_result.features.get()->at(1),
@@ -573,6 +574,8 @@
             media_perception::FEATURE_OCCUPANCY_DETECTION);
   EXPECT_EQ(state_result.features.get()->at(3),
             media_perception::FEATURE_EDGE_EMBEDDINGS);
+  EXPECT_EQ(state_result.features.get()->at(4),
+            media_perception::FEATURE_SOFTWARE_CROPPING);
 
   ASSERT_EQ(state_result.named_template_arguments->size(),
             kNamedTemplateArgumentsSize);
@@ -644,6 +647,7 @@
   state.features->emplace_back(media_perception::FEATURE_HOTWORD_DETECTION);
   state.features->emplace_back(media_perception::FEATURE_OCCUPANCY_DETECTION);
   state.features->emplace_back(media_perception::FEATURE_EDGE_EMBEDDINGS);
+  state.features->emplace_back(media_perception::FEATURE_SOFTWARE_CROPPING);
   state.features->emplace_back(media_perception::FEATURE_NONE);
 
   // {Number, Empty, String} test cases.
@@ -688,12 +692,13 @@
   ASSERT_TRUE(state_proto.whiteboard().has_aspect_ratio());
   EXPECT_EQ(state_proto.whiteboard().aspect_ratio(), kWhiteboardAspectRatio);
 
-  ASSERT_EQ(state_proto.features_size(), 5);
+  ASSERT_EQ(state_proto.features_size(), 6);
   EXPECT_EQ(state_proto.features(0), mri::State::FEATURE_AUTOZOOM);
   EXPECT_EQ(state_proto.features(1), mri::State::FEATURE_HOTWORD_DETECTION);
   EXPECT_EQ(state_proto.features(2), mri::State::FEATURE_OCCUPANCY_DETECTION);
   EXPECT_EQ(state_proto.features(3), mri::State::FEATURE_EDGE_EMBEDDINGS);
-  EXPECT_EQ(state_proto.features(4), mri::State::FEATURE_UNSET);
+  EXPECT_EQ(state_proto.features(4), mri::State::FEATURE_SOFTWARE_CROPPING);
+  EXPECT_EQ(state_proto.features(5), mri::State::FEATURE_UNSET);
 
   ASSERT_EQ(state_proto.named_template_arguments_size(),
             static_cast<int>(kNamedTemplateArgumentsSize));
diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h
index 5a89a6e..00007c80 100644
--- a/extensions/browser/api/socket/socket_api.h
+++ b/extensions/browser/api/socket/socket_api.h
@@ -25,6 +25,7 @@
 #include "net/base/address_list.h"
 #include "net/base/network_change_notifier.h"
 #include "net/socket/tcp_client_socket.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/udp_socket.mojom.h"
@@ -149,7 +150,7 @@
 
 class SocketExtensionWithDnsLookupFunction
     : public SocketAsyncApiFunction,
-      public network::mojom::ResolveHostClient {
+      public network::ResolveHostClientBase {
  protected:
   SocketExtensionWithDnsLookupFunction();
   ~SocketExtensionWithDnsLookupFunction() override;
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index 011e516a..2c2524a0 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -166,16 +166,16 @@
 
   for (auto& element : *request.request_body->elements()) {
     switch (element.type()) {
-      case network::DataElement::TYPE_DATA_PIPE:
+      case network::mojom::DataElementType::kDataPipe:
         // TODO(https://crbug.com/721414): Support data pipe elements.
         break;
 
-      case network::DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         data_sources->push_back(std::make_unique<BytesUploadDataSource>(
             base::StringPiece(element.bytes(), element.length())));
         break;
 
-      case network::DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         // TODO(https://crbug.com/715679): This may not work when network
         // process is sandboxed.
         data_sources->push_back(
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 31430a9..3590a3a0 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -300,7 +300,8 @@
       "2B6C6A4A5940017146F3E58B7F90116206E84685",  // http://crbug.com/642141
       "B6C2EFAB3EC3BF6EF03701408B6B09A67B2D0069",  // http://crbug.com/642141
       "96FF2FFA5C9173C76D47184B3E86D267B37781DE",  // http://crbug.com/642141
-      "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F"   // http://crbug.com/642141
+      "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F",  // http://crbug.com/642141
+      "CBCC42ABED43A4B58FE3810E62AFFA010EB0349F"   // PDF Viewer
     ]
   }],
   "fileSystem.directory": {
diff --git a/extensions/common/api/media_perception_private.idl b/extensions/common/api/media_perception_private.idl
index d6a6fa4..10b2079 100644
--- a/extensions/common/api/media_perception_private.idl
+++ b/extensions/common/api/media_perception_private.idl
@@ -67,7 +67,8 @@
     AUTOZOOM,
     HOTWORD_DETECTION,
     OCCUPANCY_DETECTION,
-    EDGE_EMBEDDINGS
+    EDGE_EMBEDDINGS,
+    SOFTWARE_CROPPING
   };
 
   dictionary NamedTemplateArgument {
diff --git a/extensions/common/manifest.cc b/extensions/common/manifest.cc
index 058b4f1..588e6f1 100644
--- a/extensions/common/manifest.cc
+++ b/extensions/common/manifest.cc
@@ -17,6 +17,7 @@
 #include "extensions/common/features/feature_provider.h"
 #include "extensions/common/install_warning.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handler_helpers.h"
 
 namespace extensions {
 
@@ -241,11 +242,32 @@
   return CanAccessPath(path) && value_->GetDictionary(path, out_value);
 }
 
+bool Manifest::GetDictionary(const std::string& path,
+                             const base::Value** out_value) const {
+  return GetPathOfType(path, base::Value::Type::DICTIONARY, out_value);
+}
+
 bool Manifest::GetList(
     const std::string& path, const base::ListValue** out_value) const {
   return CanAccessPath(path) && value_->GetList(path, out_value);
 }
 
+bool Manifest::GetList(const std::string& path,
+                       const base::Value** out_value) const {
+  return GetPathOfType(path, base::Value::Type::LIST, out_value);
+}
+
+bool Manifest::GetPathOfType(const std::string& path,
+                             base::Value::Type type,
+                             const base::Value** out_value) const {
+  const std::vector<base::StringPiece> components =
+      manifest_handler_helpers::TokenizeDictionaryPath(path);
+  if (!CanAccessPath(components))
+    return false;
+  *out_value = value_->FindPathOfType(components, type);
+  return *out_value != nullptr;
+}
+
 std::unique_ptr<Manifest> Manifest::CreateDeepCopy() const {
   auto manifest =
       std::make_unique<Manifest>(location_, value_->CreateDeepCopy());
@@ -266,9 +288,12 @@
 }
 
 bool Manifest::CanAccessPath(const std::string& path) const {
+  return CanAccessPath(manifest_handler_helpers::TokenizeDictionaryPath(path));
+}
+
+bool Manifest::CanAccessPath(base::span<const base::StringPiece> path) const {
   std::string key;
-  for (const base::StringPiece& component : base::SplitStringPiece(
-           path, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+  for (base::StringPiece component : path) {
     component.AppendToString(&key);
     if (!CanAccessKey(key))
       return false;
diff --git a/extensions/common/manifest.h b/extensions/common/manifest.h
index 609643b..d9709f4a 100644
--- a/extensions/common/manifest.h
+++ b/extensions/common/manifest.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/values.h"
@@ -191,10 +192,21 @@
   bool GetInteger(const std::string& path, int* out_value) const;
   bool GetString(const std::string& path, std::string* out_value) const;
   bool GetString(const std::string& path, base::string16* out_value) const;
+  // Deprecated: Use the GetDictionary() overload that accepts a base::Value
+  // output parameter instead.
   bool GetDictionary(const std::string& path,
                      const base::DictionaryValue** out_value) const;
+  bool GetDictionary(const std::string& path,
+                     const base::Value** out_value) const;
+  // Deprecated: Use the GetList() overload that accepts a base::Value output
+  // parameter instead.
   bool GetList(const std::string& path,
                const base::ListValue** out_value) const;
+  bool GetList(const std::string& path, const base::Value** out_value) const;
+
+  bool GetPathOfType(const std::string& path,
+                     base::Value::Type type,
+                     const base::Value** out_value) const;
 
   // Returns a new Manifest equal to this one.
   std::unique_ptr<Manifest> CreateDeepCopy() const;
@@ -209,6 +221,7 @@
  private:
   // Returns true if the extension can specify the given |path|.
   bool CanAccessPath(const std::string& path) const;
+  bool CanAccessPath(const base::span<const base::StringPiece> path) const;
   bool CanAccessKey(const std::string& key) const;
 
   // A persistent, globally unique ID. An extension's ID is used in things
diff --git a/extensions/common/manifest_handler_helpers.cc b/extensions/common/manifest_handler_helpers.cc
index 4a644b8..8ee5ed2 100644
--- a/extensions/common/manifest_handler_helpers.cc
+++ b/extensions/common/manifest_handler_helpers.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "extensions/common/constants.h"
@@ -22,36 +23,45 @@
 
 namespace manifest_handler_helpers {
 
+std::vector<base::StringPiece> TokenizeDictionaryPath(base::StringPiece path) {
+  return base::SplitStringPiece(path, ".", base::TRIM_WHITESPACE,
+                                base::SPLIT_WANT_ALL);
+}
+
 bool NormalizeAndValidatePath(std::string* path) {
-  size_t first_non_slash = path->find_first_not_of('/');
+  return NormalizeAndValidatePath(*path, path);
+}
+
+bool NormalizeAndValidatePath(const std::string& path,
+                              std::string* normalized_path) {
+  size_t first_non_slash = path.find_first_not_of('/');
   if (first_non_slash == std::string::npos) {
-    *path = "";
+    *normalized_path = "";
     return false;
   }
 
-  *path = path->substr(first_non_slash);
+  *normalized_path = path.substr(first_non_slash);
   return true;
 }
 
-bool LoadIconsFromDictionary(const base::DictionaryValue* icons_value,
+bool LoadIconsFromDictionary(const base::Value* icons_value,
                              ExtensionIconSet* icons,
                              base::string16* error) {
   DCHECK(icons);
   DCHECK(error);
-  for (base::DictionaryValue::Iterator iterator(*icons_value);
-       !iterator.IsAtEnd(); iterator.Advance()) {
+  for (const auto& entry : icons_value->DictItems()) {
     int size = 0;
-    std::string icon_path;
-    if (!base::StringToInt(iterator.key(), &size) || size <= 0 ||
+    if (!base::StringToInt(entry.first, &size) || size <= 0 ||
         size > extension_misc::EXTENSION_ICON_GIGANTOR * 4) {
       *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidIconKey,
-                                                   iterator.key());
+                                                   entry.first);
       return false;
     }
-    if (!iterator.value().GetAsString(&icon_path) ||
-        !NormalizeAndValidatePath(&icon_path)) {
+    std::string icon_path;
+    if (!entry.second.is_string() ||
+        !NormalizeAndValidatePath(entry.second.GetString(), &icon_path)) {
       *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidIconPath,
-                                                   iterator.key());
+                                                   entry.first);
       return false;
     }
 
diff --git a/extensions/common/manifest_handler_helpers.h b/extensions/common/manifest_handler_helpers.h
index eaa7906..4cf060a 100644
--- a/extensions/common/manifest_handler_helpers.h
+++ b/extensions/common/manifest_handler_helpers.h
@@ -7,26 +7,33 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
 
 class ExtensionIconSet;
 
 namespace base {
-class DictionaryValue;
+class Value;
 }
 
 namespace extensions {
 namespace manifest_handler_helpers {
 
+// Tokenize a dictionary path.
+std::vector<base::StringPiece> TokenizeDictionaryPath(base::StringPiece path);
+
 // Strips leading slashes from the file path. Returns true iff the final path is
-// non empty.
+// not empty.
 bool NormalizeAndValidatePath(std::string* path);
+bool NormalizeAndValidatePath(const std::string& path,
+                              std::string* normalized_path);
 
 // Loads icon paths defined in dictionary |icons_value| into ExtensionIconSet
 // |icons|. |icons_value| is a dictionary value {icon size -> icon path}.
 // Returns success. If load fails, |error| will be set.
-bool LoadIconsFromDictionary(const base::DictionaryValue* icons_value,
+bool LoadIconsFromDictionary(const base::Value* icons_value,
                              ExtensionIconSet* icons,
                              base::string16* error);
 
diff --git a/extensions/common/manifest_handlers/action_handlers_handler.cc b/extensions/common/manifest_handlers/action_handlers_handler.cc
index 22f3439..0aa0b30 100644
--- a/extensions/common/manifest_handlers/action_handlers_handler.cc
+++ b/extensions/common/manifest_handlers/action_handlers_handler.cc
@@ -43,25 +43,33 @@
 ActionHandlersHandler::~ActionHandlersHandler() = default;
 
 bool ActionHandlersHandler::Parse(Extension* extension, base::string16* error) {
-  const base::ListValue* entries = nullptr;
+  const base::Value* entries = nullptr;
   if (!extension->manifest()->GetList(keys::kActionHandlers, &entries)) {
     *error = base::ASCIIToUTF16(errors::kInvalidActionHandlersType);
     return false;
   }
 
   auto info = std::make_unique<ActionHandlersInfo>();
-  for (const base::Value& wrapped_value : *entries) {
+  for (const base::Value& wrapped_value : entries->GetList()) {
     std::string value;
     bool enabled_on_lock_screen = false;
-    const base::DictionaryValue* dictionary_value = nullptr;
-    if (wrapped_value.GetAsDictionary(&dictionary_value)) {
-      if (!dictionary_value->GetString(keys::kActionHandlerActionKey, &value)) {
+    if (wrapped_value.is_dict()) {
+      const base::Value* action_value = wrapped_value.FindKeyOfType(
+          keys::kActionHandlerActionKey, base::Value::Type::STRING);
+      if (!action_value) {
         *error = base::ASCIIToUTF16(errors::kInvalidActionHandlerDictionary);
         return false;
       }
-      dictionary_value->GetBoolean(keys::kActionHandlerEnabledOnLockScreenKey,
-                                   &enabled_on_lock_screen);
-    } else if (!wrapped_value.GetAsString(&value)) {
+      value = action_value->GetString();
+      const base::Value* lock_screen_value = wrapped_value.FindKeyOfType(
+          keys::kActionHandlerEnabledOnLockScreenKey,
+          base::Value::Type::BOOLEAN);
+      if (lock_screen_value) {
+        enabled_on_lock_screen = lock_screen_value->GetBool();
+      }
+    } else if (wrapped_value.is_string()) {
+      value = wrapped_value.GetString();
+    } else {
       *error = base::ASCIIToUTF16(errors::kInvalidActionHandlersType);
       return false;
     }
diff --git a/extensions/common/manifest_handlers/action_handlers_handler_unittest.cc b/extensions/common/manifest_handlers/action_handlers_handler_unittest.cc
index f34ac12a..5196732 100644
--- a/extensions/common/manifest_handlers/action_handlers_handler_unittest.cc
+++ b/extensions/common/manifest_handlers/action_handlers_handler_unittest.cc
@@ -22,8 +22,8 @@
 class ActionHandlersManifestTest : public ManifestTest {
  protected:
   ManifestData CreateManifest(const std::string& action_handlers) {
-    std::unique_ptr<base::DictionaryValue> manifest =
-        base::DictionaryValue::From(base::test::ParseJson(R"json({
+    std::unique_ptr<base::Value> manifest =
+        base::test::ParseJson(R"json({
                                     "name": "test",
                                     "version": "1",
                                     "app": {
@@ -33,8 +33,7 @@
                                     },
                                     "manifest_version": 2,
                                     "action_handlers": )json" +
-                                                          action_handlers +
-                                                          "}"));
+                              action_handlers + "}");
     EXPECT_TRUE(manifest);
     return ManifestData(std::move(manifest), "test");
   }
diff --git a/extensions/common/manifest_handlers/app_isolation_info.cc b/extensions/common/manifest_handlers/app_isolation_info.cc
index b4a91d3..21d0868 100644
--- a/extensions/common/manifest_handlers/app_isolation_info.cc
+++ b/extensions/common/manifest_handlers/app_isolation_info.cc
@@ -61,21 +61,22 @@
   // or is a platform app (which we already handled).
   DCHECK(extension->manifest()->HasPath(keys::kIsolation));
 
-  const base::ListValue* isolation_list = NULL;
+  const base::Value* isolation_list = nullptr;
   if (!extension->manifest()->GetList(keys::kIsolation, &isolation_list)) {
     *error = base::ASCIIToUTF16(manifest_errors::kInvalidIsolation);
     return false;
   }
 
   bool has_isolated_storage = false;
-  for (size_t i = 0; i < isolation_list->GetSize(); ++i) {
-    std::string isolation_string;
-    if (!isolation_list->GetString(i, &isolation_string)) {
+  const base::Value::ListStorage& list_storage = isolation_list->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           manifest_errors::kInvalidIsolationValue, base::NumberToString(i));
       return false;
     }
 
+    const std::string& isolation_string = list_storage[i].GetString();
     // Check for isolated storage.
     if (isolation_string == manifest_values::kIsolatedStorage) {
       has_isolated_storage = true;
diff --git a/extensions/common/manifest_handlers/background_info.cc b/extensions/common/manifest_handlers/background_info.cc
index 0c19f81..eae4adc2 100644
--- a/extensions/common/manifest_handlers/background_info.cc
+++ b/extensions/common/manifest_handlers/background_info.cc
@@ -25,7 +25,6 @@
 #include "ui/base/l10n/l10n_util.h"
 
 using base::ASCIIToUTF16;
-using base::DictionaryValue;
 
 namespace extensions {
 
@@ -138,26 +137,25 @@
 bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension,
                                            const std::string& key,
                                            base::string16* error) {
-  const base::Value* background_scripts_value = NULL;
+  const base::Value* background_scripts_value = nullptr;
   if (!extension->manifest()->Get(key, &background_scripts_value))
     return true;
 
   CHECK(background_scripts_value);
-  if (background_scripts_value->type() != base::Value::Type::LIST) {
+  if (!background_scripts_value->is_list()) {
     *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
     return false;
   }
 
-  const base::ListValue* background_scripts = NULL;
-  background_scripts_value->GetAsList(&background_scripts);
-  for (size_t i = 0; i < background_scripts->GetSize(); ++i) {
-    std::string script;
-    if (!background_scripts->GetString(i, &script)) {
+  const base::Value::ListStorage& background_scripts =
+      background_scripts_value->GetList();
+  for (size_t i = 0; i < background_scripts.size(); ++i) {
+    if (!background_scripts[i].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidBackgroundScript, base::NumberToString(i));
       return false;
     }
-    background_scripts_.push_back(script);
+    background_scripts_.push_back(background_scripts[i].GetString());
   }
 
   return true;
@@ -166,15 +164,15 @@
 bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
                                         const std::string& key,
                                         base::string16* error) {
-  const base::Value* background_page_value = NULL;
+  const base::Value* background_page_value = nullptr;
   if (!extension->manifest()->Get(key, &background_page_value))
     return true;
 
-  std::string background_str;
-  if (!background_page_value->GetAsString(&background_str)) {
+  if (!background_page_value->is_string()) {
     *error = ASCIIToUTF16(errors::kInvalidBackground);
     return false;
   }
+  const std::string& background_str = background_page_value->GetString();
 
   if (extension->is_hosted_app()) {
     background_url_ = GURL(background_str);
diff --git a/extensions/common/manifest_handlers/content_scripts_handler.cc b/extensions/common/manifest_handlers/content_scripts_handler.cc
index 030a7dc..48511e5 100644
--- a/extensions/common/manifest_handlers/content_scripts_handler.cc
+++ b/extensions/common/manifest_handlers/content_scripts_handler.cc
@@ -39,33 +39,33 @@
 
 // Helper method that loads either the include_globs or exclude_globs list
 // from an entry in the content_script lists of the manifest.
-bool LoadGlobsHelper(const base::DictionaryValue* content_script,
+bool LoadGlobsHelper(const base::Value& content_script,
                      int content_script_index,
                      const char* globs_property_name,
                      base::string16* error,
                      void (UserScript::*add_method)(const std::string& glob),
                      UserScript* instance) {
-  if (!content_script->HasKey(globs_property_name))
+  const base::Value* list = content_script.FindKey(globs_property_name);
+  if (!list)
     return true;  // they are optional
 
-  const base::ListValue* list = NULL;
-  if (!content_script->GetList(globs_property_name, &list)) {
+  if (!list->is_list()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidGlobList, base::IntToString(content_script_index),
         globs_property_name);
     return false;
   }
 
-  for (size_t i = 0; i < list->GetSize(); ++i) {
-    std::string glob;
-    if (!list->GetString(i, &glob)) {
+  const base::Value::ListStorage& list_storage = list->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidGlob, base::IntToString(content_script_index),
           globs_property_name, base::NumberToString(i));
       return false;
     }
 
-    (instance->*add_method)(glob);
+    (instance->*add_method)(list_storage[i].GetString());
   }
 
   return true;
@@ -74,20 +74,21 @@
 // Helper method that loads a UserScript object from a dictionary in the
 // content_script list of the manifest.
 std::unique_ptr<UserScript> LoadUserScriptFromDictionary(
-    const base::DictionaryValue* content_script,
+    const base::Value& content_script,
     int definition_index,
     Extension* extension,
     base::string16* error) {
   std::unique_ptr<UserScript> result(new UserScript());
   // run_at
-  if (content_script->HasKey(keys::kRunAt)) {
-    std::string run_location;
-    if (!content_script->GetString(keys::kRunAt, &run_location)) {
+  const base::Value* run_at = content_script.FindKey(keys::kRunAt);
+  if (run_at != nullptr) {
+    if (!run_at->is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidRunAt, base::IntToString(definition_index));
       return nullptr;
     }
 
+    const std::string& run_location = run_at->GetString();
     if (run_location == values::kRunAtDocumentStart) {
       result->set_run_location(UserScript::DOCUMENT_START);
     } else if (run_location == values::kRunAtDocumentEnd) {
@@ -102,37 +103,39 @@
   }
 
   // all frames
-  if (content_script->HasKey(keys::kAllFrames)) {
-    bool all_frames = false;
-    if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
+  const base::Value* all_frames = content_script.FindKey(keys::kAllFrames);
+  if (all_frames != nullptr) {
+    if (!all_frames->is_bool()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidAllFrames, base::IntToString(definition_index));
       return nullptr;
     }
-    result->set_match_all_frames(all_frames);
+    result->set_match_all_frames(all_frames->GetBool());
   }
 
   // match about blank
-  if (content_script->HasKey(keys::kMatchAboutBlank)) {
-    bool match_about_blank = false;
-    if (!content_script->GetBoolean(keys::kMatchAboutBlank,
-                                    &match_about_blank)) {
+  const base::Value* match_about_blank =
+      content_script.FindKey(keys::kMatchAboutBlank);
+  if (match_about_blank != nullptr) {
+    if (!match_about_blank->is_bool()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidMatchAboutBlank, base::IntToString(definition_index));
       return nullptr;
     }
-    result->set_match_about_blank(match_about_blank);
+    result->set_match_about_blank(match_about_blank->GetBool());
   }
 
   // matches (required)
-  const base::ListValue* matches = NULL;
-  if (!content_script->GetList(keys::kMatches, &matches)) {
+  const base::Value* matches =
+      content_script.FindKeyOfType(keys::kMatches, base::Value::Type::LIST);
+  if (matches == nullptr) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidMatches, base::IntToString(definition_index));
     return nullptr;
   }
 
-  if (matches->GetSize() == 0) {
+  const base::Value::ListStorage& list_storage = matches->GetList();
+  if (list_storage.empty()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidMatchCount, base::IntToString(definition_index));
     return nullptr;
@@ -147,14 +150,14 @@
   const bool all_urls_includes_chrome_urls =
       PermissionsData::AllUrlsIncludesChromeUrls(extension->id());
 
-  for (size_t j = 0; j < matches->GetSize(); ++j) {
-    std::string match_str;
-    if (!matches->GetString(j, &match_str)) {
+  for (size_t j = 0; j < list_storage.size(); ++j) {
+    if (!list_storage[j].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidMatch, base::IntToString(definition_index),
           base::NumberToString(j), errors::kExpectString);
       return nullptr;
     }
+    const std::string& match_str = list_storage[j].GetString();
 
     URLPattern pattern(valid_schemes);
 
@@ -192,23 +195,24 @@
   }
 
   // exclude_matches
-  if (content_script->HasKey(keys::kExcludeMatches)) {  // optional
-    const base::ListValue* exclude_matches = NULL;
-    if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
+  const base::Value* exclude_matches =
+      content_script.FindKey(keys::kExcludeMatches);
+  if (exclude_matches != nullptr) {  // optional
+    if (!exclude_matches->is_list()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidExcludeMatches, base::IntToString(definition_index));
       return nullptr;
     }
+    const base::Value::ListStorage& list_storage = exclude_matches->GetList();
 
-    for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
-      std::string match_str;
-      if (!exclude_matches->GetString(j, &match_str)) {
+    for (size_t j = 0; j < list_storage.size(); ++j) {
+      if (!list_storage[j].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidExcludeMatch, base::IntToString(definition_index),
             base::NumberToString(j), errors::kExpectString);
         return nullptr;
       }
-
+      const std::string& match_str = list_storage[j].GetString();
       URLPattern pattern(valid_schemes);
 
       URLPattern::ParseResult parse_result = pattern.Parse(match_str);
@@ -236,41 +240,40 @@
   }
 
   // js and css keys
-  const base::ListValue* js = NULL;
-  if (content_script->HasKey(keys::kJs) &&
-      !content_script->GetList(keys::kJs, &js)) {
+  const base::Value* js = content_script.FindKey(keys::kJs);
+  if (js != nullptr && !js->is_list()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidJsList, base::IntToString(definition_index));
     return nullptr;
   }
 
-  const base::ListValue* css = NULL;
-  if (content_script->HasKey(keys::kCss) &&
-      !content_script->GetList(keys::kCss, &css)) {
+  const base::Value* css = content_script.FindKey(keys::kCss);
+  if (css != nullptr && !css->is_list()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidCssList, base::IntToString(definition_index));
     return nullptr;
   }
 
   // The manifest needs to have at least one js or css user script definition.
-  if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
+  if (((js ? js->GetList().size() : 0) + (css ? css->GetList().size() : 0)) ==
+      0) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kMissingFile, base::IntToString(definition_index));
     return nullptr;
   }
 
   if (js) {
-    result->js_scripts().reserve(js->GetSize());
-    for (size_t script_index = 0; script_index < js->GetSize();
+    const base::Value::ListStorage& js_list = js->GetList();
+    result->js_scripts().reserve(js_list.size());
+    for (size_t script_index = 0; script_index < js_list.size();
          ++script_index) {
-      const base::Value* value;
-      std::string relative;
-      if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
+      if (!js_list[script_index].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidJs, base::IntToString(definition_index),
             base::NumberToString(script_index));
         return nullptr;
       }
+      const std::string& relative = js_list[script_index].GetString();
       GURL url = extension->GetResourceURL(relative);
       ExtensionResource resource = extension->GetResource(relative);
       result->js_scripts().push_back(std::make_unique<UserScript::File>(
@@ -279,17 +282,17 @@
   }
 
   if (css) {
-    result->css_scripts().reserve(css->GetSize());
-    for (size_t script_index = 0; script_index < css->GetSize();
+    const base::Value::ListStorage& css_list = css->GetList();
+    result->css_scripts().reserve(css_list.size());
+    for (size_t script_index = 0; script_index < css_list.size();
          ++script_index) {
-      const base::Value* value;
-      std::string relative;
-      if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
+      if (!css_list[script_index].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidCss, base::IntToString(definition_index),
             base::NumberToString(script_index));
         return nullptr;
       }
+      const std::string& relative = css_list[script_index].GetString();
       GURL url = extension->GetResourceURL(relative);
       ExtensionResource resource = extension->GetResource(relative);
       result->css_scripts().push_back(std::make_unique<UserScript::File>(
@@ -378,22 +381,22 @@
 bool ContentScriptsHandler::Parse(Extension* extension, base::string16* error) {
   std::unique_ptr<ContentScriptsInfo> content_scripts_info(
       new ContentScriptsInfo);
-  const base::ListValue* scripts_list = NULL;
+  const base::Value* scripts_list = nullptr;
   if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) {
     *error = base::ASCIIToUTF16(errors::kInvalidContentScriptsList);
     return false;
   }
 
-  for (size_t i = 0; i < scripts_list->GetSize(); ++i) {
-    const base::DictionaryValue* script_dict = NULL;
-    if (!scripts_list->GetDictionary(i, &script_dict)) {
+  const base::Value::ListStorage& list_storage = scripts_list->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_dict()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidContentScript, base::NumberToString(i));
       return false;
     }
 
     std::unique_ptr<UserScript> user_script =
-        LoadUserScriptFromDictionary(script_dict, i, extension, error);
+        LoadUserScriptFromDictionary(list_storage[i], i, extension, error);
     if (!user_script)
       return false;  // Failed to parse script context definition.
 
diff --git a/extensions/common/manifest_handlers/file_handler_info.cc b/extensions/common/manifest_handlers/file_handler_info.cc
index e37b8f2b..848d17b 100644
--- a/extensions/common/manifest_handlers/file_handler_info.cc
+++ b/extensions/common/manifest_handlers/file_handler_info.cc
@@ -67,7 +67,7 @@
 }
 
 bool LoadFileHandler(const std::string& handler_id,
-                     const base::DictionaryValue& handler_info,
+                     const base::Value& handler_info,
                      FileHandlersInfo* file_handlers,
                      base::string16* error,
                      std::vector<InstallWarning>* install_warnings) {
@@ -76,42 +76,50 @@
 
   handler.id = handler_id;
 
-  const base::ListValue* mime_types = NULL;
-  if (handler_info.HasKey(keys::kFileHandlerTypes) &&
-      !handler_info.GetList(keys::kFileHandlerTypes, &mime_types)) {
+  const base::Value* mime_types = handler_info.FindKey(keys::kFileHandlerTypes);
+  if (mime_types != nullptr && !mime_types->is_list()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidFileHandlerType, handler_id);
     return false;
   }
 
-  const base::ListValue* file_extensions = NULL;
-  if (handler_info.HasKey(keys::kFileHandlerExtensions) &&
-      !handler_info.GetList(keys::kFileHandlerExtensions, &file_extensions)) {
+  const base::Value* file_extensions =
+      handler_info.FindKey(keys::kFileHandlerExtensions);
+  if (file_extensions != nullptr && !file_extensions->is_list()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidFileHandlerExtension, handler_id);
     return false;
   }
 
   handler.include_directories = false;
-  if (handler_info.HasKey(keys::kFileHandlerIncludeDirectories) &&
-      !handler_info.GetBoolean(keys::kFileHandlerIncludeDirectories,
-                               &handler.include_directories)) {
-    *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidFileHandlerIncludeDirectories, handler_id);
-    return false;
+  const base::Value* include_directories =
+      handler_info.FindKey(keys::kFileHandlerIncludeDirectories);
+  if (include_directories != nullptr) {
+    if (include_directories->is_bool()) {
+      handler.include_directories = include_directories->GetBool();
+    } else {
+      *error = ErrorUtils::FormatErrorMessageUTF16(
+          errors::kInvalidFileHandlerIncludeDirectories, handler_id);
+      return false;
+    }
   }
 
   handler.verb = file_handler_verbs::kOpenWith;
-  if (handler_info.HasKey(keys::kFileHandlerVerb) &&
-      (!handler_info.GetString(keys::kFileHandlerVerb, &handler.verb) ||
-       !IsSupportedVerb(handler.verb))) {
-    *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidFileHandlerVerb, handler_id);
-    return false;
+  const base::Value* file_handler =
+      handler_info.FindKey(keys::kFileHandlerVerb);
+  if (file_handler != nullptr) {
+    if (file_handler->is_string() &&
+        IsSupportedVerb(file_handler->GetString())) {
+      handler.verb = file_handler->GetString();
+    } else {
+      *error = ErrorUtils::FormatErrorMessageUTF16(
+          errors::kInvalidFileHandlerVerb, handler_id);
+      return false;
+    }
   }
 
-  if ((!mime_types || mime_types->empty()) &&
-      (!file_extensions || file_extensions->empty()) &&
+  if ((!mime_types || mime_types->GetList().empty()) &&
+      (!file_extensions || file_extensions->GetList().empty()) &&
       !handler.include_directories) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidFileHandlerNoTypeOrExtension,
@@ -120,44 +128,42 @@
   }
 
   if (mime_types) {
-    std::string type;
-    for (size_t i = 0; i < mime_types->GetSize(); ++i) {
-      if (!mime_types->GetString(i, &type)) {
+    const base::Value::ListStorage& list_storage = mime_types->GetList();
+    for (size_t i = 0; i < list_storage.size(); ++i) {
+      if (!list_storage[i].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidFileHandlerTypeElement, handler_id,
             base::NumberToString(i));
         return false;
       }
-      handler.types.insert(type);
+      handler.types.insert(list_storage[i].GetString());
     }
   }
 
   if (file_extensions) {
-    std::string file_extension;
-    for (size_t i = 0; i < file_extensions->GetSize(); ++i) {
-      if (!file_extensions->GetString(i, &file_extension)) {
+    const base::Value::ListStorage& list_storage = file_extensions->GetList();
+    for (size_t i = 0; i < list_storage.size(); ++i) {
+      if (!list_storage[i].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidFileHandlerExtensionElement, handler_id,
             base::NumberToString(i));
         return false;
       }
-      handler.extensions.insert(file_extension);
+      handler.extensions.insert(list_storage[i].GetString());
     }
   }
 
   file_handlers->push_back(handler);
 
   // Check for unknown keys.
-  for (base::DictionaryValue::Iterator it(handler_info); !it.IsAtEnd();
-       it.Advance()) {
-    if (it.key() != keys::kFileHandlerExtensions &&
-        it.key() != keys::kFileHandlerTypes &&
-        it.key() != keys::kFileHandlerIncludeDirectories &&
-        it.key() != keys::kFileHandlerVerb) {
-      install_warnings->push_back(
-          InstallWarning(base::StringPrintf(kNotRecognized, it.key().c_str()),
-                         keys::kFileHandlers,
-                         it.key()));
+  for (const auto& entry : handler_info.DictItems()) {
+    if (entry.first != keys::kFileHandlerExtensions &&
+        entry.first != keys::kFileHandlerTypes &&
+        entry.first != keys::kFileHandlerIncludeDirectories &&
+        entry.first != keys::kFileHandlerVerb) {
+      install_warnings->push_back(InstallWarning(
+          base::StringPrintf(kNotRecognized, entry.first.c_str()),
+          keys::kFileHandlers, entry.first));
     }
   }
 
@@ -166,7 +172,7 @@
 
 bool FileHandlersParser::Parse(Extension* extension, base::string16* error) {
   std::unique_ptr<FileHandlers> info(new FileHandlers);
-  const base::DictionaryValue* all_handlers = NULL;
+  const base::Value* all_handlers = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kFileHandlers,
                                             &all_handlers)) {
     *error = base::ASCIIToUTF16(errors::kInvalidFileHandlers);
@@ -174,21 +180,15 @@
   }
 
   std::vector<InstallWarning> install_warnings;
-  for (base::DictionaryValue::Iterator iter(*all_handlers);
-       !iter.IsAtEnd();
-       iter.Advance()) {
-    const base::DictionaryValue* handler = NULL;
-    if (iter.value().GetAsDictionary(&handler)) {
-      if (!LoadFileHandler(iter.key(),
-                           *handler,
-                           &info->file_handlers,
-                           error,
-                           &install_warnings))
-        return false;
-    } else {
+  for (const auto& entry : all_handlers->DictItems()) {
+    if (!entry.second.is_dict()) {
       *error = base::ASCIIToUTF16(errors::kInvalidFileHandlers);
       return false;
     }
+    if (!LoadFileHandler(entry.first, entry.second, &info->file_handlers, error,
+                         &install_warnings)) {
+      return false;
+    }
   }
 
   int filter_count = 0;
diff --git a/extensions/common/manifest_handlers/icons_handler.cc b/extensions/common/manifest_handlers/icons_handler.cc
index 657b41a1..b031ff2 100644
--- a/extensions/common/manifest_handlers/icons_handler.cc
+++ b/extensions/common/manifest_handlers/icons_handler.cc
@@ -60,7 +60,7 @@
 
 bool IconsHandler::Parse(Extension* extension, base::string16* error) {
   std::unique_ptr<IconsInfo> icons_info(new IconsInfo);
-  const base::DictionaryValue* icons_dict = NULL;
+  const base::Value* icons_dict = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kIcons, &icons_dict)) {
     *error = base::ASCIIToUTF16(manifest_errors::kInvalidIcons);
     return false;
diff --git a/extensions/common/manifest_handlers/icons_handler_unittest.cc b/extensions/common/manifest_handlers/icons_handler_unittest.cc
index 7f31f2f..494bb99 100644
--- a/extensions/common/manifest_handlers/icons_handler_unittest.cc
+++ b/extensions/common/manifest_handlers/icons_handler_unittest.cc
@@ -14,19 +14,18 @@
   ProductIconManifestTest() {}
 
  protected:
-  std::unique_ptr<base::DictionaryValue> CreateManifest(
-      const std::string& extra_icons) {
-    std::unique_ptr<base::DictionaryValue> manifest =
-        base::DictionaryValue::From(
-            base::test::ParseJson("{ \n"
-                                  "  \"name\": \"test\", \n"
-                                  "  \"version\": \"0.1\", \n"
-                                  "  \"manifest_version\": 2, \n"
-                                  "  \"icons\": { \n" +
-                                  extra_icons + "    \"16\": \"icon1.png\", \n"
-                                                "    \"32\": \"icon2.png\" \n"
-                                                "  } \n"
-                                                "} \n"));
+  std::unique_ptr<base::Value> CreateManifest(const std::string& extra_icons) {
+    std::unique_ptr<base::Value> manifest = base::test::ParseJson(
+        "{ \n"
+        "  \"name\": \"test\", \n"
+        "  \"version\": \"0.1\", \n"
+        "  \"manifest_version\": 2, \n"
+        "  \"icons\": { \n" +
+        extra_icons +
+        "    \"16\": \"icon1.png\", \n"
+        "    \"32\": \"icon2.png\" \n"
+        "  } \n"
+        "} \n");
     EXPECT_TRUE(manifest);
     return manifest;
   }
@@ -38,28 +37,28 @@
 TEST_F(ProductIconManifestTest, Sizes) {
   // Too big.
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest("\"100000\": \"icon3.png\", \n");
     ManifestData manifest(std::move(ext_manifest), "test");
     LoadAndExpectError(manifest, "Invalid key in icons: \"100000\".");
   }
   // Too small.
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest("\"0\": \"icon3.png\", \n");
     ManifestData manifest(std::move(ext_manifest), "test");
     LoadAndExpectError(manifest, "Invalid key in icons: \"0\".");
   }
   // NaN.
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest("\"sixteen\": \"icon3.png\", \n");
     ManifestData manifest(std::move(ext_manifest), "test");
     LoadAndExpectError(manifest, "Invalid key in icons: \"sixteen\".");
   }
   // Just right.
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest("\"512\": \"icon3.png\", \n");
     ManifestData manifest(std::move(ext_manifest), "test");
     scoped_refptr<extensions::Extension> extension =
diff --git a/extensions/common/manifest_handlers/kiosk_mode_info.cc b/extensions/common/manifest_handlers/kiosk_mode_info.cc
index e02561a..3d7b73b 100644
--- a/extensions/common/manifest_handlers/kiosk_mode_info.cc
+++ b/extensions/common/manifest_handlers/kiosk_mode_info.cc
@@ -142,17 +142,16 @@
   std::set<std::string> secondary_app_ids;
   if (manifest->HasKey(keys::kKioskSecondaryApps)) {
     const base::Value* secondary_apps_value = nullptr;
-    const base::ListValue* list = nullptr;
-    if (!manifest->Get(keys::kKioskSecondaryApps, &secondary_apps_value) ||
-        !secondary_apps_value->GetAsList(&list)) {
+    if (!manifest->GetList(keys::kKioskSecondaryApps, &secondary_apps_value)) {
       *error = base::ASCIIToUTF16(manifest_errors::kInvalidKioskSecondaryApps);
       return false;
     }
 
+    const base::Value::ListStorage& list = secondary_apps_value->GetList();
     const bool allow_enabled_on_launch =
         AllowSecondaryAppEnabledOnLaunch(extension);
 
-    for (const auto& value : *list) {
+    for (const auto& value : list) {
       std::unique_ptr<KioskSecondaryAppsType> app =
           KioskSecondaryAppsType::FromValue(value, error);
       if (!app) {
diff --git a/extensions/common/manifest_handlers/mime_types_handler.cc b/extensions/common/manifest_handlers/mime_types_handler.cc
index d3ed85ef..f4874a5 100644
--- a/extensions/common/manifest_handlers/mime_types_handler.cc
+++ b/extensions/common/manifest_handlers/mime_types_handler.cc
@@ -113,22 +113,20 @@
 
 bool MimeTypesHandlerParser::Parse(extensions::Extension* extension,
                                    base::string16* error) {
-  const base::ListValue* mime_types_value = NULL;
-  if (!extension->manifest()->GetList(keys::kMIMETypes,
-                                      &mime_types_value)) {
+  const base::Value* mime_types_value = nullptr;
+  if (!extension->manifest()->GetList(keys::kMIMETypes, &mime_types_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidMimeTypesHandler);
     return false;
   }
 
-  std::unique_ptr<MimeTypesHandlerInfo> info(new MimeTypesHandlerInfo);
+  auto info = std::make_unique<MimeTypesHandlerInfo>();
   info->handler_.set_extension_id(extension->id());
-  for (size_t i = 0; i < mime_types_value->GetSize(); ++i) {
-    std::string filter;
-    if (!mime_types_value->GetString(i, &filter)) {
+  for (const auto& entry : mime_types_value->GetList()) {
+    if (!entry.is_string()) {
       *error = base::ASCIIToUTF16(errors::kInvalidMIMETypes);
       return false;
     }
-    info->handler_.AddMIMEType(filter);
+    info->handler_.AddMIMEType(entry.GetString());
   }
 
   std::string mime_types_handler;
diff --git a/extensions/common/manifest_handlers/nacl_modules_handler.cc b/extensions/common/manifest_handlers/nacl_modules_handler.cc
index 1261c3a..fe86cf19 100644
--- a/extensions/common/manifest_handlers/nacl_modules_handler.cc
+++ b/extensions/common/manifest_handlers/nacl_modules_handler.cc
@@ -44,7 +44,7 @@
 }
 
 bool NaClModulesHandler::Parse(Extension* extension, base::string16* error) {
-  const base::ListValue* list_value = NULL;
+  const base::Value* list_value = nullptr;
   if (!extension->manifest()->GetList(keys::kNaClModules, &list_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidNaClModules);
     return false;
@@ -52,24 +52,26 @@
 
   std::unique_ptr<NaClModuleData> nacl_module_data(new NaClModuleData);
 
-  for (size_t i = 0; i < list_value->GetSize(); ++i) {
-    const base::DictionaryValue* module_value = NULL;
-    if (!list_value->GetDictionary(i, &module_value)) {
+  const base::Value::ListStorage& list_storage = list_value->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_dict()) {
       *error = base::ASCIIToUTF16(errors::kInvalidNaClModules);
       return false;
     }
 
     // Get nacl_modules[i].path.
-    std::string path_str;
-    if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) {
+    const base::Value* path_str = list_storage[i].FindKeyOfType(
+        keys::kNaClModulesPath, base::Value::Type::STRING);
+    if (path_str == nullptr) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidNaClModulesPath, base::NumberToString(i));
       return false;
     }
 
     // Get nacl_modules[i].mime_type.
-    std::string mime_type;
-    if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) {
+    const base::Value* mime_type = list_storage[i].FindKeyOfType(
+        keys::kNaClModulesMIMEType, base::Value::Type::STRING);
+    if (mime_type == nullptr) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidNaClModulesMIMEType, base::NumberToString(i));
       return false;
@@ -77,8 +79,8 @@
 
     nacl_module_data->nacl_modules_.push_back(NaClModuleInfo());
     nacl_module_data->nacl_modules_.back().url =
-        extension->GetResourceURL(path_str);
-    nacl_module_data->nacl_modules_.back().mime_type = mime_type;
+        extension->GetResourceURL(path_str->GetString());
+    nacl_module_data->nacl_modules_.back().mime_type = mime_type->GetString();
   }
 
   extension->SetManifestData(keys::kNaClModules, std::move(nacl_module_data));
diff --git a/extensions/common/manifest_handlers/oauth2_manifest_handler.cc b/extensions/common/manifest_handlers/oauth2_manifest_handler.cc
index b1880ac..e69a8da 100644
--- a/extensions/common/manifest_handlers/oauth2_manifest_handler.cc
+++ b/extensions/common/manifest_handlers/oauth2_manifest_handler.cc
@@ -50,7 +50,7 @@
 bool OAuth2ManifestHandler::Parse(Extension* extension,
                                   base::string16* error) {
   std::unique_ptr<OAuth2Info> info(new OAuth2Info);
-  const base::DictionaryValue* dict = NULL;
+  const base::Value* dict = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kOAuth2, &dict)) {
     *error = base::ASCIIToUTF16(errors::kInvalidOAuth2ClientId);
     return false;
@@ -60,34 +60,41 @@
   // oauth2.auto_approve based on whitelist, and if it is present.
   // GetBoolean reads the value of auto_approve directly from dict to prevent
   // duplicate checking.
-  if (extension->manifest()->HasPath(keys::kOAuth2AutoApprove) &&
-      !dict->GetBoolean(kAutoApprove, &info->auto_approve)) {
-    *error = base::ASCIIToUTF16(errors::kInvalidOAuth2AutoApprove);
-    return false;
+  if (extension->manifest()->HasPath(keys::kOAuth2AutoApprove)) {
+    const base::Value* auto_approve =
+        dict->FindKeyOfType(kAutoApprove, base::Value::Type::BOOLEAN);
+    if (auto_approve == nullptr) {
+      *error = base::ASCIIToUTF16(errors::kInvalidOAuth2AutoApprove);
+      return false;
+    }
+    info->auto_approve = auto_approve->GetBool();
   }
 
   // Component apps using auto_approve may use Chrome's client ID by
   // omitting the field.
-  if ((!dict->GetString(kClientId, &info->client_id) ||
-       info->client_id.empty()) &&
+  const base::Value* client_id =
+      dict->FindKeyOfType(kClientId, base::Value::Type::STRING);
+  if (client_id != nullptr)
+    info->client_id = client_id->GetString();
+
+  if (info->client_id.empty() &&
       (extension->location() != Manifest::COMPONENT || !info->auto_approve)) {
     *error = base::ASCIIToUTF16(errors::kInvalidOAuth2ClientId);
     return false;
   }
-
-  const base::ListValue* list = NULL;
-  if (!dict->GetList(kScopes, &list)) {
+  const base::Value* list =
+      dict->FindKeyOfType(kScopes, base::Value::Type::LIST);
+  if (list == nullptr) {
     *error = base::ASCIIToUTF16(errors::kInvalidOAuth2Scopes);
     return false;
   }
 
-  for (size_t i = 0; i < list->GetSize(); ++i) {
-    std::string scope;
-    if (!list->GetString(i, &scope)) {
+  for (const auto& entry : list->GetList()) {
+    if (!entry.is_string()) {
       *error = base::ASCIIToUTF16(errors::kInvalidOAuth2Scopes);
       return false;
     }
-    info->scopes.push_back(scope);
+    info->scopes.push_back(entry.GetString());
   }
 
   extension->SetManifestData(keys::kOAuth2, std::move(info));
diff --git a/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc b/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
index c0c9215..1034171 100644
--- a/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
+++ b/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handler_helpers.h"
 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
 #include "extensions/common/manifest_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,6 +19,8 @@
 
 namespace {
 
+using manifest_handler_helpers::TokenizeDictionaryPath;
+
 // Produces extension ID = "mdbihdcgjmagbcapkhhkjbbdlkflmbfo".
 const char kExtensionKey[] =
     "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCV9PlZjcTIXfnlB3HXo50OlM/CnIq0y7jm"
@@ -43,70 +46,81 @@
     CLIENT_ID_EMPTY
   };
 
-  std::unique_ptr<base::DictionaryValue> CreateManifest(
-      AutoApproveValue auto_approve,
-      bool extension_id_whitelisted,
-      ClientIdValue client_id) {
-    std::unique_ptr<base::DictionaryValue> manifest =
-        base::DictionaryValue::From(
-            base::test::ParseJson("{ \n"
-                                  "  \"name\": \"test\", \n"
-                                  "  \"version\": \"0.1\", \n"
-                                  "  \"manifest_version\": 2, \n"
-                                  "  \"oauth2\": { \n"
-                                  "    \"scopes\": [ \"scope1\" ], \n"
-                                  "  }, \n"
-                                  "} \n"));
+  std::unique_ptr<base::Value> CreateManifest(AutoApproveValue auto_approve,
+                                              bool extension_id_whitelisted,
+                                              ClientIdValue client_id) {
+    std::unique_ptr<base::Value> manifest = base::test::ParseJson(
+        "{ \n"
+        "  \"name\": \"test\", \n"
+        "  \"version\": \"0.1\", \n"
+        "  \"manifest_version\": 2, \n"
+        "  \"oauth2\": { \n"
+        "    \"scopes\": [ \"scope1\" ], \n"
+        "  }, \n"
+        "} \n");
     EXPECT_TRUE(manifest);
+    EXPECT_TRUE(manifest->is_dict());
     switch (auto_approve) {
       case AUTO_APPROVE_NOT_SET:
         break;
       case AUTO_APPROVE_FALSE:
-        manifest->SetBoolean(keys::kOAuth2AutoApprove, false);
+        manifest->SetPath(TokenizeDictionaryPath(keys::kOAuth2AutoApprove),
+                          base::Value(false));
         break;
       case AUTO_APPROVE_TRUE:
-        manifest->SetBoolean(keys::kOAuth2AutoApprove, true);
+        manifest->SetPath(TokenizeDictionaryPath(keys::kOAuth2AutoApprove),
+                          base::Value(true));
         break;
       case AUTO_APPROVE_INVALID:
-        manifest->SetString(keys::kOAuth2AutoApprove, "incorrect value");
+        manifest->SetPath(TokenizeDictionaryPath(keys::kOAuth2AutoApprove),
+                          base::Value("incorrect value"));
         break;
     }
     switch (client_id) {
       case CLIENT_ID_DEFAULT:
-        manifest->SetString(keys::kOAuth2ClientId, "client1");
+        manifest->SetPath(TokenizeDictionaryPath(keys::kOAuth2ClientId),
+                          base::Value("client1"));
         break;
       case CLIENT_ID_NOT_SET:
         break;
       case CLIENT_ID_EMPTY:
-        manifest->SetString(keys::kOAuth2ClientId, "");
+        manifest->SetPath(TokenizeDictionaryPath(keys::kOAuth2ClientId),
+                          base::Value(""));
     }
     if (extension_id_whitelisted)
-      manifest->SetString(keys::kKey, kExtensionKey);
+      manifest->SetPath(TokenizeDictionaryPath(keys::kKey),
+                        base::Value(kExtensionKey));
     return manifest;
   }
-
 };
 
 TEST_F(OAuth2ManifestTest, OAuth2SectionParsing) {
-  base::DictionaryValue base_manifest;
+  base::Value base_manifest(base::Value::Type::DICTIONARY);
 
-  base_manifest.SetString(keys::kName, "test");
-  base_manifest.SetString(keys::kVersion, "0.1");
-  base_manifest.SetInteger(keys::kManifestVersion, 2);
-  base_manifest.SetString(keys::kOAuth2ClientId, "client1");
-  auto scopes = std::make_unique<base::ListValue>();
-  scopes->AppendString("scope1");
-  scopes->AppendString("scope2");
-  base_manifest.Set(keys::kOAuth2Scopes, std::move(scopes));
+  base_manifest.SetPath(TokenizeDictionaryPath(keys::kName),
+                        base::Value("test"));
+  base_manifest.SetPath(TokenizeDictionaryPath(keys::kVersion),
+                        base::Value("0.1"));
+  base_manifest.SetPath(TokenizeDictionaryPath(keys::kManifestVersion),
+                        base::Value(2));
+  base_manifest.SetPath(TokenizeDictionaryPath(keys::kOAuth2ClientId),
+                        base::Value("client1"));
+  base::Value scopes(base::Value::Type::LIST);
+  scopes.GetList().push_back(base::Value("scope1"));
+  scopes.GetList().push_back(base::Value("scope2"));
+  base_manifest.SetPath(TokenizeDictionaryPath(keys::kOAuth2Scopes),
+                        std::move(scopes));
 
   // OAuth2 section should be parsed for an extension.
   {
-    base::DictionaryValue ext_manifest;
+    base::Value ext_manifest(base::Value::Type::DICTIONARY);
     // Lack of "app" section representa an extension. So the base manifest
     // itself represents an extension.
     ext_manifest.MergeDictionary(&base_manifest);
-    ext_manifest.SetString(keys::kKey, kExtensionKey);
-    ext_manifest.SetBoolean(keys::kOAuth2AutoApprove, true);
+    ext_manifest.SetPath(TokenizeDictionaryPath(keys::kKey),
+                         base::Value(kExtensionKey));
+    ext_manifest.SetPath(TokenizeDictionaryPath(keys::kOAuth2AutoApprove),
+                         base::Value(true));
 
     ManifestData manifest(&ext_manifest, "test");
     scoped_refptr<extensions::Extension> extension =
@@ -121,8 +135,9 @@
 
   // OAuth2 section should be parsed for a packaged app.
   {
-    base::DictionaryValue app_manifest;
-    app_manifest.SetString(keys::kLaunchLocalPath, "launch.html");
+    base::Value app_manifest(base::Value::Type::DICTIONARY);
+    app_manifest.SetPath(TokenizeDictionaryPath(keys::kLaunchLocalPath),
+                         base::Value("launch.html"));
     app_manifest.MergeDictionary(&base_manifest);
 
     ManifestData manifest(&app_manifest, "test");
@@ -138,8 +153,9 @@
 
   // OAuth2 section should NOT be parsed for a hosted app.
   {
-    base::DictionaryValue app_manifest;
-    app_manifest.SetString(keys::kLaunchWebURL, "http://www.google.com");
+    base::Value app_manifest(base::Value::Type::DICTIONARY);
+    app_manifest.SetPath(TokenizeDictionaryPath(keys::kLaunchWebURL),
+                         base::Value("http://www.google.com"));
     app_manifest.MergeDictionary(&base_manifest);
 
     ManifestData manifest(&app_manifest, "test");
@@ -158,7 +174,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionNotOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -168,7 +184,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionNotOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_FALSE, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -181,7 +197,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionNotOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_TRUE, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -194,7 +210,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionNotOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_INVALID, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -207,7 +223,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_NOT_SET, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -217,7 +233,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_FALSE, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -227,7 +243,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_TRUE, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
@@ -237,7 +253,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionOnWhitelist) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_INVALID, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   std::string error;
@@ -250,7 +266,7 @@
 
 TEST_F(OAuth2ManifestTest, InvalidClientId) {
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_NOT_SET);
     ManifestData manifest(std::move(ext_manifest), "test");
     std::string error;
@@ -258,7 +274,7 @@
   }
 
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_EMPTY);
     ManifestData manifest(std::move(ext_manifest), "test");
     std::string error;
@@ -269,7 +285,7 @@
 TEST_F(OAuth2ManifestTest, ComponentInvalidClientId) {
   // Component Apps without auto_approve must include a client ID.
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_NOT_SET);
     ManifestData manifest(std::move(ext_manifest), "test");
     std::string error;
@@ -279,7 +295,7 @@
   }
 
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_EMPTY);
     ManifestData manifest(std::move(ext_manifest), "test");
     std::string error;
@@ -291,7 +307,7 @@
 
 TEST_F(OAuth2ManifestTest, ComponentWithChromeClientId) {
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_TRUE, true, CLIENT_ID_NOT_SET);
     ManifestData manifest(std::move(ext_manifest), "test");
     scoped_refptr<extensions::Extension> extension =
@@ -300,7 +316,7 @@
   }
 
   {
-    std::unique_ptr<base::DictionaryValue> ext_manifest =
+    std::unique_ptr<base::Value> ext_manifest =
         CreateManifest(AUTO_APPROVE_TRUE, true, CLIENT_ID_EMPTY);
     ManifestData manifest(std::move(ext_manifest), "test");
     scoped_refptr<extensions::Extension> extension =
@@ -310,7 +326,7 @@
 }
 
 TEST_F(OAuth2ManifestTest, ComponentWithStandardClientId) {
-  std::unique_ptr<base::DictionaryValue> ext_manifest =
+  std::unique_ptr<base::Value> ext_manifest =
       CreateManifest(AUTO_APPROVE_TRUE, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
   scoped_refptr<extensions::Extension> extension =
diff --git a/extensions/common/manifest_handlers/options_page_info.cc b/extensions/common/manifest_handlers/options_page_info.cc
index 87ab51d..c8cddb38 100644
--- a/extensions/common/manifest_handlers/options_page_info.cc
+++ b/extensions/common/manifest_handlers/options_page_info.cc
@@ -19,7 +19,6 @@
 #include "ui/base/l10n/l10n_util.h"
 
 using base::ASCIIToUTF16;
-using base::DictionaryValue;
 
 namespace extensions {
 
@@ -161,12 +160,12 @@
                          keys::kOptionsPage,
                          error,
                          &options_page)) {
-      return std::unique_ptr<OptionsPageInfo>();
+      return nullptr;
     }
   }
 
-  return base::WrapUnique(
-      new OptionsPageInfo(options_page, chrome_style, open_in_tab));
+  return std::make_unique<OptionsPageInfo>(options_page, chrome_style,
+                                           open_in_tab);
 }
 
 OptionsPageManifestHandler::OptionsPageManifestHandler() {
diff --git a/extensions/common/manifest_handlers/permissions_parser.cc b/extensions/common/manifest_handlers/permissions_parser.cc
index 2072509..1d9a0aea 100644
--- a/extensions/common/manifest_handlers/permissions_parser.cc
+++ b/extensions/common/manifest_handlers/permissions_parser.cc
@@ -92,7 +92,7 @@
   if (!extension->manifest()->HasKey(key))
     return true;
 
-  const base::ListValue* permissions = NULL;
+  const base::Value* permissions = nullptr;
   if (!extension->manifest()->GetList(key, &permissions)) {
     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
                                                  std::string());
diff --git a/extensions/common/manifest_handlers/requirements_info.cc b/extensions/common/manifest_handlers/requirements_info.cc
index 0456adb..d743e30 100644
--- a/extensions/common/manifest_handlers/requirements_info.cc
+++ b/extensions/common/manifest_handlers/requirements_info.cc
@@ -59,75 +59,67 @@
     return true;
   }
 
-  const base::DictionaryValue* requirements_value = NULL;
+  const base::Value* requirements_value = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kRequirements,
                                             &requirements_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidRequirements);
     return false;
   }
 
-  for (base::DictionaryValue::Iterator iter(*requirements_value);
-       !iter.IsAtEnd();
-       iter.Advance()) {
-    const base::DictionaryValue* requirement_value;
-    if (!iter.value().GetAsDictionary(&requirement_value)) {
-      *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidRequirement, iter.key());
+  for (const auto& entry : requirements_value->DictItems()) {
+    if (!entry.second.is_dict()) {
+      *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidRequirement,
+                                                   entry.first);
       return false;
     }
+    const base::Value& requirement_value = entry.second;
 
     // The plugins requirement is deprecated. Raise an install warning. If the
     // extension explicitly requires npapi plugins, raise an error.
-    if (iter.key() == "plugins") {
+    if (entry.first == "plugins") {
       extension->AddInstallWarning(
           InstallWarning(errors::kPluginsRequirementDeprecated));
-      bool requires_npapi = false;
-      if (requirement_value->GetBooleanWithoutPathExpansion("npapi",
-                                                            &requires_npapi) &&
-          requires_npapi) {
+      const base::Value* npapi_requirement =
+          requirement_value.FindKeyOfType("npapi", base::Value::Type::BOOLEAN);
+      if (npapi_requirement != nullptr && npapi_requirement->GetBool()) {
         *error = base::ASCIIToUTF16(errors::kNPAPIPluginsNotSupported);
         return false;
       }
-    } else if (iter.key() == "3D") {
-      const base::ListValue* features = NULL;
-      if (!requirement_value->GetListWithoutPathExpansion("features",
-                                                          &features) ||
-          !features) {
+    } else if (entry.first == "3D") {
+      const base::Value* features =
+          requirement_value.FindKeyOfType("features", base::Value::Type::LIST);
+      if (features == nullptr) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidRequirement, iter.key());
+            errors::kInvalidRequirement, entry.first);
         return false;
       }
 
-      for (auto feature_iter = features->begin();
-           feature_iter != features->end(); ++feature_iter) {
-        std::string feature;
-        if (feature_iter->GetAsString(&feature)) {
-          if (feature == "webgl") {
-            requirements->webgl = true;
-          } else if (feature == "css3d") {
-            // css3d is always available, so no check is needed, but no error is
-            // generated.
-          } else {
-            *error = ErrorUtils::FormatErrorMessageUTF16(
-                errors::kInvalidRequirement, iter.key());
-            return false;
-          }
-        }
-      }
-    } else if (iter.key() == "window") {
-      for (base::DictionaryValue::Iterator feature_iter(*requirement_value);
-           !feature_iter.IsAtEnd(); feature_iter.Advance()) {
-        bool feature_required = false;
-        if (!feature_iter.value().GetAsBoolean(&feature_required)) {
-          *error = ErrorUtils::FormatErrorMessageUTF16(
-              errors::kInvalidRequirement, iter.key());
-          return false;
-        }
-        if (feature_iter.key() == "shape") {
-          requirements->window_shape = feature_required;
+      for (const auto& feature : features->GetList()) {
+        if (!feature.is_string())
+          continue;
+        if (feature.GetString() == "webgl") {
+          requirements->webgl = true;
+        } else if (feature.GetString() == "css3d") {
+          // css3d is always available, so no check is needed, but no error is
+          // generated.
         } else {
           *error = ErrorUtils::FormatErrorMessageUTF16(
-              errors::kInvalidRequirement, iter.key());
+              errors::kInvalidRequirement, entry.first);
+          return false;
+        }
+      }
+    } else if (entry.first == "window") {
+      for (const auto& feature : requirement_value.DictItems()) {
+        if (!feature.second.is_bool()) {
+          *error = ErrorUtils::FormatErrorMessageUTF16(
+              errors::kInvalidRequirement, entry.first);
+          return false;
+        }
+        if (feature.first == "shape") {
+          requirements->window_shape = feature.second.GetBool();
+        } else {
+          *error = ErrorUtils::FormatErrorMessageUTF16(
+              errors::kInvalidRequirement, entry.first);
           return false;
         }
       }
diff --git a/extensions/common/manifest_handlers/sandboxed_page_info.cc b/extensions/common/manifest_handlers/sandboxed_page_info.cc
index 4bb0315..e5caeb56 100644
--- a/extensions/common/manifest_handlers/sandboxed_page_info.cc
+++ b/extensions/common/manifest_handlers/sandboxed_page_info.cc
@@ -58,19 +58,20 @@
 bool SandboxedPageHandler::Parse(Extension* extension, base::string16* error) {
   std::unique_ptr<SandboxedPageInfo> sandboxed_info(new SandboxedPageInfo);
 
-  const base::ListValue* list_value = NULL;
+  const base::Value* list_value = nullptr;
   if (!extension->manifest()->GetList(keys::kSandboxedPages, &list_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidSandboxedPagesList);
     return false;
   }
 
-  for (size_t i = 0; i < list_value->GetSize(); ++i) {
-    std::string relative_path;
-    if (!list_value->GetString(i, &relative_path)) {
+  const base::Value::ListStorage& list_storage = list_value->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidSandboxedPage, base::NumberToString(i));
       return false;
     }
+    std::string relative_path = list_storage[i].GetString();
     URLPattern pattern(URLPattern::SCHEME_EXTENSION);
     if (pattern.Parse(extension->url().spec()) !=
         URLPattern::ParseResult::kSuccess) {
diff --git a/extensions/common/manifest_handlers/shared_module_info.cc b/extensions/common/manifest_handlers/shared_module_info.cc
index a237138..e5f3341d 100644
--- a/extensions/common/manifest_handlers/shared_module_info.cc
+++ b/extensions/common/manifest_handlers/shared_module_info.cc
@@ -134,7 +134,7 @@
   }
 
   if (has_export) {
-    const base::DictionaryValue* export_value = NULL;
+    const base::Value* export_value = nullptr;
     if (!extension->manifest()->GetDictionary(keys::kExport, &export_value)) {
       *error = base::ASCIIToUTF16(errors::kInvalidExport);
       return false;
@@ -143,60 +143,66 @@
     // TODO(https://crbug.com/842354): Remove support for the legacy allowlist
     // key.
     const char* allowlist_key = nullptr;
-    if (export_value->HasKey(keys::kSharedModuleAllowlist))
+    if (export_value->FindKey(keys::kSharedModuleAllowlist) != nullptr) {
       allowlist_key = keys::kSharedModuleAllowlist;
-    else if (export_value->HasKey(keys::kSharedModuleLegacyAllowlist))
+    } else if (export_value->FindKey(keys::kSharedModuleLegacyAllowlist) !=
+               nullptr) {
       allowlist_key = keys::kSharedModuleLegacyAllowlist;
+    }
 
     if (allowlist_key) {
-      const base::ListValue* allowlist_value = NULL;
-      if (!export_value->GetList(allowlist_key, &allowlist_value)) {
+      const base::Value* allowlist_value =
+          export_value->FindKeyOfType(allowlist_key, base::Value::Type::LIST);
+      if (allowlist_value == nullptr) {
         *error = base::ASCIIToUTF16(errors::kInvalidExportAllowlist);
         return false;
       }
-      for (size_t i = 0; i < allowlist_value->GetSize(); ++i) {
-        std::string extension_id;
-        if (!allowlist_value->GetString(i, &extension_id) ||
-            !crx_file::id_util::IdIsValid(extension_id)) {
+      const base::Value::ListStorage& list_storage = allowlist_value->GetList();
+      for (size_t i = 0; i < list_storage.size(); ++i) {
+        if (!list_storage[i].is_string() ||
+            !crx_file::id_util::IdIsValid(list_storage[i].GetString())) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
               errors::kInvalidExportAllowlistString, base::NumberToString(i));
           return false;
         }
-        export_allowlist_.insert(extension_id);
+        export_allowlist_.insert(list_storage[i].GetString());
       }
     }
   }
 
   if (has_import) {
-    const base::ListValue* import_list = NULL;
+    const base::Value* import_list = nullptr;
     if (!extension->manifest()->GetList(keys::kImport, &import_list)) {
       *error = base::ASCIIToUTF16(errors::kInvalidImport);
       return false;
     }
-    for (size_t i = 0; i < import_list->GetSize(); ++i) {
-      const base::DictionaryValue* import_entry = NULL;
-      if (!import_list->GetDictionary(i, &import_entry)) {
+    const base::Value::ListStorage& list_storage = import_list->GetList();
+    for (size_t i = 0; i < list_storage.size(); ++i) {
+      const base::Value& import_entry = list_storage[i];
+      if (!import_entry.is_dict()) {
         *error = base::ASCIIToUTF16(errors::kInvalidImport);
         return false;
       }
-      std::string extension_id;
       imports_.push_back(ImportInfo());
-      if (!import_entry->GetString(keys::kId, &extension_id) ||
-          !crx_file::id_util::IdIsValid(extension_id)) {
+      const base::Value* extension_id =
+          import_entry.FindKeyOfType(keys::kId, base::Value::Type::STRING);
+      if (extension_id == nullptr ||
+          !crx_file::id_util::IdIsValid(extension_id->GetString())) {
         *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidImportId,
                                                      base::NumberToString(i));
         return false;
       }
-      imports_.back().extension_id = extension_id;
-      if (import_entry->HasKey(keys::kMinimumVersion)) {
-        std::string min_version;
-        if (!import_entry->GetString(keys::kMinimumVersion, &min_version)) {
+      imports_.back().extension_id = extension_id->GetString();
+      const base::Value* min_version =
+          import_entry.FindKey(keys::kMinimumVersion);
+      if (min_version != nullptr) {
+        if (!min_version->is_string()) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
               errors::kInvalidImportVersion, base::NumberToString(i));
           return false;
         }
-        imports_.back().minimum_version = min_version;
-        base::Version v(min_version);
+        imports_.back().minimum_version = min_version->GetString();
+        base::Version v(min_version->GetString());
         if (!v.IsValid()) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
               errors::kInvalidImportVersion, base::NumberToString(i));
diff --git a/extensions/common/manifest_handlers/web_accessible_resources_info.cc b/extensions/common/manifest_handlers/web_accessible_resources_info.cc
index 071edce..be6685f 100644
--- a/extensions/common/manifest_handlers/web_accessible_resources_info.cc
+++ b/extensions/common/manifest_handlers/web_accessible_resources_info.cc
@@ -62,19 +62,20 @@
                                           base::string16* error) {
   std::unique_ptr<WebAccessibleResourcesInfo> info(
       new WebAccessibleResourcesInfo);
-  const base::ListValue* list_value = NULL;
+  const base::Value* list_value = nullptr;
   if (!extension->manifest()->GetList(keys::kWebAccessibleResources,
                                       &list_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList);
     return false;
   }
-  for (size_t i = 0; i < list_value->GetSize(); ++i) {
-    std::string relative_path;
-    if (!list_value->GetString(i, &relative_path)) {
+  const base::Value::ListStorage& list_storage = list_value->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
+    if (!list_storage[i].is_string()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidWebAccessibleResource, base::NumberToString(i));
       return false;
     }
+    std::string relative_path = list_storage[i].GetString();
     URLPattern pattern(URLPattern::SCHEME_EXTENSION);
     if (pattern.Parse(extension->url().spec()) !=
         URLPattern::ParseResult::kSuccess) {
diff --git a/extensions/common/manifest_handlers/webview_info.cc b/extensions/common/manifest_handlers/webview_info.cc
index 79ecf38e1..f3cc32d 100644
--- a/extensions/common/manifest_handlers/webview_info.cc
+++ b/extensions/common/manifest_handlers/webview_info.cc
@@ -115,69 +115,73 @@
 bool WebviewHandler::Parse(Extension* extension, base::string16* error) {
   std::unique_ptr<WebviewInfo> info(new WebviewInfo(extension->id()));
 
-  const base::DictionaryValue* dict_value = NULL;
+  const base::Value* dict_value = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kWebview,
                                             &dict_value)) {
     *error = base::ASCIIToUTF16(errors::kInvalidWebview);
     return false;
   }
 
-  const base::ListValue* partition_list = NULL;
-  if (!dict_value->GetList(keys::kWebviewPartitions, &partition_list)) {
+  const base::Value* partition_list = dict_value->FindKeyOfType(
+      keys::kWebviewPartitions, base::Value::Type::LIST);
+  if (partition_list == nullptr) {
     *error = base::ASCIIToUTF16(errors::kInvalidWebviewPartitionsList);
     return false;
   }
 
   // The partition list must have at least one entry.
-  if (partition_list->GetSize() == 0) {
+  const base::Value::ListStorage& partition_list_storage =
+      partition_list->GetList();
+  if (partition_list_storage.empty()) {
     *error = base::ASCIIToUTF16(errors::kInvalidWebviewPartitionsList);
     return false;
   }
 
-  for (size_t i = 0; i < partition_list->GetSize(); ++i) {
-    const base::DictionaryValue* partition = NULL;
-    if (!partition_list->GetDictionary(i, &partition)) {
+  for (size_t i = 0; i < partition_list_storage.size(); ++i) {
+    if (!partition_list_storage[i].is_dict()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidWebviewPartition, base::NumberToString(i));
       return false;
     }
 
-    std::string partition_pattern;
-    if (!partition->GetString(keys::kWebviewName, &partition_pattern)) {
+    const base::Value* webview_name = partition_list_storage[i].FindKeyOfType(
+        keys::kWebviewName, base::Value::Type::STRING);
+    if (webview_name == nullptr) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           errors::kInvalidWebviewPartitionName, base::NumberToString(i));
       return false;
     }
+    const std::string& partition_pattern = webview_name->GetString();
 
-    const base::ListValue* url_list = NULL;
-    if (!partition->GetList(keys::kWebviewAccessibleResources,
-                            &url_list)) {
+    const base::Value* url_list = partition_list_storage[i].FindKeyOfType(
+        keys::kWebviewAccessibleResources, base::Value::Type::LIST);
+    if (url_list == nullptr) {
       *error = base::ASCIIToUTF16(
           errors::kInvalidWebviewAccessibleResourcesList);
       return false;
     }
 
     // The URL list should have at least one entry.
-    if (url_list->GetSize() == 0) {
+    const base::Value::ListStorage& url_list_storage = url_list->GetList();
+    if (url_list_storage.empty()) {
       *error = base::ASCIIToUTF16(
           errors::kInvalidWebviewAccessibleResourcesList);
       return false;
     }
 
-    std::unique_ptr<PartitionItem> partition_item(
-        new PartitionItem(partition_pattern));
+    auto partition_item = std::make_unique<PartitionItem>(partition_pattern);
 
-    for (size_t i = 0; i < url_list->GetSize(); ++i) {
-      std::string relative_path;
-      if (!url_list->GetString(i, &relative_path)) {
+    for (size_t i = 0; i < url_list_storage.size(); ++i) {
+      if (!url_list_storage[i].is_string()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidWebviewAccessibleResource, base::NumberToString(i));
         return false;
       }
-      URLPattern pattern(URLPattern::SCHEME_EXTENSION,
-                         Extension::GetResourceURL(extension->url(),
-                                                   relative_path).spec());
-      partition_item->AddPattern(pattern);
+      partition_item->AddPattern(
+          URLPattern(URLPattern::SCHEME_EXTENSION,
+                     Extension::GetResourceURL(extension->url(),
+                                               url_list_storage[i].GetString())
+                         .spec()));
     }
     info->AddPartitionItem(std::move(partition_item));
   }
diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc
index 80cbe37..fd2ae552 100644
--- a/extensions/common/permissions/api_permission_set.cc
+++ b/extensions/common/permissions/api_permission_set.cc
@@ -88,8 +88,7 @@
                            base::string16* error,
                            std::vector<std::string>* unhandled_permissions) {
   if (permission_value) {
-    const base::ListValue* permissions;
-    if (!permission_value->GetAsList(&permissions)) {
+    if (!permission_value->is_list()) {
       if (error) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidPermission, base_name);
@@ -101,9 +100,10 @@
       return true;
     }
 
-    for (size_t i = 0; i < permissions->GetSize(); ++i) {
+    const base::Value::ListStorage& list_storage = permission_value->GetList();
+    for (size_t i = 0; i < list_storage.size(); ++i) {
       std::string permission_str;
-      if (!permissions->GetString(i, &permission_str)) {
+      if (!list_storage[i].is_string()) {
         // permission should be a string
         if (error) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
@@ -115,15 +115,15 @@
         continue;
       }
 
-      if (!CreateAPIPermission(
-              base_name + '.' + permission_str, NULL, source,
-              api_permissions, error, unhandled_permissions))
+      if (!CreateAPIPermission(base_name + '.' + list_storage[i].GetString(),
+                               nullptr, source, api_permissions, error,
+                               unhandled_permissions))
         return false;
     }
   }
 
-  return CreateAPIPermission(base_name, NULL, source,
-                             api_permissions, error, NULL);
+  return CreateAPIPermission(base_name, nullptr, source, api_permissions, error,
+                             nullptr);
 }
 
 }  // namespace
@@ -141,29 +141,41 @@
 
 // static
 bool APIPermissionSet::ParseFromJSON(
-    const base::ListValue* permissions,
+    const base::Value* permissions,
     APIPermissionSet::ParseSource source,
     APIPermissionSet* api_permissions,
     base::string16* error,
     std::vector<std::string>* unhandled_permissions) {
-  for (size_t i = 0; i < permissions->GetSize(); ++i) {
+  if (!permissions->is_list()) {
+    if (error) {
+      *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermission,
+                                                   "<root>");
+      return false;
+    }
+    LOG(WARNING) << "Root Permissions value is not a list.";
+    // Failed to parse, but since error is NULL, failures are not fatal so
+    // return true here anyway.
+    return true;
+  }
+  const base::Value::ListStorage& list_storage = permissions->GetList();
+  for (size_t i = 0; i < list_storage.size(); ++i) {
     std::string permission_str;
-    const base::Value* permission_value = NULL;
-    if (!permissions->GetString(i, &permission_str)) {
-      const base::DictionaryValue* dict = NULL;
-      // permission should be a string or a single key dict.
-      if (!permissions->GetDictionary(i, &dict) || dict->size() != 1) {
-        if (error) {
-          *error = ErrorUtils::FormatErrorMessageUTF16(
-              errors::kInvalidPermission, base::NumberToString(i));
-          return false;
-        }
-        LOG(WARNING) << "Permission is not a string or single key dict.";
-        continue;
+    const base::Value* permission_value = nullptr;
+    // permission should be a string or a single key dict.
+    if (list_storage[i].is_string()) {
+      permission_str = list_storage[i].GetString();
+    } else if (list_storage[i].is_dict() && list_storage[i].DictSize() == 1) {
+      auto dict_iter = list_storage[i].DictItems().begin();
+      permission_str = dict_iter->first;
+      permission_value = &dict_iter->second;
+    } else {
+      if (error) {
+        *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermission,
+                                                     base::NumberToString(i));
+        return false;
       }
-      base::DictionaryValue::Iterator it(*dict);
-      permission_str = it.key();
-      permission_value = &it.value();
+      LOG(WARNING) << "Permission is not a string or single key dict.";
+      continue;
     }
 
     // Check if this permission is a special case where its value should
diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h
index 467fef2..6aed049 100644
--- a/extensions/common/permissions/api_permission_set.h
+++ b/extensions/common/permissions/api_permission_set.h
@@ -16,7 +16,7 @@
 #include "extensions/common/permissions/base_set_operators.h"
 
 namespace base {
-class ListValue;
+class Value;
 }  // namespace base
 
 namespace extensions {
@@ -54,12 +54,11 @@
   // next permission if invalid data is detected. If |error| is not NULL, it
   // will be set to an error message and false is returned when an invalid
   // permission is found.
-  static bool ParseFromJSON(
-      const base::ListValue* permissions,
-      ParseSource source,
-      APIPermissionSet* api_permissions,
-      base::string16* error,
-      std::vector<std::string>* unhandled_permissions);
+  static bool ParseFromJSON(const base::Value* permissions,
+                            ParseSource source,
+                            APIPermissionSet* api_permissions,
+                            base::string16* error,
+                            std::vector<std::string>* unhandled_permissions);
 };
 
 // An ID representing a single permission that belongs to an app or extension.
diff --git a/extensions/renderer/scoped_web_frame.cc b/extensions/renderer/scoped_web_frame.cc
index 543ce742..cdab006 100644
--- a/extensions/renderer/scoped_web_frame.cc
+++ b/extensions/renderer/scoped_web_frame.cc
@@ -19,7 +19,6 @@
 
 ScopedWebFrame::ScopedWebFrame()
     : view_(blink::WebView::Create(/*client=*/nullptr,
-                                   /*widget_client=*/nullptr,
                                    /*is_hidden=*/false,
                                    /*compositing_enabled=*/false,
                                    /*opener=*/nullptr)),
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index 79842ede..621c32a 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -124,7 +124,7 @@
   AppsFocusRules() {}
   ~AppsFocusRules() override {}
 
-  bool SupportsChildActivation(aura::Window* window) const override {
+  bool SupportsChildActivation(const aura::Window* window) const override {
     return true;
   }
 
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index fc91e109..662b926 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -181,27 +181,14 @@
 # not_shared:   For GENn types, True if objects can't be shared between contexts
 
 _FUNCTION_INFO = {
-  'CreateAndConsumeTextureINTERNAL': {
-    'decoder_func': 'DoCreateAndConsumeTextureINTERNAL',
-    'internal': True,
-    'type': 'PUT',
-    'count': 16,  # GL_MAILBOX_SIZE_CHROMIUM
-    'unit_test': False,
-    'trace_level': 2,
-  },
   'CopySubTextureINTERNAL': {
     'decoder_func': 'DoCopySubTextureINTERNAL',
     'internal': True,
+    'type': 'PUT',
+    'count': 32,  # GL_MAILBOX_SIZE_CHROMIUM x2
     'unit_test': False,
     'trace_level': 2,
   },
-  'DeleteTexturesINTERNAL': {
-    'type': 'DELn',
-    'internal': True,
-    'resource_type': 'Texture',
-    'resource_types': 'Textures',
-    'unit_test': False,
-  },
   'Finish': {
     'impl_func': False,
     'client_test': False,
diff --git a/gpu/command_buffer/client/raster_cmd_helper_autogen.h b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
index b4c0dbb..ec36213 100644
--- a/gpu/command_buffer/client/raster_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
@@ -182,41 +182,20 @@
   }
 }
 
-void CreateAndConsumeTextureINTERNALImmediate(GLuint texture_id,
-                                              const GLbyte* mailbox) {
+void CopySubTextureINTERNALImmediate(GLint xoffset,
+                                     GLint yoffset,
+                                     GLint x,
+                                     GLint y,
+                                     GLsizei width,
+                                     GLsizei height,
+                                     const GLbyte* mailboxes) {
   const uint32_t size =
-      raster::cmds::CreateAndConsumeTextureINTERNALImmediate::ComputeSize();
-  raster::cmds::CreateAndConsumeTextureINTERNALImmediate* c =
+      raster::cmds::CopySubTextureINTERNALImmediate::ComputeSize();
+  raster::cmds::CopySubTextureINTERNALImmediate* c =
       GetImmediateCmdSpaceTotalSize<
-          raster::cmds::CreateAndConsumeTextureINTERNALImmediate>(size);
+          raster::cmds::CopySubTextureINTERNALImmediate>(size);
   if (c) {
-    c->Init(texture_id, mailbox);
-  }
-}
-
-void CopySubTextureINTERNAL(GLuint source_id,
-                            GLuint dest_id,
-                            GLint xoffset,
-                            GLint yoffset,
-                            GLint x,
-                            GLint y,
-                            GLsizei width,
-                            GLsizei height) {
-  raster::cmds::CopySubTextureINTERNAL* c =
-      GetCmdSpace<raster::cmds::CopySubTextureINTERNAL>();
-  if (c) {
-    c->Init(source_id, dest_id, xoffset, yoffset, x, y, width, height);
-  }
-}
-
-void DeleteTexturesINTERNALImmediate(GLsizei n, const GLuint* textures) {
-  const uint32_t size =
-      raster::cmds::DeleteTexturesINTERNALImmediate::ComputeSize(n);
-  raster::cmds::DeleteTexturesINTERNALImmediate* c =
-      GetImmediateCmdSpaceTotalSize<
-          raster::cmds::DeleteTexturesINTERNALImmediate>(size);
-  if (c) {
-    c->Init(n, textures);
+    c->Init(xoffset, yoffset, x, y, width, height, mailboxes);
   }
 }
 
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index 6d557ddf..a4ad95d8 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -1030,15 +1030,12 @@
     SetGLError(GL_INVALID_VALUE, "glCopySubTexture", "height < 0");
     return;
   }
-  // TODO(piman): coalesce all these commands.
-  GLuint texture_ids[2] = {1, 2};
-  helper_->CreateAndConsumeTextureINTERNALImmediate(texture_ids[0],
-                                                    source_mailbox.name);
-  helper_->CreateAndConsumeTextureINTERNALImmediate(texture_ids[1],
-                                                    dest_mailbox.name);
-  helper_->CopySubTextureINTERNAL(texture_ids[0], texture_ids[1], xoffset,
-                                  yoffset, x, y, width, height);
-  helper_->DeleteTexturesINTERNALImmediate(2, texture_ids);
+  GLbyte mailboxes[sizeof(source_mailbox.name) * 2];
+  memcpy(mailboxes, source_mailbox.name, sizeof(source_mailbox.name));
+  memcpy(mailboxes + sizeof(source_mailbox.name), dest_mailbox.name,
+         sizeof(dest_mailbox.name));
+  helper_->CopySubTextureINTERNALImmediate(xoffset, yoffset, x, y, width,
+                                           height, mailboxes);
   CheckGLError();
 }
 
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index d62e5d1..5663189 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -787,14 +787,14 @@
 static_assert(offsetof(ClearPaintCacheINTERNAL, header) == 0,
               "offset of ClearPaintCacheINTERNAL header should be 0");
 
-struct CreateAndConsumeTextureINTERNALImmediate {
-  typedef CreateAndConsumeTextureINTERNALImmediate ValueType;
-  static const CommandId kCmdId = kCreateAndConsumeTextureINTERNALImmediate;
+struct CopySubTextureINTERNALImmediate {
+  typedef CopySubTextureINTERNALImmediate ValueType;
+  static const CommandId kCmdId = kCopySubTextureINTERNALImmediate;
   static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
   static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);
 
   static uint32_t ComputeDataSize() {
-    return static_cast<uint32_t>(sizeof(GLbyte) * 16);
+    return static_cast<uint32_t>(sizeof(GLbyte) * 32);
   }
 
   static uint32_t ComputeSize() {
@@ -803,80 +803,38 @@
 
   void SetHeader() { header.SetCmdByTotalSize<ValueType>(ComputeSize()); }
 
-  void Init(GLuint _texture_id, const GLbyte* _mailbox) {
-    SetHeader();
-    texture_id = _texture_id;
-    memcpy(ImmediateDataAddress(this), _mailbox, ComputeDataSize());
-  }
-
-  void* Set(void* cmd, GLuint _texture_id, const GLbyte* _mailbox) {
-    static_cast<ValueType*>(cmd)->Init(_texture_id, _mailbox);
-    const uint32_t size = ComputeSize();
-    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
-  }
-
-  gpu::CommandHeader header;
-  uint32_t texture_id;
-};
-
-static_assert(sizeof(CreateAndConsumeTextureINTERNALImmediate) == 8,
-              "size of CreateAndConsumeTextureINTERNALImmediate should be 8");
-static_assert(
-    offsetof(CreateAndConsumeTextureINTERNALImmediate, header) == 0,
-    "offset of CreateAndConsumeTextureINTERNALImmediate header should be 0");
-static_assert(offsetof(CreateAndConsumeTextureINTERNALImmediate, texture_id) ==
-                  4,
-              "offset of CreateAndConsumeTextureINTERNALImmediate texture_id "
-              "should be 4");
-
-struct CopySubTextureINTERNAL {
-  typedef CopySubTextureINTERNAL ValueType;
-  static const CommandId kCmdId = kCopySubTextureINTERNAL;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init(GLuint _source_id,
-            GLuint _dest_id,
-            GLint _xoffset,
+  void Init(GLint _xoffset,
             GLint _yoffset,
             GLint _x,
             GLint _y,
             GLsizei _width,
-            GLsizei _height) {
+            GLsizei _height,
+            const GLbyte* _mailboxes) {
     SetHeader();
-    source_id = _source_id;
-    dest_id = _dest_id;
     xoffset = _xoffset;
     yoffset = _yoffset;
     x = _x;
     y = _y;
     width = _width;
     height = _height;
+    memcpy(ImmediateDataAddress(this), _mailboxes, ComputeDataSize());
   }
 
   void* Set(void* cmd,
-            GLuint _source_id,
-            GLuint _dest_id,
             GLint _xoffset,
             GLint _yoffset,
             GLint _x,
             GLint _y,
             GLsizei _width,
-            GLsizei _height) {
-    static_cast<ValueType*>(cmd)->Init(_source_id, _dest_id, _xoffset, _yoffset,
-                                       _x, _y, _width, _height);
-    return NextCmdAddress<ValueType>(cmd);
+            GLsizei _height,
+            const GLbyte* _mailboxes) {
+    static_cast<ValueType*>(cmd)->Init(_xoffset, _yoffset, _x, _y, _width,
+                                       _height, _mailboxes);
+    const uint32_t size = ComputeSize();
+    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
   }
 
   gpu::CommandHeader header;
-  uint32_t source_id;
-  uint32_t dest_id;
   int32_t xoffset;
   int32_t yoffset;
   int32_t x;
@@ -885,68 +843,22 @@
   int32_t height;
 };
 
-static_assert(sizeof(CopySubTextureINTERNAL) == 36,
-              "size of CopySubTextureINTERNAL should be 36");
-static_assert(offsetof(CopySubTextureINTERNAL, header) == 0,
-              "offset of CopySubTextureINTERNAL header should be 0");
-static_assert(offsetof(CopySubTextureINTERNAL, source_id) == 4,
-              "offset of CopySubTextureINTERNAL source_id should be 4");
-static_assert(offsetof(CopySubTextureINTERNAL, dest_id) == 8,
-              "offset of CopySubTextureINTERNAL dest_id should be 8");
-static_assert(offsetof(CopySubTextureINTERNAL, xoffset) == 12,
-              "offset of CopySubTextureINTERNAL xoffset should be 12");
-static_assert(offsetof(CopySubTextureINTERNAL, yoffset) == 16,
-              "offset of CopySubTextureINTERNAL yoffset should be 16");
-static_assert(offsetof(CopySubTextureINTERNAL, x) == 20,
-              "offset of CopySubTextureINTERNAL x should be 20");
-static_assert(offsetof(CopySubTextureINTERNAL, y) == 24,
-              "offset of CopySubTextureINTERNAL y should be 24");
-static_assert(offsetof(CopySubTextureINTERNAL, width) == 28,
-              "offset of CopySubTextureINTERNAL width should be 28");
-static_assert(offsetof(CopySubTextureINTERNAL, height) == 32,
-              "offset of CopySubTextureINTERNAL height should be 32");
-
-struct DeleteTexturesINTERNALImmediate {
-  typedef DeleteTexturesINTERNALImmediate ValueType;
-  static const CommandId kCmdId = kDeleteTexturesINTERNALImmediate;
-  static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeDataSize(GLsizei _n) {
-    return static_cast<uint32_t>(sizeof(GLuint) * _n);  // NOLINT
-  }
-
-  static uint32_t ComputeSize(GLsizei _n) {
-    return static_cast<uint32_t>(sizeof(ValueType) +
-                                 ComputeDataSize(_n));  // NOLINT
-  }
-
-  void SetHeader(GLsizei _n) {
-    header.SetCmdByTotalSize<ValueType>(ComputeSize(_n));
-  }
-
-  void Init(GLsizei _n, const GLuint* _textures) {
-    SetHeader(_n);
-    n = _n;
-    memcpy(ImmediateDataAddress(this), _textures, ComputeDataSize(_n));
-  }
-
-  void* Set(void* cmd, GLsizei _n, const GLuint* _textures) {
-    static_cast<ValueType*>(cmd)->Init(_n, _textures);
-    const uint32_t size = ComputeSize(_n);
-    return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
-  }
-
-  gpu::CommandHeader header;
-  int32_t n;
-};
-
-static_assert(sizeof(DeleteTexturesINTERNALImmediate) == 8,
-              "size of DeleteTexturesINTERNALImmediate should be 8");
-static_assert(offsetof(DeleteTexturesINTERNALImmediate, header) == 0,
-              "offset of DeleteTexturesINTERNALImmediate header should be 0");
-static_assert(offsetof(DeleteTexturesINTERNALImmediate, n) == 4,
-              "offset of DeleteTexturesINTERNALImmediate n should be 4");
+static_assert(sizeof(CopySubTextureINTERNALImmediate) == 28,
+              "size of CopySubTextureINTERNALImmediate should be 28");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, header) == 0,
+              "offset of CopySubTextureINTERNALImmediate header should be 0");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, xoffset) == 4,
+              "offset of CopySubTextureINTERNALImmediate xoffset should be 4");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, yoffset) == 8,
+              "offset of CopySubTextureINTERNALImmediate yoffset should be 8");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, x) == 12,
+              "offset of CopySubTextureINTERNALImmediate x should be 12");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, y) == 16,
+              "offset of CopySubTextureINTERNALImmediate y should be 16");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, width) == 20,
+              "offset of CopySubTextureINTERNALImmediate width should be 20");
+static_assert(offsetof(CopySubTextureINTERNALImmediate, height) == 24,
+              "offset of CopySubTextureINTERNALImmediate height should be 24");
 
 struct TraceBeginCHROMIUM {
   typedef TraceBeginCHROMIUM ValueType;
diff --git a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
index eab9a6d..1402583 100644
--- a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
@@ -296,7 +296,7 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(RasterFormatTest, CreateAndConsumeTextureINTERNALImmediate) {
+TEST_F(RasterFormatTest, CopySubTextureINTERNALImmediate) {
   const int kSomeBaseValueToTestWith = 51;
   static GLbyte data[] = {
       static_cast<GLbyte>(kSomeBaseValueToTestWith + 0),
@@ -315,63 +315,44 @@
       static_cast<GLbyte>(kSomeBaseValueToTestWith + 13),
       static_cast<GLbyte>(kSomeBaseValueToTestWith + 14),
       static_cast<GLbyte>(kSomeBaseValueToTestWith + 15),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 16),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 17),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 18),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 19),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 20),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 21),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 22),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 23),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 24),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 25),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 26),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 27),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 28),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 29),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 30),
+      static_cast<GLbyte>(kSomeBaseValueToTestWith + 31),
   };
-  cmds::CreateAndConsumeTextureINTERNALImmediate& cmd =
-      *GetBufferAs<cmds::CreateAndConsumeTextureINTERNALImmediate>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11), data);
-  EXPECT_EQ(static_cast<uint32_t>(
-                cmds::CreateAndConsumeTextureINTERNALImmediate::kCmdId),
-            cmd.header.command);
+  cmds::CopySubTextureINTERNALImmediate& cmd =
+      *GetBufferAs<cmds::CopySubTextureINTERNALImmediate>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLint>(11), static_cast<GLint>(12),
+              static_cast<GLint>(13), static_cast<GLint>(14),
+              static_cast<GLsizei>(15), static_cast<GLsizei>(16), data);
+  EXPECT_EQ(
+      static_cast<uint32_t>(cmds::CopySubTextureINTERNALImmediate::kCmdId),
+      cmd.header.command);
   EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)),
             cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLuint>(11), cmd.texture_id);
+  EXPECT_EQ(static_cast<GLint>(11), cmd.xoffset);
+  EXPECT_EQ(static_cast<GLint>(12), cmd.yoffset);
+  EXPECT_EQ(static_cast<GLint>(13), cmd.x);
+  EXPECT_EQ(static_cast<GLint>(14), cmd.y);
+  EXPECT_EQ(static_cast<GLsizei>(15), cmd.width);
+  EXPECT_EQ(static_cast<GLsizei>(16), cmd.height);
   CheckBytesWrittenMatchesExpectedSize(
       next_cmd, sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)));
 }
 
-TEST_F(RasterFormatTest, CopySubTextureINTERNAL) {
-  cmds::CopySubTextureINTERNAL& cmd =
-      *GetBufferAs<cmds::CopySubTextureINTERNAL>();
-  void* next_cmd =
-      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLuint>(12),
-              static_cast<GLint>(13), static_cast<GLint>(14),
-              static_cast<GLint>(15), static_cast<GLint>(16),
-              static_cast<GLsizei>(17), static_cast<GLsizei>(18));
-  EXPECT_EQ(static_cast<uint32_t>(cmds::CopySubTextureINTERNAL::kCmdId),
-            cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLuint>(11), cmd.source_id);
-  EXPECT_EQ(static_cast<GLuint>(12), cmd.dest_id);
-  EXPECT_EQ(static_cast<GLint>(13), cmd.xoffset);
-  EXPECT_EQ(static_cast<GLint>(14), cmd.yoffset);
-  EXPECT_EQ(static_cast<GLint>(15), cmd.x);
-  EXPECT_EQ(static_cast<GLint>(16), cmd.y);
-  EXPECT_EQ(static_cast<GLsizei>(17), cmd.width);
-  EXPECT_EQ(static_cast<GLsizei>(18), cmd.height);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
-TEST_F(RasterFormatTest, DeleteTexturesINTERNALImmediate) {
-  static GLuint ids[] = {
-      12,
-      23,
-      34,
-  };
-  cmds::DeleteTexturesINTERNALImmediate& cmd =
-      *GetBufferAs<cmds::DeleteTexturesINTERNALImmediate>();
-  void* next_cmd = cmd.Set(&cmd, static_cast<GLsizei>(base::size(ids)), ids);
-  EXPECT_EQ(
-      static_cast<uint32_t>(cmds::DeleteTexturesINTERNALImmediate::kCmdId),
-      cmd.header.command);
-  EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(cmd.n * 4u),
-            cmd.header.size * 4u);
-  EXPECT_EQ(static_cast<GLsizei>(base::size(ids)), cmd.n);
-  CheckBytesWrittenMatchesExpectedSize(
-      next_cmd,
-      sizeof(cmd) + RoundSizeToMultipleOfEntries(base::size(ids) * 4u));
-  EXPECT_EQ(0, memcmp(ids, ImmediateDataAddress(&cmd), sizeof(ids)));
-}
-
 TEST_F(RasterFormatTest, TraceBeginCHROMIUM) {
   cmds::TraceBeginCHROMIUM& cmd = *GetBufferAs<cmds::TraceBeginCHROMIUM>();
   void* next_cmd =
diff --git a/gpu/command_buffer/common/raster_cmd_ids_autogen.h b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
index 9425e84e..0c879b9 100644
--- a/gpu/command_buffer/common/raster_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_ids_autogen.h
@@ -30,12 +30,10 @@
   OP(DeletePaintCacheTextBlobsINTERNALImmediate) /* 271 */ \
   OP(DeletePaintCachePathsINTERNALImmediate)     /* 272 */ \
   OP(ClearPaintCacheINTERNAL)                    /* 273 */ \
-  OP(CreateAndConsumeTextureINTERNALImmediate)   /* 274 */ \
-  OP(CopySubTextureINTERNAL)                     /* 275 */ \
-  OP(DeleteTexturesINTERNALImmediate)            /* 276 */ \
-  OP(TraceBeginCHROMIUM)                         /* 277 */ \
-  OP(TraceEndCHROMIUM)                           /* 278 */ \
-  OP(SetActiveURLCHROMIUM)                       /* 279 */
+  OP(CopySubTextureINTERNALImmediate)            /* 274 */ \
+  OP(TraceBeginCHROMIUM)                         /* 275 */ \
+  OP(TraceEndCHROMIUM)                           /* 276 */ \
+  OP(SetActiveURLCHROMIUM)                       /* 277 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/raster_cmd_buffer_functions.txt b/gpu/command_buffer/raster_cmd_buffer_functions.txt
index f2205d3..40ee7be 100644
--- a/gpu/command_buffer/raster_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/raster_cmd_buffer_functions.txt
@@ -37,9 +37,8 @@
 GL_APICALL void         GL_APIENTRY glClearPaintCacheINTERNAL (void);
 
 // TOOD(backer): Remove GL encoding. These are not GL functions.
-GL_APICALL GLuint       GL_APIENTRY glCreateAndConsumeTextureINTERNAL (GLuint texture_id, const GLbyte* mailbox);
-GL_APICALL void         GL_APIENTRY glCopySubTextureINTERNAL (GLuint source_id, GLuint dest_id, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
-GL_APICALL void         GL_APIENTRY glDeleteTexturesINTERNAL (GLsizeiNotNegative n, const GLuint* textures);
+// |mailboxes| argument is the concatenation of the source mailbox and the destination mailbox (32 bytes total)
+GL_APICALL void         GL_APIENTRY glCopySubTextureINTERNAL (GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, const GLbyte* mailboxes);
 GL_APICALL void         GL_APIENTRY glTraceBeginCHROMIUM (const char* category_name, const char* trace_name);
 GL_APICALL void         GL_APIENTRY glTraceEndCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glSetActiveURLCHROMIUM (const char* url);
diff --git a/gpu/command_buffer/service/copy_texture_chromium_mock.h b/gpu/command_buffer/service/copy_texture_chromium_mock.h
index 622cf5da..a163f292 100644
--- a/gpu/command_buffer/service/copy_texture_chromium_mock.h
+++ b/gpu/command_buffer/service/copy_texture_chromium_mock.h
@@ -73,7 +73,7 @@
 
   // Cannot MOCK_METHOD more than 10 args.
   void DoCopyTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -91,7 +91,7 @@
       CopyTextureMethod method,
       CopyTexImageResourceManager* luma_emulation_blitter) override {}
   void DoCopySubTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -117,7 +117,7 @@
       CopyTextureMethod method,
       CopyTexImageResourceManager* luma_emulation_blitter) override {}
   void DoCopySubTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -143,7 +143,7 @@
       const GLfloat transform_matrix[16],
       CopyTexImageResourceManager* luma_emulation_blitter) override{};
   void DoCopyTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
diff --git a/gpu/command_buffer/service/decoder_context.h b/gpu/command_buffer/service/decoder_context.h
index 523bf03..61b0954 100644
--- a/gpu/command_buffer/service/decoder_context.h
+++ b/gpu/command_buffer/service/decoder_context.h
@@ -218,7 +218,7 @@
   virtual void RestoreAllAttributes() const = 0;
 
   // Restores texture states for a given service id.
-  virtual void RestoreTextureState(unsigned service_id) const = 0;
+  virtual void RestoreTextureState(unsigned service_id) = 0;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index f315142d7..939bf436 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -546,7 +546,7 @@
 }
 
 void DoCopyTexImage2D(
-    const gpu::DecoderContext* decoder,
+    gpu::DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -599,7 +599,7 @@
 }
 
 void DoCopyTexSubImage2D(
-    const gpu::DecoderContext* decoder,
+    gpu::DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -771,7 +771,7 @@
 };
 
 void DoReadbackAndTexImage(TexImageCommandType command_type,
-                           const gpu::DecoderContext* decoder,
+                           gpu::DecoderContext* decoder,
                            GLenum source_target,
                            GLuint source_id,
                            GLint source_level,
@@ -858,7 +858,7 @@
       const gles2::FeatureInfo::FeatureFlags& feature_flags) override;
   void Destroy() override;
   void DoCopyTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -876,7 +876,7 @@
       CopyTextureMethod method,
       CopyTexImageResourceManager* luma_emulation_blitter) override;
   void DoCopySubTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -902,7 +902,7 @@
       CopyTextureMethod method,
       CopyTexImageResourceManager* luma_emulation_blitter) override;
   void DoCopySubTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -928,7 +928,7 @@
       const GLfloat transform_matrix[16],
       CopyTexImageResourceManager* luma_emulation_blitter) override;
   void DoCopyTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -975,7 +975,7 @@
   };
 
   void DoCopyTextureInternal(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -1099,7 +1099,7 @@
 }
 
 void CopyTextureResourceManagerImpl::DoCopyTexture(
-    const DecoderContext* decoder,
+    DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -1125,7 +1125,7 @@
 }
 
 void CopyTextureResourceManagerImpl::DoCopySubTexture(
-    const DecoderContext* decoder,
+    DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -1219,7 +1219,7 @@
 }
 
 void CopyTextureResourceManagerImpl::DoCopySubTextureWithTransform(
-    const DecoderContext* decoder,
+    DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -1253,7 +1253,7 @@
 }
 
 void CopyTextureResourceManagerImpl::DoCopyTextureWithTransform(
-    const DecoderContext* decoder,
+    DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
@@ -1333,7 +1333,7 @@
 }
 
 void CopyTextureResourceManagerImpl::DoCopyTextureInternal(
-    const DecoderContext* decoder,
+    DecoderContext* decoder,
     GLenum source_target,
     GLuint source_id,
     GLint source_level,
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
index f15bf14..c0952db 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h
@@ -57,7 +57,7 @@
   virtual void Destroy() = 0;
 
   virtual void DoCopyTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -76,7 +76,7 @@
       CopyTexImageResourceManager* luma_emulation_blitter) = 0;
 
   virtual void DoCopySubTexture(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -103,7 +103,7 @@
       CopyTexImageResourceManager* luma_emulation_blitter) = 0;
 
   virtual void DoCopySubTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
@@ -134,7 +134,7 @@
   // matrix should be given in column-major form, so it can be passed
   // directly to GL.
   virtual void DoCopyTextureWithTransform(
-      const DecoderContext* decoder,
+      DecoderContext* decoder,
       GLenum source_target,
       GLuint source_id,
       GLint source_level,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 82010906..df1798ff8 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -676,7 +676,7 @@
   void RestoreBufferBinding(unsigned int target) override;
   void RestoreFramebufferBindings() const override;
   void RestoreRenderbufferBindings() override;
-  void RestoreTextureState(unsigned service_id) const override;
+  void RestoreTextureState(unsigned service_id) override;
 
   void ClearDeviceWindowRectangles() const;
   void RestoreDeviceWindowRectangles() const override;
@@ -6190,7 +6190,7 @@
   state_.RestoreRenderbufferBindings();
 }
 
-void GLES2DecoderImpl::RestoreTextureState(unsigned service_id) const {
+void GLES2DecoderImpl::RestoreTextureState(unsigned service_id) {
   Texture* texture = texture_manager()->GetTextureForServiceId(service_id);
   if (texture) {
     GLenum target = texture->target();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 563e3ff..0a152ba 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -87,7 +87,7 @@
   MOCK_CONST_METHOD0(RestoreGlobalState, void());
   MOCK_CONST_METHOD0(RestoreProgramBindings, void());
   MOCK_METHOD0(RestoreRenderbufferBindings, void());
-  MOCK_CONST_METHOD1(RestoreTextureState, void(unsigned service_id));
+  MOCK_METHOD1(RestoreTextureState, void(unsigned service_id));
   MOCK_CONST_METHOD1(RestoreTextureUnitBindings, void(unsigned unit));
   MOCK_METHOD1(RestoreVertexAttribArray, void(unsigned index));
   MOCK_CONST_METHOD0(RestoreDeviceWindowRectangles, void());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 2788be4..f2b5153e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1400,8 +1400,7 @@
 
 void GLES2DecoderPassthroughImpl::RestoreProgramBindings() const {}
 
-void GLES2DecoderPassthroughImpl::RestoreTextureState(
-    unsigned service_id) const {}
+void GLES2DecoderPassthroughImpl::RestoreTextureState(unsigned service_id) {}
 
 void GLES2DecoderPassthroughImpl::RestoreTextureUnitBindings(
     unsigned unit) const {}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index fbb6cc93..ed68201 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -220,7 +220,7 @@
   void RestoreRenderbufferBindings() override;
   void RestoreGlobalState() const override;
   void RestoreProgramBindings() const override;
-  void RestoreTextureState(unsigned service_id) const override;
+  void RestoreTextureState(unsigned service_id) override;
   void RestoreTextureUnitBindings(unsigned unit) const override;
   void RestoreVertexAttribArray(unsigned index) override;
   void RestoreAllExternalTextureBindingsIfNeeded() override;
diff --git a/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc b/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc
index 02875468..78631ec29 100644
--- a/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc
+++ b/gpu/command_buffer/service/gles2_cmd_srgb_converter.cc
@@ -378,7 +378,7 @@
   decoder->RestoreGlobalState();
 }
 
-void SRGBConverter::GenerateMipmap(const gles2::GLES2Decoder* decoder,
+void SRGBConverter::GenerateMipmap(gles2::GLES2Decoder* decoder,
                                    Texture* tex,
                                    GLenum target) {
   // This function generateMipmap for srgb texture.
diff --git a/gpu/command_buffer/service/gles2_cmd_srgb_converter.h b/gpu/command_buffer/service/gles2_cmd_srgb_converter.h
index fd54e175..8518a14 100644
--- a/gpu/command_buffer/service/gles2_cmd_srgb_converter.h
+++ b/gpu/command_buffer/service/gles2_cmd_srgb_converter.h
@@ -50,7 +50,7 @@
       bool encode,
       bool enable_scissor_test);
 
-  void GenerateMipmap(const gles2::GLES2Decoder* decoder,
+  void GenerateMipmap(gles2::GLES2Decoder* decoder,
                       Texture* tex,
                       GLenum target);
 
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 09b5ae4..191ba5af 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -132,24 +132,6 @@
       gr_context->resetContext(kTextureBinding_GrGLBackendState);
   }
 
-  ScopedTextureBinder(gles2::ContextState* state,
-                      gles2::TextureManager* texture_manager,
-                      gles2::TextureRef* texture_ref,
-                      GLenum target,
-                      GrContext* gr_context,
-                      bool state_is_dirty)
-      : ScopedTextureBinder(state,
-                            target,
-                            texture_ref->texture()->service_id(),
-                            gr_context,
-                            state_is_dirty) {
-    auto* texture = texture_ref->texture();
-    if (!texture->target())
-      texture_manager->SetTarget(texture_ref, target);
-    DCHECK_EQ(texture->target(), target_)
-        << "Texture bound to more than 1 target.";
-  }
-
   ~ScopedTextureBinder() {
     if (!state_is_dirty_)
       state_->api()->glBindTextureFn(target_, 0);
@@ -265,7 +247,7 @@
   void RestoreFramebufferBindings() const override;
   void RestoreRenderbufferBindings() override;
   void RestoreProgramBindings() const override;
-  void RestoreTextureState(unsigned service_id) const override;
+  void RestoreTextureState(unsigned service_id) override;
   void RestoreTextureUnitBindings(unsigned unit) const override;
   void RestoreVertexAttribArray(unsigned index) override;
   void RestoreAllExternalTextureBindingsIfNeeded() override;
@@ -408,22 +390,6 @@
 
   gles2::ImageManager* image_manager() { return group_->image_manager(); }
 
-  // Creates a Texture for the given texture.
-  gles2::TextureRef* CreateTexture(GLuint client_id, GLuint service_id) {
-    return texture_manager()->CreateTexture(client_id, service_id);
-  }
-
-  // Gets the texture info for the given texture. Returns nullptr if none
-  // exists.
-  gles2::TextureRef* GetTexture(GLuint client_id) const {
-    return texture_manager()->GetTexture(client_id);
-  }
-
-  // Deletes the texture info for the given texture.
-  void RemoveTexture(GLuint client_id) {
-    texture_manager()->RemoveTexture(client_id);
-  }
-
   // Set remaining commands to process to 0 to force DoCommands to return
   // and allow context preemption and GPU watchdog checks in
   // CommandExecutor().
@@ -435,10 +401,6 @@
                               int num_entries,
                               int* entries_processed);
 
-  void DoCreateAndConsumeTextureINTERNAL(GLuint client_id,
-                                         const volatile GLbyte* key);
-  void DeleteTexturesINTERNALHelper(GLsizei n,
-                                    const volatile GLuint* client_ids);
   bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids);
   void DeleteQueriesEXTHelper(GLsizei n, const volatile GLuint* client_ids);
   void DoFinish();
@@ -447,14 +409,13 @@
   void DoTraceEndCHROMIUM();
   bool InitializeCopyTexImageBlitter();
   bool InitializeCopyTextureCHROMIUM();
-  void DoCopySubTextureINTERNAL(GLuint source_id,
-                                GLuint dest_id,
-                                GLint xoffset,
+  void DoCopySubTextureINTERNAL(GLint xoffset,
                                 GLint yoffset,
                                 GLint x,
                                 GLint y,
                                 GLsizei width,
-                                GLsizei height);
+                                GLsizei height,
+                                const volatile GLbyte* mailboxes);
   // If the texture has an image but that image is not bound or copied to the
   // texture, this will first attempt to bind it, and if that fails
   // CopyTexImage on it.
@@ -609,6 +570,9 @@
   // Workaround for https://crbug.com/906453
   bool flush_workaround_disabled_for_test_ = false;
 
+  bool in_copy_sub_texture_ = false;
+  bool reset_texture_state_ = false;
+
   base::WeakPtrFactory<DecoderContext> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RasterDecoderImpl);
@@ -965,24 +929,9 @@
   raster_decoder_context_state_->PessimisticallyResetGrContext();
 }
 
-void RasterDecoderImpl::RestoreTextureState(unsigned service_id) const {
-  raster_decoder_context_state_->PessimisticallyResetGrContext();
-  gles2::Texture* texture =
-      texture_manager()->GetTextureForServiceId(service_id);
-  if (texture) {
-    GLenum target = texture->target();
-    api()->glBindTextureFn(target, service_id);
-    api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, texture->wrap_s());
-    api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, texture->wrap_t());
-    api()->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER,
-                             texture->min_filter());
-    api()->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER,
-                             texture->mag_filter());
-    if (feature_info_->IsWebGL2OrES3Context()) {
-      api()->glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL,
-                               texture->base_level());
-    }
-  }
+void RasterDecoderImpl::RestoreTextureState(unsigned service_id) {
+  DCHECK(in_copy_sub_texture_);
+  reset_texture_state_ = true;
 }
 
 void RasterDecoderImpl::RestoreTextureUnitBindings(unsigned unit) const {
@@ -1580,67 +1529,6 @@
   ProcessPendingQueries(false);
 }
 
-void RasterDecoderImpl::DoCreateAndConsumeTextureINTERNAL(
-    GLuint client_id,
-    const volatile GLbyte* key) {
-  TRACE_EVENT2("gpu", "RasterDecoderImpl::DoCreateAndConsumeTextureINTERNAL",
-               "context", logger_.GetLogPrefix(), "key[0]",
-               static_cast<unsigned char>(key[0]));
-  Mailbox mailbox =
-      Mailbox::FromVolatile(*reinterpret_cast<const volatile Mailbox*>(key));
-  DLOG_IF(ERROR, !mailbox.Verify()) << "CreateAndConsumeTexture was "
-                                       "passed a mailbox that was not "
-                                       "generated by ProduceTextureCHROMIUM.";
-  if (!client_id) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glCreateAndConsumeTextureCHROMIUM",
-                       "invalid client id");
-    return;
-  }
-
-  gles2::TextureRef* texture_ref = GetTexture(client_id);
-  if (texture_ref) {
-    // No need to create texture here, the client_id already has an associated
-    // texture.
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glCreateAndConsumeTextureCHROMIUM",
-                       "client id already in use");
-    return;
-  }
-
-  gles2::Texture* texture = gles2::Texture::CheckedCast(
-      group_->mailbox_manager()->ConsumeTexture(mailbox));
-  if (!texture) {
-    // Create texture to handle invalid mailbox (see http://crbug.com/472465).
-    GLuint service_id = 0;
-    api()->glGenTexturesFn(1, &service_id);
-    DCHECK(service_id);
-    texture_manager()->CreateTexture(client_id, service_id);
-
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glCreateAndConsumeTextureCHROMIUM",
-                       "invalid mailbox name");
-    return;
-  }
-
-  texture_ref = texture_manager()->Consume(client_id, texture);
-
-  // TODO(backer): Validate that the consumed texture is consistent with
-  // TextureMetadata.
-}
-
-void RasterDecoderImpl::DeleteTexturesINTERNALHelper(
-    GLsizei n,
-    const volatile GLuint* client_ids) {
-  for (GLsizei ii = 0; ii < n; ++ii) {
-    GLuint client_id = client_ids[ii];
-    gles2::TextureRef* texture_ref = GetTexture(client_id);
-    if (texture_ref) {
-      RemoveTexture(client_id);
-    }
-  }
-}
-
 bool RasterDecoderImpl::GenQueriesEXTHelper(GLsizei n,
                                             const GLuint* client_ids) {
   for (GLsizei ii = 0; ii < n; ++ii) {
@@ -1757,37 +1645,50 @@
   return true;
 }
 
-void RasterDecoderImpl::DoCopySubTextureINTERNAL(GLuint source_id,
-                                                 GLuint dest_id,
-                                                 GLint xoffset,
-                                                 GLint yoffset,
-                                                 GLint x,
-                                                 GLint y,
-                                                 GLsizei width,
-                                                 GLsizei height) {
-  gles2::TextureRef* source_texture_ref = GetTexture(source_id);
-  gles2::TextureRef* dest_texture_ref = GetTexture(dest_id);
-  if (!source_texture_ref || !dest_texture_ref) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                       "unknown texture id");
+void RasterDecoderImpl::DoCopySubTextureINTERNAL(
+    GLint xoffset,
+    GLint yoffset,
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    const volatile GLbyte* mailboxes) {
+  Mailbox source_mailbox = Mailbox::FromVolatile(
+      reinterpret_cast<const volatile Mailbox*>(mailboxes)[0]);
+  DLOG_IF(ERROR, !source_mailbox.Verify())
+      << "CopySubTexture was passed an invalid mailbox";
+  Mailbox dest_mailbox = Mailbox::FromVolatile(
+      reinterpret_cast<const volatile Mailbox*>(mailboxes)[1]);
+  DLOG_IF(ERROR, !dest_mailbox.Verify())
+      << "CopySubTexture was passed an invalid mailbox";
+
+  // TODO(piman): use shared image representations instead.
+  gles2::Texture* source_texture = gles2::Texture::CheckedCast(
+      group_->mailbox_manager()->ConsumeTexture(source_mailbox));
+  gles2::Texture* dest_texture = gles2::Texture::CheckedCast(
+      group_->mailbox_manager()->ConsumeTexture(dest_mailbox));
+  if (!source_texture || !dest_texture) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture", "unknown mailbox");
     return;
   }
-
-  gles2::Texture* source_texture = source_texture_ref->texture();
-  gles2::Texture* dest_texture = dest_texture_ref->texture();
   if (source_texture == dest_texture) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
                        "source and destination textures are the same");
     return;
   }
-
   GLenum source_target = source_texture->target();
-  GLint source_level = 0;
   GLenum dest_target = dest_texture->target();
+  if (!source_target || !dest_target) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
+                       "textures not initialized");
+    return;
+  }
+
+  GLint source_level = 0;
   GLint dest_level = 0;
 
   ScopedTextureBinder binder(
-      state(), texture_manager(), dest_texture_ref, dest_target, gr_context(),
+      state(), dest_target, dest_texture->service_id(), gr_context(),
       raster_decoder_context_state_->need_context_state_reset);
   base::Optional<ScopedPixelUnpackState> pixel_unpack_state;
 
@@ -1891,8 +1792,8 @@
   }
 
   // Clear the source texture if necessary.
-  if (!texture_manager()->ClearTextureLevel(this, source_texture_ref,
-                                            source_target, 0 /* level */)) {
+  if (!texture_manager()->ClearTextureLevel(this, source_texture, source_target,
+                                            0 /* level */)) {
     LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTexture",
                        "source texture dimensions too big");
     return;
@@ -1913,20 +1814,18 @@
                 dest_texture->GetLevelClearedRect(dest_target, dest_level)
                     .size()
                     .GetArea());
-      texture_manager()->SetLevelClearedRect(dest_texture_ref, dest_target,
-                                             dest_level, cleared_rect);
+      dest_texture->SetLevelClearedRect(dest_target, dest_level, cleared_rect);
     } else {
       // Otherwise clear part of texture level that is not already cleared.
-      if (!texture_manager()->ClearTextureLevel(this, dest_texture_ref,
-                                                dest_target, dest_level)) {
+      if (!texture_manager()->ClearTextureLevel(this, dest_texture, dest_target,
+                                                dest_level)) {
         LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTexture",
                            "destination texture dimensions too big");
         return;
       }
     }
   } else {
-    texture_manager()->SetLevelCleared(dest_texture_ref, dest_target,
-                                       dest_level, true);
+    dest_texture->SetLevelCleared(dest_target, dest_level, true);
   }
 
   // TODO(qiankun.miao@intel.com): Support level > 0 for CopyTexSubImage.
@@ -1982,6 +1881,7 @@
   }
 #endif
 
+  in_copy_sub_texture_ = true;
   copy_texture_chromium_->DoCopySubTexture(
       this, source_target, source_texture->service_id(), source_level,
       source_internal_format, dest_target, dest_texture->service_id(),
@@ -1990,6 +1890,21 @@
       false /* unpack_flip_y */, false /* unpack_premultiply_alpha */,
       false /* unpack_unmultiply_alpha */, false /* dither */, method,
       copy_tex_image_blit_.get());
+  in_copy_sub_texture_ = false;
+  if (reset_texture_state_) {
+    reset_texture_state_ = false;
+    for (auto* texture : {source_texture, dest_texture}) {
+      GLenum target = texture->target();
+      api()->glBindTextureFn(target, texture->service_id());
+      api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, texture->wrap_s());
+      api()->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, texture->wrap_t());
+      api()->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER,
+                               texture->min_filter());
+      api()->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER,
+                               texture->mag_filter());
+    }
+    raster_decoder_context_state_->PessimisticallyResetGrContext();
+  }
 }
 
 void RasterDecoderImpl::DoBindOrCopyTexImageIfNeeded(gles2::Texture* texture,
diff --git a/gpu/command_buffer/service/raster_decoder_autogen.h b/gpu/command_buffer/service/raster_decoder_autogen.h
index 8499f640..c0e399e 100644
--- a/gpu/command_buffer/service/raster_decoder_autogen.h
+++ b/gpu/command_buffer/service/raster_decoder_autogen.h
@@ -258,44 +258,29 @@
   return error::kNoError;
 }
 
-error::Error RasterDecoderImpl::HandleCreateAndConsumeTextureINTERNALImmediate(
+error::Error RasterDecoderImpl::HandleCopySubTextureINTERNALImmediate(
     uint32_t immediate_data_size,
     const volatile void* cmd_data) {
-  const volatile raster::cmds::CreateAndConsumeTextureINTERNALImmediate& c =
-      *static_cast<const volatile raster::cmds::
-                       CreateAndConsumeTextureINTERNALImmediate*>(cmd_data);
-  GLuint texture_id = static_cast<GLuint>(c.texture_id);
-  uint32_t mailbox_size;
-  if (!gles2::GLES2Util::ComputeDataSize<GLbyte, 16>(1, &mailbox_size)) {
-    return error::kOutOfBounds;
-  }
-  if (mailbox_size > immediate_data_size) {
-    return error::kOutOfBounds;
-  }
-  volatile const GLbyte* mailbox =
-      gles2::GetImmediateDataAs<volatile const GLbyte*>(c, mailbox_size,
-                                                        immediate_data_size);
-  if (mailbox == nullptr) {
-    return error::kOutOfBounds;
-  }
-  DoCreateAndConsumeTextureINTERNAL(texture_id, mailbox);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleCopySubTextureINTERNAL(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::CopySubTextureINTERNAL& c =
-      *static_cast<const volatile raster::cmds::CopySubTextureINTERNAL*>(
+  const volatile raster::cmds::CopySubTextureINTERNALImmediate& c =
+      *static_cast<
+          const volatile raster::cmds::CopySubTextureINTERNALImmediate*>(
           cmd_data);
-  GLuint source_id = static_cast<GLuint>(c.source_id);
-  GLuint dest_id = static_cast<GLuint>(c.dest_id);
   GLint xoffset = static_cast<GLint>(c.xoffset);
   GLint yoffset = static_cast<GLint>(c.yoffset);
   GLint x = static_cast<GLint>(c.x);
   GLint y = static_cast<GLint>(c.y);
   GLsizei width = static_cast<GLsizei>(c.width);
   GLsizei height = static_cast<GLsizei>(c.height);
+  uint32_t mailboxes_size;
+  if (!gles2::GLES2Util::ComputeDataSize<GLbyte, 32>(1, &mailboxes_size)) {
+    return error::kOutOfBounds;
+  }
+  if (mailboxes_size > immediate_data_size) {
+    return error::kOutOfBounds;
+  }
+  volatile const GLbyte* mailboxes =
+      gles2::GetImmediateDataAs<volatile const GLbyte*>(c, mailboxes_size,
+                                                        immediate_data_size);
   if (width < 0) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureINTERNAL",
                        "width < 0");
@@ -306,30 +291,10 @@
                        "height < 0");
     return error::kNoError;
   }
-  DoCopySubTextureINTERNAL(source_id, dest_id, xoffset, yoffset, x, y, width,
-                           height);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleDeleteTexturesINTERNALImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::DeleteTexturesINTERNALImmediate& c =
-      *static_cast<
-          const volatile raster::cmds::DeleteTexturesINTERNALImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t textures_size;
-  if (!gles2::SafeMultiplyUint32(n, sizeof(GLuint), &textures_size)) {
+  if (mailboxes == nullptr) {
     return error::kOutOfBounds;
   }
-  volatile const GLuint* textures =
-      gles2::GetImmediateDataAs<volatile const GLuint*>(c, textures_size,
-                                                        immediate_data_size);
-  if (textures == nullptr) {
-    return error::kOutOfBounds;
-  }
-  DeleteTexturesINTERNALHelper(n, textures);
+  DoCopySubTextureINTERNAL(xoffset, yoffset, x, y, width, height, mailboxes);
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/service/raster_decoder_mock.h b/gpu/command_buffer/service/raster_decoder_mock.h
index 4fc45fe..6c5424c 100644
--- a/gpu/command_buffer/service/raster_decoder_mock.h
+++ b/gpu/command_buffer/service/raster_decoder_mock.h
@@ -87,7 +87,7 @@
   MOCK_CONST_METHOD0(RestoreFramebufferBindings, void());
   MOCK_CONST_METHOD0(RestoreProgramBindings, void());
   MOCK_METHOD0(RestoreRenderbufferBindings, void());
-  MOCK_CONST_METHOD1(RestoreTextureState, void(unsigned service_id));
+  MOCK_METHOD1(RestoreTextureState, void(unsigned service_id));
   MOCK_CONST_METHOD1(RestoreTextureUnitBindings, void(unsigned unit));
   MOCK_METHOD1(RestoreVertexAttribArray, void(unsigned index));
 
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 72e3929..5e670b6 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -35,6 +35,17 @@
 namespace gpu {
 namespace raster {
 
+namespace {
+
+void CopyMailboxes(GLbyte (&output)[sizeof(Mailbox) * 2],
+                   const Mailbox& source,
+                   const Mailbox& dest) {
+  memcpy(output, source.name, sizeof(source.name));
+  memcpy(output + sizeof(source.name), dest.name, sizeof(dest.name));
+}
+
+}  // anonymous namespace
+
 class RasterDecoderTest : public RasterDecoderTestBase {
  public:
   RasterDecoderTest() = default;
@@ -139,10 +150,10 @@
 TEST_P(RasterDecoderTest, CopyTexSubImage2DTwiceClearsUnclearedTexture) {
   raster_decoder_context_state_->need_context_state_reset = true;
   // Create uninitialized source texture.
-  GLuint source_texture_id = kNewClientId;
-  CreateFakeTexture(source_texture_id, kNewServiceId,
-                    viz::ResourceFormat::RGBA_8888, /*width=*/2, /*height=*/2,
-                    /*cleared=*/false);
+  gpu::Mailbox source_texture_mailbox =
+      CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RGBA_8888,
+                        /*width=*/2, /*height=*/2,
+                        /*cleared=*/false);
 
   // This will initialize the top half of destination.
   {
@@ -152,9 +163,11 @@
                                   GL_TEXTURE_2D, GL_TEXTURE_2D, 0, GL_RGBA,
                                   GL_UNSIGNED_BYTE, 0, 0, 2, 2, 0);
     SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
-    CopySubTextureINTERNAL cmd;
-    cmd.Init(source_texture_id, client_texture_id_, 0, 0, 0, 0, 2, 1);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+    GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
+    CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
+    cmd.Init(0, 0, 0, 0, 2, 1, mailboxes);
+    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
   }
 
   // This will initialize bottom right corner of the destination.
@@ -165,15 +178,15 @@
                                   GL_TEXTURE_2D, GL_TEXTURE_2D, 0, GL_RGBA,
                                   GL_UNSIGNED_BYTE, 0, 1, 2, 1, 0);
     SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
-    CopySubTextureINTERNAL cmd;
-    cmd.Init(source_texture_id, client_texture_id_, 1, 1, 0, 0, 1, 1);
-    EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+    auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+    GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
+    CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
+    cmd.Init(1, 1, 0, 0, 1, 1, mailboxes);
+    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
   }
 
-  gles2::TextureManager* manager = group().texture_manager();
-  gles2::TextureRef* texture_ref = manager->GetTexture(client_texture_id_);
-  ASSERT_TRUE(texture_ref != nullptr);
-  gles2::Texture* texture = texture_ref->texture();
+  auto* texture = gles2::Texture::CheckedCast(
+      group().mailbox_manager()->ConsumeTexture(client_texture_mailbox_));
   EXPECT_TRUE(texture->SafeToRenderFrom());
 }
 
@@ -184,14 +197,16 @@
   InitDecoder(init);
 
   // Create dest texture.
-  GLuint dest_texture_id = kNewClientId;
-  CreateFakeTexture(dest_texture_id, kNewServiceId, viz::ResourceFormat::RED_8,
-                    /*width=*/2, /*height=*/2, /*cleared=*/true);
+  gpu::Mailbox dest_texture_mailbox =
+      CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RED_8,
+                        /*width=*/2, /*height=*/2, /*cleared=*/true);
 
   SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
-  CopySubTextureINTERNAL copy_cmd;
-  copy_cmd.Init(client_texture_id_, dest_texture_id, 0, 0, 0, 0, 2, 1);
-  EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd));
+  auto& copy_cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+  GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
+  CopyMailboxes(mailboxes, client_texture_mailbox_, dest_texture_mailbox);
+  copy_cmd.Init(0, 0, 0, 0, 2, 1, mailboxes);
+  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(copy_cmd, sizeof(mailboxes)));
   EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
 }
 
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index 372b282..6901924b 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -54,7 +54,6 @@
 RasterDecoderTestBase::RasterDecoderTestBase()
     : surface_(nullptr),
       context_(nullptr),
-      client_texture_id_(106),
       shared_memory_id_(0),
       shared_memory_offset_(0),
       shared_memory_address_(nullptr),
@@ -142,15 +141,14 @@
   }
 }
 
-void RasterDecoderTestBase::CreateFakeTexture(
-    GLuint client_id,
+gpu::Mailbox RasterDecoderTestBase::CreateFakeTexture(
     GLuint service_id,
     viz::ResourceFormat resource_format,
     GLsizei width,
     GLsizei height,
     bool cleared) {
   // Create texture and temporary ref.
-  const GLuint kTempClientId = 271828;
+  const GLuint kTempClientId = next_fake_texture_client_id_++;
   auto* temp_ref =
       group_->texture_manager()->CreateTexture(kTempClientId, service_id);
   group_->texture_manager()->SetTarget(temp_ref, GL_TEXTURE_2D);
@@ -161,22 +159,8 @@
       cleared ? gfx::Rect(width, height) : gfx::Rect());
   gpu::Mailbox mailbox = gpu::Mailbox::Generate();
   group_->mailbox_manager()->ProduceTexture(mailbox, temp_ref->texture());
-
-  // Consume texture to hold a permanent ref.
-  cmds::CreateAndConsumeTextureINTERNALImmediate& cmd =
-      *GetImmediateAs<cmds::CreateAndConsumeTextureINTERNALImmediate>();
-  cmd.Init(client_id, mailbox.name);
-  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailbox.name)));
-
-  // Check that client_texture_id has appropriate attributes.
-  auto* texture_ref = group().texture_manager()->GetTexture(client_id);
-  ASSERT_NE(texture_ref, nullptr);
-  auto* texture = texture_ref->texture();
-  EXPECT_EQ(service_id, texture->service_id());
-
-  // Release temporary ref.
-  group_->texture_manager()->RemoveTexture(kTempClientId);
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  return mailbox;
 }
 
 void RasterDecoderTestBase::InitDecoder(const InitState& init) {
@@ -286,9 +270,9 @@
   decoder_->MakeCurrent();
   decoder_->BeginDecoding();
 
-  CreateFakeTexture(client_texture_id_, kServiceTextureId,
-                    viz::ResourceFormat::RGBA_8888, /*width=*/2,
-                    /*height=*/2, /*cleared=*/false);
+  client_texture_mailbox_ = CreateFakeTexture(
+      kServiceTextureId, viz::ResourceFormat::RGBA_8888, /*width=*/2,
+      /*height=*/2, /*cleared=*/false);
 }
 
 void RasterDecoderTestBase::ResetDecoder() {
@@ -385,23 +369,6 @@
   ClearSharedMemory();
 }
 
-void RasterDecoderTestBase::DoDeleteTexture(GLuint client_id,
-                                            GLuint service_id) {
-  {
-    InSequence s;
-
-    // Calling DoDeleteTexture will unbind the texture from any texture units
-    // it's currently bound to.
-    EXPECT_CALL(*gl_, BindTexture(_, 0)).Times(AnyNumber());
-
-    EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(service_id)))
-        .Times(1)
-        .RetiresOnSaturation();
-
-    GenHelper<cmds::DeleteTexturesINTERNALImmediate>(client_id);
-  }
-}
-
 void RasterDecoderTestBase::SetScopedTextureBinderExpectations(GLenum target) {
   // ScopedTextureBinder
   EXPECT_CALL(*gl_, ActiveTexture(_))
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index bbcbfad1..bfe2a5f 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -180,12 +180,11 @@
   void SetupInitStateManualExpectationsForDoLineWidth(GLfloat width);
   void ExpectEnableDisable(GLenum cap, bool enable);
 
-  void CreateFakeTexture(GLuint client_id,
-                         GLuint service_id,
-                         viz::ResourceFormat resource_format,
-                         GLsizei width,
-                         GLsizei height,
-                         bool cleared);
+  gpu::Mailbox CreateFakeTexture(GLuint service_id,
+                                 viz::ResourceFormat resource_format,
+                                 GLsizei width,
+                                 GLsizei height,
+                                 bool cleared);
 
   // Note that the error is returned as GLint instead of GLenum.
   // This is because there is a mismatch in the types of GLenum and
@@ -196,7 +195,6 @@
   GLint GetGLError();
 
   void DoBindTexture(GLenum target, GLuint client_id, GLuint service_id);
-  void DoDeleteTexture(GLuint client_id, GLuint service_id);
   void SetScopedTextureBinderExpectations(GLenum target);
 
   void SetupClearTextureExpectations(GLuint service_id,
@@ -251,7 +249,7 @@
       command_buffer_service_for_mock_decoder_;
   std::unique_ptr<RasterDecoder> decoder_;
 
-  GLuint client_texture_id_;
+  gpu::Mailbox client_texture_mailbox_;
 
   int32_t shared_memory_id_;
   uint32_t shared_memory_offset_;
@@ -275,6 +273,7 @@
   scoped_refptr<gles2::ContextGroup> group_;
   base::MessageLoop message_loop_;
   gles2::MockCopyTextureResourceManager* copy_texture_manager_;  // not owned
+  GLuint next_fake_texture_client_id_ = 271828;
 };
 
 class RasterDecoderManualInitTest : public RasterDecoderTestBase {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
index 57bd17f..3c47d68 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
@@ -176,7 +176,7 @@
 TEST_P(RasterDecoderLostContextTest, TextureDestroyAfterLostFromMakeCurrent) {
   Init(/*has_robustness=*/true);
 
-  CreateFakeTexture(kNewClientId, kNewServiceId, viz::ResourceFormat::RGBA_8888,
+  CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RGBA_8888,
                     /*width=*/2, /*height=*/2,
                     /*cleared=*/false);
 
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 2768431d..f0d0b07 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -2211,6 +2211,13 @@
                                        GLint level) {
   DCHECK(ref);
   Texture* texture = ref->texture();
+  return ClearTextureLevel(decoder, texture, target, level);
+}
+
+bool TextureManager::ClearTextureLevel(DecoderContext* decoder,
+                                       Texture* texture,
+                                       GLenum target,
+                                       GLint level) {
   if (texture->num_uncleared_mips() == 0) {
     return true;
   }
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 2067d477..fe981ad 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -313,6 +313,11 @@
   // rectangle if level does not exist.
   gfx::Rect GetLevelClearedRect(GLenum target, GLint level) const;
 
+  // Marks a |rect| of a particular level as cleared.
+  void SetLevelClearedRect(GLenum target,
+                           GLint level,
+                           const gfx::Rect& cleared_rect);
+
   // Whether a particular level/face is cleared.
   bool IsLevelCleared(GLenum target, GLint level) const;
   // Whether a particular level/face is partially cleared.
@@ -465,11 +470,6 @@
     return npot_;
   }
 
-  // Marks a |rect| of a particular level as cleared.
-  void SetLevelClearedRect(GLenum target,
-                           GLint level,
-                           const gfx::Rect& cleared_rect);
-
   // Updates the cleared flag for this texture by inspecting all the mips.
   void UpdateCleared();
 
@@ -940,6 +940,11 @@
                          GLenum target,
                          GLint level);
 
+  bool ClearTextureLevel(DecoderContext* decoder,
+                         Texture* texture,
+                         GLenum target,
+                         GLint level);
+
   // Creates a new texture info.
   TextureRef* CreateTexture(GLuint client_id, GLuint service_id);
 
diff --git a/gpu/command_buffer/service/webgpu_decoder.cc b/gpu/command_buffer/service/webgpu_decoder.cc
index 50388ac..6e1cb4e 100644
--- a/gpu/command_buffer/service/webgpu_decoder.cc
+++ b/gpu/command_buffer/service/webgpu_decoder.cc
@@ -67,7 +67,7 @@
   void RestoreFramebufferBindings() const override { NOTREACHED(); }
   void RestoreRenderbufferBindings() override { NOTREACHED(); }
   void RestoreProgramBindings() const override { NOTREACHED(); }
-  void RestoreTextureState(unsigned service_id) const override { NOTREACHED(); }
+  void RestoreTextureState(unsigned service_id) override { NOTREACHED(); }
   void RestoreTextureUnitBindings(unsigned unit) const override {
     NOTREACHED();
   }
diff --git a/gpu/gles2_conform_support/egl/context.cc b/gpu/gles2_conform_support/egl/context.cc
index f5352c0..06f61d4c 100644
--- a/gpu/gles2_conform_support/egl/context.cc
+++ b/gpu/gles2_conform_support/egl/context.cc
@@ -354,11 +354,11 @@
   gl_context_ = nullptr;
 
   transfer_buffer_.reset();
+  gles2_cmd_helper_.reset();
+  command_buffer_.reset();
   if (decoder_)
     decoder_->Destroy(have_context);
   decoder_.reset();
-  gles2_cmd_helper_.reset();
-  command_buffer_.reset();
 }
 
 bool Context::HasService() const {
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index 81a6337c..9b92d764 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -39,9 +39,12 @@
 
 #if defined(CYGPROFILE_INSTRUMENTATION)
 const int kGpuTimeout = 30000;
-#elif defined(OS_WIN)
+#elif defined(OS_WIN) || defined(OS_MACOSX)
 // Use a slightly longer timeout on Windows due to prevalence of slow and
 // infected machines.
+
+// Also use a slightly longer timeout on MacOSX to get rid of GPU process
+// hangs at context creation during startup. See https://crbug.com/918490.
 const int kGpuTimeout = 15000;
 #else
 const int kGpuTimeout = 10000;
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index 9e910c5..5b69e428 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/buildflag_header.gni")
+import("//build/config/dcheck_always_on.gni")
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
 import("//testing/test.gni")
@@ -53,7 +54,15 @@
 
     data_deps = []
     if (is_fuchsia) {
-      data_deps += [ "//third_party/fuchsia-sdk:vulkan_layers" ]
+      data_deps += [ "//third_party/fuchsia-sdk:vulkan_base" ]
+
+      # VulkanInstance enables validation layer in Debug builds and when DCHECKs
+      # are enabled in Release builds. In these cases the validation layer
+      # libraries and configs need to be included in the generated Fuchsia
+      # package.
+      if (is_debug || dcheck_always_on) {
+        data_deps += [ "//third_party/fuchsia-sdk:vulkan_validation" ]
+      }
     }
   }
 
diff --git a/headless/lib/browser/protocol/browser_handler.cc b/headless/lib/browser/protocol/browser_handler.cc
index 455458c..026fdf6 100644
--- a/headless/lib/browser/protocol/browser_handler.cc
+++ b/headless/lib/browser/protocol/browser_handler.cc
@@ -78,7 +78,7 @@
 Response BrowserHandler::SetWindowBounds(
     int window_id,
     std::unique_ptr<Browser::Bounds> window_bounds) {
-  HeadlessWebContentsImpl* web_contents = web_contents =
+  HeadlessWebContentsImpl* web_contents =
       browser()->GetWebContentsForWindowId(window_id);
   if (!web_contents)
     return Response::Error("Browser window not found");
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index 332548b5..60099fa 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -288,8 +288,8 @@
   base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
       kTraceEventBrowserProcessSortIndex);
 
-  std::unique_ptr<content::BrowserMainRunner> browser_runner(
-      content::BrowserMainRunner::Create());
+  std::unique_ptr<content::BrowserMainRunner> browser_runner =
+      content::BrowserMainRunner::Create();
 
   int exit_code = browser_runner->Initialize(main_function_params);
   DCHECK_LT(exit_code, 0) << "content::BrowserMainRunner::Initialize failed in "
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 1920ec7..7d4db715 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -102,9 +102,9 @@
   triggers: "Cast Android (dbg)"
   triggers: "Cast Audio Linux"
   triggers: "Cast Linux"
-  triggers: "Chromium Android ARM 32-bit Goma RBE ToT"
-  triggers: "Chromium Android ARM 32-bit Goma RBE Staging"
   triggers: "Chromium Android ARM 32-bit Goma RBE Prod"
+  triggers: "Chromium Android ARM 32-bit Goma RBE Staging"
+  triggers: "Chromium Android ARM 32-bit Goma RBE ToT"
   triggers: "Chromium Linux Goma RBE Prod"
   triggers: "Chromium Linux Goma RBE Staging (clobber)"
   triggers: "Chromium Linux Goma RBE Staging (dbg) (clobber)"
@@ -4437,12 +4437,12 @@
 }
 
 job {
-  id: "Chromium Linux Goma RBE Prod"
+  id: "Chromium Linux Goma RBE ToT"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Chromium Linux Goma RBE Prod"
+    builder: "Chromium Linux Goma RBE ToT"
   }
 }
 
@@ -4487,12 +4487,12 @@
 }
 
 job {
-  id: "Chromium Linux Goma RBE ToT"
+  id: "Chromium Linux Goma RBE Prod"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
-    builder: "Chromium Linux Goma RBE ToT"
+    builder: "Chromium Linux Goma RBE Prod"
   }
 }
 
@@ -4507,16 +4507,6 @@
 }
 
 job {
-  id: "Chromium Mac Goma RBE Staging (clobber)"
-  acl_sets: "default"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Chromium Mac Goma RBE Staging (clobber)"
-  }
-}
-
-job {
   id: "Chromium Mac Goma RBE Staging"
   acl_sets: "default"
   buildbucket: {
@@ -4527,6 +4517,16 @@
 }
 
 job {
+  id: "Chromium Mac Goma RBE Staging (clobber)"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Chromium Mac Goma RBE Staging (clobber)"
+  }
+}
+
+job {
   id: "Chromium Mac Goma RBE Staging (dbg)"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index 293f4f01..fc96327 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -549,6 +549,7 @@
         xcode_path=xcode_path,
         xctest=False
     )
+    self.erase_all_simulators()
     self._init_sharding_data()
     self.logs = collections.OrderedDict()
     self.test_results = collections.OrderedDict()
@@ -619,3 +620,11 @@
     # or there are failures for last run.
     return not self.test_results['commands'][-1]['logs']['failed'] and all(
         [not r['logs']['errors'] for r in self.test_results['commands']])
+
+  def erase_all_simulators(self):
+    """Erases all simulator devices.
+
+    Fix for DVTCoreSimulatorAdditionsErrorDomain error.
+    """
+    print 'Erasing all simulators.'
+    subprocess.call(['xcrun', 'simctl', 'erase', 'all'])
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 9672ea8..552bba2 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -739,7 +739,7 @@
       ->NotifyEvent(feature_engagement::events::kChromeOpened);
 
   // Ensure the main tab model is created.
-  [_browserViewWrangler createMainTabModel];
+  [_browserViewWrangler createMainBrowser];
 
   _spotlightManager =
       [SpotlightManager spotlightManagerWithBrowserState:_mainBrowserState];
@@ -796,7 +796,11 @@
     // The startup parameters may create new tabs or navigations. If the restore
     // infobar is displayed now, it may be dismissed immediately and the user
     // will never be able to restore the session.
-    [_restoreHelper showRestoreIfNeeded:[self currentTabModel]];
+    TabModel* currentTabModel = [self currentTabModel];
+    [_restoreHelper
+        showRestoreIfNeededUsingWebState:currentTabModel.webStateList
+                                             ->GetActiveWebState()
+                         sessionRestorer:currentTabModel];
     _restoreHelper = nil;
   }
 
@@ -866,7 +870,7 @@
   // be destroyed.
   [_tabSwitcher setOtrTabModel:nil];
 
-  [_browserViewWrangler destroyAndRebuildIncognitoTabModel];
+  [_browserViewWrangler destroyAndRebuildIncognitoBrowser];
 
   if (otrBVCIsCurrent) {
     [self activateBVCAndMakeCurrentBVCPrimary];
@@ -2313,7 +2317,11 @@
     // Now that all the operations on the tabs have been done, display the
     // restore infobar if needed.
     dispatch_async(dispatch_get_main_queue(), ^{
-      [_restoreHelper showRestoreIfNeeded:[self currentTabModel]];
+      TabModel* currentTabModel = [self currentTabModel];
+      [_restoreHelper
+          showRestoreIfNeededUsingWebState:currentTabModel.webStateList
+                                               ->GetActiveWebState()
+                           sessionRestorer:currentTabModel];
       _restoreHelper = nil;
     });
   }
diff --git a/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.cc b/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
index e67501c..2dd0c2f 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
+++ b/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
@@ -42,7 +42,8 @@
 
 // static
 AutocompleteClassifierFactory* AutocompleteClassifierFactory::GetInstance() {
-  return base::Singleton<AutocompleteClassifierFactory>::get();
+  static base::NoDestructor<AutocompleteClassifierFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h b/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h
index 2e0f335..7963c9c3 100644
--- a/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h
+++ b/ios/chrome/browser/autocomplete/autocomplete_classifier_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class AutocompleteClassifier;
 
 namespace ios {
@@ -34,7 +30,7 @@
   static TestingFactory GetDefaultFactory();
 
  private:
-  friend struct base::DefaultSingletonTraits<AutocompleteClassifierFactory>;
+  friend class base::NoDestructor<AutocompleteClassifierFactory>;
 
   AutocompleteClassifierFactory();
   ~AutocompleteClassifierFactory() override;
diff --git a/ios/chrome/browser/autocomplete/in_memory_url_index_factory.cc b/ios/chrome/browser/autocomplete/in_memory_url_index_factory.cc
index 617edc9..5ee9c0c 100644
--- a/ios/chrome/browser/autocomplete/in_memory_url_index_factory.cc
+++ b/ios/chrome/browser/autocomplete/in_memory_url_index_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/omnibox/browser/in_memory_url_index.h"
@@ -51,7 +51,8 @@
 
 // static
 InMemoryURLIndexFactory* InMemoryURLIndexFactory::GetInstance() {
-  return base::Singleton<InMemoryURLIndexFactory>::get();
+  static base::NoDestructor<InMemoryURLIndexFactory> instance;
+  return instance.get();
 }
 
 InMemoryURLIndexFactory::InMemoryURLIndexFactory()
diff --git a/ios/chrome/browser/autocomplete/in_memory_url_index_factory.h b/ios/chrome/browser/autocomplete/in_memory_url_index_factory.h
index 9015437..183be2e 100644
--- a/ios/chrome/browser/autocomplete/in_memory_url_index_factory.h
+++ b/ios/chrome/browser/autocomplete/in_memory_url_index_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class InMemoryURLIndex;
 
 namespace ios {
@@ -34,7 +30,7 @@
   static TestingFactory GetDefaultFactory();
 
  private:
-  friend struct base::DefaultSingletonTraits<InMemoryURLIndexFactory>;
+  friend class base::NoDestructor<InMemoryURLIndexFactory>;
 
   InMemoryURLIndexFactory();
   ~InMemoryURLIndexFactory() override;
diff --git a/ios/chrome/browser/autocomplete/shortcuts_backend_factory.h b/ios/chrome/browser/autocomplete/shortcuts_backend_factory.h
index 68fda57a..0bdb2274 100644
--- a/ios/chrome/browser/autocomplete/shortcuts_backend_factory.h
+++ b/ios/chrome/browser/autocomplete/shortcuts_backend_factory.h
@@ -7,13 +7,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class ShortcutsBackend;
 
 namespace ios {
@@ -32,7 +28,7 @@
   static ShortcutsBackendFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<ShortcutsBackendFactory>;
+  friend class base::NoDestructor<ShortcutsBackendFactory>;
 
   ShortcutsBackendFactory();
   ~ShortcutsBackendFactory() override;
diff --git a/ios/chrome/browser/autocomplete/shortcuts_backend_factory.mm b/ios/chrome/browser/autocomplete/shortcuts_backend_factory.mm
index 8e394a2..04f697a1 100644
--- a/ios/chrome/browser/autocomplete/shortcuts_backend_factory.mm
+++ b/ios/chrome/browser/autocomplete/shortcuts_backend_factory.mm
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/omnibox/browser/shortcuts_backend.h"
@@ -58,7 +58,8 @@
 
 // static
 ShortcutsBackendFactory* ShortcutsBackendFactory::GetInstance() {
-  return base::Singleton<ShortcutsBackendFactory>::get();
+  static base::NoDestructor<ShortcutsBackendFactory> instance;
+  return instance.get();
 }
 
 ShortcutsBackendFactory::ShortcutsBackendFactory()
diff --git a/ios/chrome/browser/autofill/address_normalizer_factory.cc b/ios/chrome/browser/autofill/address_normalizer_factory.cc
index 73112c2..ae0881a 100644
--- a/ios/chrome/browser/autofill/address_normalizer_factory.cc
+++ b/ios/chrome/browser/autofill/address_normalizer_factory.cc
@@ -29,9 +29,8 @@
 
 // static
 AddressNormalizer* AddressNormalizerFactory::GetInstance() {
-  static base::LazyInstance<AddressNormalizerFactory>::DestructorAtExit
-      instance = LAZY_INSTANCE_INITIALIZER;
-  return &(instance.Get().address_normalizer_);
+  static base::NoDestructor<AddressNormalizerFactory> instance;
+  return &(instance->address_normalizer_);
 }
 
 AddressNormalizerFactory::AddressNormalizerFactory()
diff --git a/ios/chrome/browser/autofill/address_normalizer_factory.h b/ios/chrome/browser/autofill/address_normalizer_factory.h
index 7c0fac3..2e109d5 100644
--- a/ios/chrome/browser/autofill/address_normalizer_factory.h
+++ b/ios/chrome/browser/autofill/address_normalizer_factory.h
@@ -5,8 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_AUTOFILL_ADDRESS_NORMALIZER_FACTORY_H_
 #define IOS_CHROME_BROWSER_AUTOFILL_ADDRESS_NORMALIZER_FACTORY_H_
 
-#include "base/lazy_instance.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/address_normalizer_impl.h"
 
 namespace autofill {
@@ -17,7 +17,7 @@
   static AddressNormalizer* GetInstance();
 
  private:
-  friend struct base::LazyInstanceTraitsBase<AddressNormalizerFactory>;
+  friend class base::NoDestructor<AddressNormalizerFactory>;
 
   AddressNormalizerFactory();
   ~AddressNormalizerFactory();
diff --git a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
index 5304f89..2b044cc 100644
--- a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
+++ b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -33,7 +33,8 @@
 // static
 AutocompleteHistoryManagerFactory*
 AutocompleteHistoryManagerFactory::GetInstance() {
-  return base::Singleton<AutocompleteHistoryManagerFactory>::get();
+  static base::NoDestructor<AutocompleteHistoryManagerFactory> instance;
+  return instance.get();
 }
 
 AutocompleteHistoryManagerFactory::AutocompleteHistoryManagerFactory()
diff --git a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
index 7971aafe..fbb6ef49 100644
--- a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
+++ b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -33,7 +29,7 @@
   static AutocompleteHistoryManagerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<AutocompleteHistoryManagerFactory>;
+  friend class base::NoDestructor<AutocompleteHistoryManagerFactory>;
 
   AutocompleteHistoryManagerFactory();
   ~AutocompleteHistoryManagerFactory() override;
diff --git a/ios/chrome/browser/autofill/autofill_profile_validator_factory.cc b/ios/chrome/browser/autofill/autofill_profile_validator_factory.cc
index 7759583..f07f4ff 100644
--- a/ios/chrome/browser/autofill/autofill_profile_validator_factory.cc
+++ b/ios/chrome/browser/autofill/autofill_profile_validator_factory.cc
@@ -15,9 +15,8 @@
 namespace autofill {
 
 AutofillProfileValidator* AutofillProfileValidatorFactory::GetInstance() {
-  static base::LazyInstance<AutofillProfileValidatorFactory>::DestructorAtExit
-      instance = LAZY_INSTANCE_INITIALIZER;
-  return &(instance.Get().autofill_profile_validator_);
+  static base::NoDestructor<AutofillProfileValidatorFactory> instance;
+  return &(instance->autofill_profile_validator_);
 }
 
 AutofillProfileValidatorFactory::AutofillProfileValidatorFactory()
diff --git a/ios/chrome/browser/autofill/autofill_profile_validator_factory.h b/ios/chrome/browser/autofill/autofill_profile_validator_factory.h
index 8d6ae4fc..3001ac4c 100644
--- a/ios/chrome/browser/autofill/autofill_profile_validator_factory.h
+++ b/ios/chrome/browser/autofill/autofill_profile_validator_factory.h
@@ -5,8 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_AUTOFILL_AUTOFILL_PROFILE_VALIDATOR_FACTORY_H_
 #define IOS_CHROME_BROWSER_AUTOFILL_AUTOFILL_PROFILE_VALIDATOR_FACTORY_H_
 
-#include "base/lazy_instance.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/autofill_profile_validator.h"
 
 namespace autofill {
@@ -17,7 +17,7 @@
   static AutofillProfileValidator* GetInstance();
 
  private:
-  friend struct base::LazyInstanceTraitsBase<AutofillProfileValidatorFactory>;
+  friend class base::NoDestructor<AutofillProfileValidatorFactory>;
 
   AutofillProfileValidatorFactory();
   ~AutofillProfileValidatorFactory();
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index cfa0682..e6dea1c3 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -335,10 +335,32 @@
   return nil;
 }
 
+// Returns YES if one the first level of children has "Picker" in their class
+// name. No otherwise.
+- (BOOL)containsPickerView:(UIView*)view {
+  for (UIView* subview in view.subviews) {
+    if ([NSStringFromClass([subview class]) rangeOfString:@"Picker"].location !=
+        NSNotFound) {
+      return YES;
+    }
+  }
+  return NO;
+}
+
 - (void)keyboardWillOrDidChangeFrame:(NSNotification*)notification {
   CGRect keyboardFrame =
       [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
   UIView* keyboardView = [self getKeyboardView];
+  // On iPhones when the field is a selector the keyboard becomes a picker.
+  // Restore the keyboard in these cases, but allow the user to return to see
+  // the info in Manual Fallback.
+  if (!IsIPadIdiom() && [self containsPickerView:keyboardView]) {
+    [self.manualFillAccessoryViewController resetAnimated:NO];
+    [self unlockManualFallbackView];
+    [self.keyboardReplacementView removeFromSuperview];
+    self.keyboardReplacementView = nil;
+    return;
+  }
   CGRect windowRect = keyboardView.window.bounds;
   // On iPad when the keyboard is undocked, on iOS 11 and 12,
   // `UIKeyboard*HideNotification` or `UIKeyboard*ShowNotification` are not
diff --git a/ios/chrome/browser/autofill/legacy_strike_database_factory.cc b/ios/chrome/browser/autofill/legacy_strike_database_factory.cc
index 057c6b5..ca054d3e 100644
--- a/ios/chrome/browser/autofill/legacy_strike_database_factory.cc
+++ b/ios/chrome/browser/autofill/legacy_strike_database_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/legacy_strike_database.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/application_context.h"
@@ -23,7 +23,8 @@
 
 // static
 LegacyStrikeDatabaseFactory* LegacyStrikeDatabaseFactory::GetInstance() {
-  return base::Singleton<LegacyStrikeDatabaseFactory>::get();
+  static base::NoDestructor<LegacyStrikeDatabaseFactory> instance;
+  return instance.get();
 }
 
 LegacyStrikeDatabaseFactory::LegacyStrikeDatabaseFactory()
diff --git a/ios/chrome/browser/autofill/legacy_strike_database_factory.h b/ios/chrome/browser/autofill/legacy_strike_database_factory.h
index 4f56462..56ca6f46 100644
--- a/ios/chrome/browser/autofill/legacy_strike_database_factory.h
+++ b/ios/chrome/browser/autofill/legacy_strike_database_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,7 +28,7 @@
   static LegacyStrikeDatabaseFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<LegacyStrikeDatabaseFactory>;
+  friend class base::NoDestructor<LegacyStrikeDatabaseFactory>;
 
   LegacyStrikeDatabaseFactory();
   ~LegacyStrikeDatabaseFactory() override;
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.cc b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
index ebc4047..986d3784 100644
--- a/ios/chrome/browser/autofill/personal_data_manager_factory.cc
+++ b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -31,7 +31,8 @@
 
 // static
 PersonalDataManagerFactory* PersonalDataManagerFactory::GetInstance() {
-  return base::Singleton<PersonalDataManagerFactory>::get();
+  static base::NoDestructor<PersonalDataManagerFactory> instance;
+  return instance.get();
 }
 
 PersonalDataManagerFactory::PersonalDataManagerFactory()
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.h b/ios/chrome/browser/autofill/personal_data_manager_factory.h
index 75ab40a..fc08227 100644
--- a/ios/chrome/browser/autofill/personal_data_manager_factory.h
+++ b/ios/chrome/browser/autofill/personal_data_manager_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,7 +28,7 @@
   static PersonalDataManagerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<PersonalDataManagerFactory>;
+  friend class base::NoDestructor<PersonalDataManagerFactory>;
 
   PersonalDataManagerFactory();
   ~PersonalDataManagerFactory() override;
diff --git a/ios/chrome/browser/autofill/strike_database_factory.cc b/ios/chrome/browser/autofill/strike_database_factory.cc
index 6e5d6f9a..49ca9fa 100644
--- a/ios/chrome/browser/autofill/strike_database_factory.cc
+++ b/ios/chrome/browser/autofill/strike_database_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/strike_database.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/application_context.h"
@@ -23,7 +23,8 @@
 
 // static
 StrikeDatabaseFactory* StrikeDatabaseFactory::GetInstance() {
-  return base::Singleton<StrikeDatabaseFactory>::get();
+  static base::NoDestructor<StrikeDatabaseFactory> instance;
+  return instance.get();
 }
 
 StrikeDatabaseFactory::StrikeDatabaseFactory()
diff --git a/ios/chrome/browser/autofill/strike_database_factory.h b/ios/chrome/browser/autofill/strike_database_factory.h
index 6822440..8562185 100644
--- a/ios/chrome/browser/autofill/strike_database_factory.h
+++ b/ios/chrome/browser/autofill/strike_database_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,7 +28,7 @@
   static StrikeDatabaseFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<StrikeDatabaseFactory>;
+  friend class base::NoDestructor<StrikeDatabaseFactory>;
 
   StrikeDatabaseFactory();
   ~StrikeDatabaseFactory() override;
diff --git a/ios/chrome/browser/autofill/validation_rules_storage_factory.cc b/ios/chrome/browser/autofill/validation_rules_storage_factory.cc
index 0763dd08..64b687a 100644
--- a/ios/chrome/browser/autofill/validation_rules_storage_factory.cc
+++ b/ios/chrome/browser/autofill/validation_rules_storage_factory.cc
@@ -16,10 +16,9 @@
 
 // static
 std::unique_ptr<Storage> ValidationRulesStorageFactory::CreateStorage() {
-  static base::LazyInstance<ValidationRulesStorageFactory>::DestructorAtExit
-      instance = LAZY_INSTANCE_INITIALIZER;
+  static base::NoDestructor<ValidationRulesStorageFactory> instance;
   return std::unique_ptr<Storage>(
-      new ChromeStorageImpl(instance.Get().json_pref_store_.get()));
+      new ChromeStorageImpl(instance->json_pref_store_.get()));
 }
 
 ValidationRulesStorageFactory::ValidationRulesStorageFactory() {
diff --git a/ios/chrome/browser/autofill/validation_rules_storage_factory.h b/ios/chrome/browser/autofill/validation_rules_storage_factory.h
index 6277ee6..90653943 100644
--- a/ios/chrome/browser/autofill/validation_rules_storage_factory.h
+++ b/ios/chrome/browser/autofill/validation_rules_storage_factory.h
@@ -7,9 +7,9 @@
 
 #include <memory>
 
-#include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 
 namespace i18n {
 namespace addressinput {
@@ -30,7 +30,7 @@
   static std::unique_ptr<::i18n::addressinput::Storage> CreateStorage();
 
  private:
-  friend struct base::LazyInstanceTraitsBase<ValidationRulesStorageFactory>;
+  friend class base::NoDestructor<ValidationRulesStorageFactory>;
 
   ValidationRulesStorageFactory();
   ~ValidationRulesStorageFactory();
diff --git a/ios/chrome/browser/bookmarks/bookmark_model_factory.cc b/ios/chrome/browser/bookmarks/bookmark_model_factory.cc
index d3332b99..7560702 100644
--- a/ios/chrome/browser/bookmarks/bookmark_model_factory.cc
+++ b/ios/chrome/browser/bookmarks/bookmark_model_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 
 #include <utility>
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
@@ -41,7 +41,8 @@
 
 // static
 BookmarkModelFactory* BookmarkModelFactory::GetInstance() {
-  return base::Singleton<BookmarkModelFactory>::get();
+  static base::NoDestructor<BookmarkModelFactory> instance;
+  return instance.get();
 }
 
 BookmarkModelFactory::BookmarkModelFactory()
diff --git a/ios/chrome/browser/bookmarks/bookmark_model_factory.h b/ios/chrome/browser/bookmarks/bookmark_model_factory.h
index a1e21ec..28c8184 100644
--- a/ios/chrome/browser/bookmarks/bookmark_model_factory.h
+++ b/ios/chrome/browser/bookmarks/bookmark_model_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace bookmarks {
 class BookmarkModel;
 }
@@ -34,7 +30,7 @@
   static BookmarkModelFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<BookmarkModelFactory>;
+  friend class base::NoDestructor<BookmarkModelFactory>;
 
   BookmarkModelFactory();
   ~BookmarkModelFactory() override;
diff --git a/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.cc b/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.cc
index 672d50e..4442362 100644
--- a/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.cc
+++ b/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.cc
@@ -22,7 +22,8 @@
 
 // static
 BookmarkSyncServiceFactory* BookmarkSyncServiceFactory::GetInstance() {
-  return base::Singleton<BookmarkSyncServiceFactory>::get();
+  static base::NoDestructor<BookmarkSyncServiceFactory> instance;
+  return instance.get();
 }
 
 BookmarkSyncServiceFactory::BookmarkSyncServiceFactory()
diff --git a/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h b/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h
index 984d8f1..428609a 100644
--- a/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h
+++ b/ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h
@@ -5,14 +5,10 @@
 #ifndef IOS_CHROME_BROWSER_BOOKMARKS_BOOKMARK_SYNC_SERVICE_FACTORY_H_
 #define IOS_CHROME_BROWSER_BOOKMARKS_BOOKMARK_SYNC_SERVICE_FACTORY_H_
 
-#include "base/memory/singleton.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace sync_bookmarks {
 class BookmarkSyncService;
 }
@@ -33,7 +29,7 @@
   static BookmarkSyncServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<BookmarkSyncServiceFactory>;
+  friend class base::NoDestructor<BookmarkSyncServiceFactory>;
 
   BookmarkSyncServiceFactory();
   ~BookmarkSyncServiceFactory() override;
diff --git a/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.cc b/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.cc
index b9e69aa..2b3878c 100644
--- a/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.cc
+++ b/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/sequenced_task_runner.h"
 #include "components/bookmarks/browser/startup_task_runner_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
@@ -23,7 +23,8 @@
 // static
 StartupTaskRunnerServiceFactory*
 StartupTaskRunnerServiceFactory::GetInstance() {
-  return base::Singleton<StartupTaskRunnerServiceFactory>::get();
+  static base::NoDestructor<StartupTaskRunnerServiceFactory> instance;
+  return instance.get();
 }
 
 StartupTaskRunnerServiceFactory::StartupTaskRunnerServiceFactory()
diff --git a/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h b/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h
index c50a8724..01441333 100644
--- a/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h
+++ b/ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace bookmarks {
 class StartupTaskRunnerService;
 }
@@ -32,7 +28,7 @@
   static StartupTaskRunnerServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<StartupTaskRunnerServiceFactory>;
+  friend class base::NoDestructor<StartupTaskRunnerServiceFactory>;
 
   StartupTaskRunnerServiceFactory();
   ~StartupTaskRunnerServiceFactory() override;
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_factory.h b/ios/chrome/browser/browsing_data/browsing_data_remover_factory.h
index 10e5e03..3c26801a 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_factory.h
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,7 +28,7 @@
   static BrowsingDataRemoverFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<BrowsingDataRemoverFactory>;
+  friend class base::NoDestructor<BrowsingDataRemoverFactory>;
 
   BrowsingDataRemoverFactory();
   ~BrowsingDataRemoverFactory() override;
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_factory.mm b/ios/chrome/browser/browsing_data/browsing_data_remover_factory.mm
index 0b086051..133ae22 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_factory.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_factory.mm
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -33,7 +33,8 @@
 
 // static
 BrowsingDataRemoverFactory* BrowsingDataRemoverFactory::GetInstance() {
-  return base::Singleton<BrowsingDataRemoverFactory>::get();
+  static base::NoDestructor<BrowsingDataRemoverFactory> instance;
+  return instance.get();
 }
 
 BrowsingDataRemoverFactory::BrowsingDataRemoverFactory()
diff --git a/ios/chrome/browser/content_settings/cookie_settings_factory.cc b/ios/chrome/browser/content_settings/cookie_settings_factory.cc
index 89fa6bc..ae3ed5b 100644
--- a/ios/chrome/browser/content_settings/cookie_settings_factory.cc
+++ b/ios/chrome/browser/content_settings/cookie_settings_factory.cc
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
@@ -23,7 +23,8 @@
 
 // static
 CookieSettingsFactory* CookieSettingsFactory::GetInstance() {
-  return base::Singleton<CookieSettingsFactory>::get();
+  static base::NoDestructor<CookieSettingsFactory> instance;
+  return instance.get();
 }
 
 CookieSettingsFactory::CookieSettingsFactory()
diff --git a/ios/chrome/browser/content_settings/cookie_settings_factory.h b/ios/chrome/browser/content_settings/cookie_settings_factory.h
index 1b4f8e6..5172964 100644
--- a/ios/chrome/browser/content_settings/cookie_settings_factory.h
+++ b/ios/chrome/browser/content_settings/cookie_settings_factory.h
@@ -7,13 +7,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace content_settings {
 class CookieSettings;
 }
@@ -31,7 +27,7 @@
   static CookieSettingsFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<CookieSettingsFactory>;
+  friend class base::NoDestructor<CookieSettingsFactory>;
 
   CookieSettingsFactory();
   ~CookieSettingsFactory() override;
diff --git a/ios/chrome/browser/content_settings/host_content_settings_map_factory.cc b/ios/chrome/browser/content_settings/host_content_settings_map_factory.cc
index 7aae358e..713fbd97 100644
--- a/ios/chrome/browser/content_settings/host_content_settings_map_factory.cc
+++ b/ios/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/prefs/pref_service.h"
@@ -22,7 +22,8 @@
 
 // static
 HostContentSettingsMapFactory* HostContentSettingsMapFactory::GetInstance() {
-  return base::Singleton<HostContentSettingsMapFactory>::get();
+  static base::NoDestructor<HostContentSettingsMapFactory> instance;
+  return instance.get();
 }
 
 HostContentSettingsMapFactory::HostContentSettingsMapFactory()
diff --git a/ios/chrome/browser/content_settings/host_content_settings_map_factory.h b/ios/chrome/browser/content_settings/host_content_settings_map_factory.h
index 82f76a9..8ee83903 100644
--- a/ios/chrome/browser/content_settings/host_content_settings_map_factory.h
+++ b/ios/chrome/browser/content_settings/host_content_settings_map_factory.h
@@ -7,15 +7,11 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
 
 class HostContentSettingsMap;
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace ios {
 
 class ChromeBrowserState;
@@ -30,7 +26,7 @@
   static HostContentSettingsMapFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<HostContentSettingsMapFactory>;
+  friend class base::NoDestructor<HostContentSettingsMapFactory>;
 
   HostContentSettingsMapFactory();
   ~HostContentSettingsMapFactory() override;
diff --git a/ios/chrome/browser/crash_report/crash_restore_helper.h b/ios/chrome/browser/crash_report/crash_restore_helper.h
index 51d4719a..79ed883 100644
--- a/ios/chrome/browser/crash_report/crash_restore_helper.h
+++ b/ios/chrome/browser/crash_report/crash_restore_helper.h
@@ -11,12 +11,15 @@
 class ChromeBrowserState;
 }
 
-@class TabModel;
+@protocol SessionWindowRestoring;
+namespace web {
+class WebState;
+}
 
 // Helper class for handling session restoration after a crash.
 @interface CrashRestoreHelper : NSObject
 
-- (id)initWithBrowserState:(ios::ChromeBrowserState*)browserState;
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState;
 
 // Saves the session information stored on disk in temporary files and will
 // then delete those from their default location. This will ensure that the
@@ -26,7 +29,8 @@
 
 // Shows an infobar on the currently selected tab of the given |tabModel|. This
 // infobar lets the user restore its session after a crash.
-- (void)showRestoreIfNeeded:(TabModel*)tabModel;
+- (void)showRestoreIfNeededUsingWebState:(web::WebState*)webState
+                         sessionRestorer:(id<SessionWindowRestoring>)restorer;
 
 @end
 
diff --git a/ios/chrome/browser/crash_report/crash_restore_helper.mm b/ios/chrome/browser/crash_report/crash_restore_helper.mm
index 04e8f97..05e2d86 100644
--- a/ios/chrome/browser/crash_report/crash_restore_helper.mm
+++ b/ios/chrome/browser/crash_report/crash_restore_helper.mm
@@ -24,10 +24,10 @@
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
-#import "ios/chrome/browser/tabs/tab.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/sessions/session_window_restoring.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
+#import "ios/web/public/web_state/web_state.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -174,8 +174,8 @@
   ios::ChromeBrowserState* _browserState;
   BOOL _needRestoration;
   std::unique_ptr<InfoBarManagerObserverBridge> _infoBarBridge;
-  // The TabModel to restore sessions to.
-  TabModel* _tabModel;
+  // Object that will handle session restoration.
+  id<SessionWindowRestoring> _restorer;
 
   // Indicate that the session has been restored to tabs or to recently closed
   // and should not be rerestored.
@@ -189,19 +189,19 @@
   return self;
 }
 
-- (void)showRestoreIfNeeded:(TabModel*)tabModel {
+- (void)showRestoreIfNeededUsingWebState:(web::WebState*)webState
+                         sessionRestorer:(id<SessionWindowRestoring>)restorer {
   if (!_needRestoration)
     return;
 
   // The last session didn't exit cleanly. Show an infobar to the user so
   // that they can restore if they want. The delegate deletes itself when
   // it is closed.
-  DCHECK(tabModel);
-  web::WebState* webState = tabModel.webStateList->GetActiveWebState();
+
   DCHECK(webState);
   infobars::InfoBarManager* infoBarManager =
       InfoBarManagerImpl::FromWebState(webState);
-  _tabModel = tabModel;
+  _restorer = restorer;
   SessionCrashedInfoBarDelegate::Create(infoBarManager, self);
   _infoBarBridge.reset(new InfoBarManagerObserverBridge(infoBarManager, self));
 }
@@ -274,7 +274,7 @@
 
   DCHECK_EQ(session.sessionWindows.count, 1u);
   breakpad_helper::WillStartCrashRestoration();
-  return [_tabModel restoreSessionWindow:session.sessionWindows[0]];
+  return [_restorer restoreSessionWindow:session.sessionWindows[0]];
 }
 
 - (void)infoBarRemoved:(infobars::InfoBar*)infobar {
diff --git a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
index 65492d2..56035e5 100644
--- a/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
+++ b/ios/chrome/browser/dom_distiller/dom_distiller_service_factory.cc
@@ -16,8 +16,8 @@
 #include "components/dom_distiller/ios/distiller_page_factory_ios.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "components/leveldb_proto/proto_database.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/web/public/browser_state.h"
diff --git a/ios/chrome/browser/download/browser_download_service_factory.h b/ios/chrome/browser/download/browser_download_service_factory.h
index cf66a8e..ae55d70 100644
--- a/ios/chrome/browser/download/browser_download_service_factory.h
+++ b/ios/chrome/browser/download/browser_download_service_factory.h
@@ -6,15 +6,11 @@
 #define IOS_CHROME_BROWSER_DOWNLOAD_BROWSER_DOWNLOAD_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 class BrowserDownloadService;
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace web {
 class BrowserState;
 }  // namespace web
@@ -28,7 +24,7 @@
   static BrowserDownloadServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<BrowserDownloadServiceFactory>;
+  friend class base::NoDestructor<BrowserDownloadServiceFactory>;
 
   BrowserDownloadServiceFactory();
   ~BrowserDownloadServiceFactory() override;
diff --git a/ios/chrome/browser/download/browser_download_service_factory.mm b/ios/chrome/browser/download/browser_download_service_factory.mm
index e0c9465b..c13702c 100644
--- a/ios/chrome/browser/download/browser_download_service_factory.mm
+++ b/ios/chrome/browser/download/browser_download_service_factory.mm
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/download/browser_download_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/download/browser_download_service.h"
@@ -23,7 +23,8 @@
 
 // static
 BrowserDownloadServiceFactory* BrowserDownloadServiceFactory::GetInstance() {
-  return base::Singleton<BrowserDownloadServiceFactory>::get();
+  static base::NoDestructor<BrowserDownloadServiceFactory> instance;
+  return instance.get();
 }
 
 BrowserDownloadServiceFactory::BrowserDownloadServiceFactory()
diff --git a/ios/chrome/browser/google/google_logo_service_factory.h b/ios/chrome/browser/google/google_logo_service_factory.h
index 067e5a3..9688273 100644
--- a/ios/chrome/browser/google/google_logo_service_factory.h
+++ b/ios/chrome/browser/google/google_logo_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,7 +28,7 @@
   static GoogleLogoServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<GoogleLogoServiceFactory>;
+  friend class base::NoDestructor<GoogleLogoServiceFactory>;
 
   GoogleLogoServiceFactory();
   ~GoogleLogoServiceFactory() override;
diff --git a/ios/chrome/browser/google/google_logo_service_factory.mm b/ios/chrome/browser/google/google_logo_service_factory.mm
index b73904a..e25ebce 100644
--- a/ios/chrome/browser/google/google_logo_service_factory.mm
+++ b/ios/chrome/browser/google/google_logo_service_factory.mm
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/google/google_logo_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -26,7 +26,8 @@
 
 // static
 GoogleLogoServiceFactory* GoogleLogoServiceFactory::GetInstance() {
-  return base::Singleton<GoogleLogoServiceFactory>::get();
+  static base::NoDestructor<GoogleLogoServiceFactory> instance;
+  return instance.get();
 }
 
 GoogleLogoServiceFactory::GoogleLogoServiceFactory()
diff --git a/ios/chrome/browser/google/google_url_tracker_factory.cc b/ios/chrome/browser/google/google_url_tracker_factory.cc
index f62da15..fd9c1dd0 100644
--- a/ios/chrome/browser/google/google_url_tracker_factory.cc
+++ b/ios/chrome/browser/google/google_url_tracker_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/google/google_url_tracker_factory.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/google/core/browser/google_pref_names.h"
 #include "components/google/core/browser/google_url_tracker.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
@@ -26,7 +26,8 @@
 
 // static
 GoogleURLTrackerFactory* GoogleURLTrackerFactory::GetInstance() {
-  return base::Singleton<GoogleURLTrackerFactory>::get();
+  static base::NoDestructor<GoogleURLTrackerFactory> instance;
+  return instance.get();
 }
 
 GoogleURLTrackerFactory::GoogleURLTrackerFactory()
diff --git a/ios/chrome/browser/google/google_url_tracker_factory.h b/ios/chrome/browser/google/google_url_tracker_factory.h
index 2ee6d020..37111c4 100644
--- a/ios/chrome/browser/google/google_url_tracker_factory.h
+++ b/ios/chrome/browser/google/google_url_tracker_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace bas
-
 class GoogleURLTracker;
 
 namespace ios {
@@ -30,7 +26,7 @@
   static GoogleURLTrackerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<GoogleURLTrackerFactory>;
+  friend class base::NoDestructor<GoogleURLTrackerFactory>;
 
   GoogleURLTrackerFactory();
   ~GoogleURLTrackerFactory() override;
diff --git a/ios/chrome/browser/history/history_service_factory.cc b/ios/chrome/browser/history/history_service_factory.cc
index b77ffcd..240ab8d 100644
--- a/ios/chrome/browser/history/history_service_factory.cc
+++ b/ios/chrome/browser/history/history_service_factory.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/visit_delegate.h"
@@ -54,7 +54,8 @@
 
 // static
 HistoryServiceFactory* HistoryServiceFactory::GetInstance() {
-  return base::Singleton<HistoryServiceFactory>::get();
+  static base::NoDestructor<HistoryServiceFactory> instance;
+  return instance.get();
 }
 
 HistoryServiceFactory::HistoryServiceFactory()
diff --git a/ios/chrome/browser/history/history_service_factory.h b/ios/chrome/browser/history/history_service_factory.h
index c6f2e47..57c1a61 100644
--- a/ios/chrome/browser/history/history_service_factory.h
+++ b/ios/chrome/browser/history/history_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 enum class ServiceAccessType;
 
 namespace history {
@@ -38,7 +34,7 @@
   static HistoryServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<HistoryServiceFactory>;
+  friend class base::NoDestructor<HistoryServiceFactory>;
 
   HistoryServiceFactory();
   ~HistoryServiceFactory() override;
diff --git a/ios/chrome/browser/history/history_tab_helper.mm b/ios/chrome/browser/history/history_tab_helper.mm
index b0c3637..80a5c5a9 100644
--- a/ios/chrome/browser/history/history_tab_helper.mm
+++ b/ios/chrome/browser/history/history_tab_helper.mm
@@ -105,13 +105,8 @@
     return;
   }
 
-  if (navigation_context->IsDownload()) {
-    return;
-  }
-
-  if (!navigation_context->HasCommitted() &&
-      !navigation_context->IsSameDocument()) {
-    // Navigation was replaced.
+  if (!navigation_context->HasCommitted()) {
+    // Navigation was replaced or aborted.
     return;
   }
 
diff --git a/ios/chrome/browser/history/top_sites_factory.cc b/ios/chrome/browser/history/top_sites_factory.cc
index 09a091f..223d86ee 100644
--- a/ios/chrome/browser/history/top_sites_factory.cc
+++ b/ios/chrome/browser/history/top_sites_factory.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/history/core/browser/default_top_sites_provider.h"
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/top_sites_impl.h"
@@ -30,7 +30,8 @@
 
 // static
 TopSitesFactory* TopSitesFactory::GetInstance() {
-  return base::Singleton<TopSitesFactory>::get();
+  static base::NoDestructor<TopSitesFactory> instance;
+  return instance.get();
 }
 
 TopSitesFactory::TopSitesFactory()
diff --git a/ios/chrome/browser/history/top_sites_factory.h b/ios/chrome/browser/history/top_sites_factory.h
index a84d600..47b38fa3 100644
--- a/ios/chrome/browser/history/top_sites_factory.h
+++ b/ios/chrome/browser/history/top_sites_factory.h
@@ -7,11 +7,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
 
 namespace history {
 class TopSites;
@@ -30,7 +27,7 @@
   static TopSitesFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<TopSitesFactory>;
+  friend class base::NoDestructor<TopSitesFactory>;
 
   TopSitesFactory();
   ~TopSitesFactory() override;
diff --git a/ios/chrome/browser/history/web_history_service_factory.cc b/ios/chrome/browser/history/web_history_service_factory.cc
index 2c15c96..d0fd8a8 100644
--- a/ios/chrome/browser/history/web_history_service_factory.cc
+++ b/ios/chrome/browser/history/web_history_service_factory.cc
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/history/web_history_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/history/core/browser/web_history_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
@@ -45,7 +45,8 @@
 
 // static
 WebHistoryServiceFactory* WebHistoryServiceFactory::GetInstance() {
-  return base::Singleton<WebHistoryServiceFactory>::get();
+  static base::NoDestructor<WebHistoryServiceFactory> instance;
+  return instance.get();
 }
 
 WebHistoryServiceFactory::WebHistoryServiceFactory()
diff --git a/ios/chrome/browser/history/web_history_service_factory.h b/ios/chrome/browser/history/web_history_service_factory.h
index 7207ab6..7281de46 100644
--- a/ios/chrome/browser/history/web_history_service_factory.h
+++ b/ios/chrome/browser/history/web_history_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace history {
 class WebHistoryService;
 }
@@ -32,7 +28,7 @@
   static WebHistoryServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<WebHistoryServiceFactory>;
+  friend class base::NoDestructor<WebHistoryServiceFactory>;
 
   WebHistoryServiceFactory();
   ~WebHistoryServiceFactory() override;
diff --git a/ios/chrome/browser/language/language_model_manager_factory.cc b/ios/chrome/browser/language/language_model_manager_factory.cc
index 56cb040..e98cfb8 100644
--- a/ios/chrome/browser/language/language_model_manager_factory.cc
+++ b/ios/chrome/browser/language/language_model_manager_factory.cc
@@ -66,7 +66,8 @@
 
 // static
 LanguageModelManagerFactory* LanguageModelManagerFactory::GetInstance() {
-  return base::Singleton<LanguageModelManagerFactory>::get();
+  static base::NoDestructor<LanguageModelManagerFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/language/language_model_manager_factory.h b/ios/chrome/browser/language/language_model_manager_factory.h
index 66407ee..58ee713 100644
--- a/ios/chrome/browser/language/language_model_manager_factory.h
+++ b/ios/chrome/browser/language/language_model_manager_factory.h
@@ -7,7 +7,8 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
@@ -32,7 +33,7 @@
       ios::ChromeBrowserState* browser_state);
 
  private:
-  friend struct base::DefaultSingletonTraits<LanguageModelManagerFactory>;
+  friend class base::NoDestructor<LanguageModelManagerFactory>;
 
   LanguageModelManagerFactory();
   ~LanguageModelManagerFactory() override;
diff --git a/ios/chrome/browser/language/url_language_histogram_factory.cc b/ios/chrome/browser/language/url_language_histogram_factory.cc
index f91e2b1..adf2779 100644
--- a/ios/chrome/browser/language/url_language_histogram_factory.cc
+++ b/ios/chrome/browser/language/url_language_histogram_factory.cc
@@ -12,7 +12,8 @@
 
 // static
 UrlLanguageHistogramFactory* UrlLanguageHistogramFactory::GetInstance() {
-  return base::Singleton<UrlLanguageHistogramFactory>::get();
+  static base::NoDestructor<UrlLanguageHistogramFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/language/url_language_histogram_factory.h b/ios/chrome/browser/language/url_language_histogram_factory.h
index 62f6c9a..ad94ab2 100644
--- a/ios/chrome/browser/language/url_language_histogram_factory.h
+++ b/ios/chrome/browser/language/url_language_histogram_factory.h
@@ -7,7 +7,8 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
@@ -30,7 +31,7 @@
       ios::ChromeBrowserState* browser_state);
 
  private:
-  friend struct base::DefaultSingletonTraits<UrlLanguageHistogramFactory>;
+  friend class base::NoDestructor<UrlLanguageHistogramFactory>;
 
   UrlLanguageHistogramFactory();
   ~UrlLanguageHistogramFactory() override;
diff --git a/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h b/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h
index f311595..3f8c56e 100644
--- a/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h
+++ b/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h
@@ -6,13 +6,9 @@
 #define IOS_CHROME_BROWSER_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }  // namespace ios
@@ -38,7 +34,7 @@
       web::BrowserState* context) const override;
 
  private:
-  friend struct base::DefaultSingletonTraits<ProtoDatabaseProviderFactory>;
+  friend class base::NoDestructor<ProtoDatabaseProviderFactory>;
 
   ProtoDatabaseProviderFactory();
   ~ProtoDatabaseProviderFactory() override;
@@ -48,4 +44,4 @@
 
 }  // namespace leveldb_proto
 
-#endif  // IOS_CHROME_BROWSER_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_FACTORY_H_
\ No newline at end of file
+#endif  // IOS_CHROME_BROWSER_LEVELDB_PROTO_PROTO_DATABASE_PROVIDER_FACTORY_H_
diff --git a/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.mm b/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.mm
index fa852bd..29bfeac 100644
--- a/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.mm
+++ b/ios/chrome/browser/leveldb_proto/proto_database_provider_factory.mm
@@ -4,9 +4,9 @@
 
 #include "ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "components/leveldb_proto/proto_database_provider.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -17,7 +17,8 @@
 
 // static
 ProtoDatabaseProviderFactory* ProtoDatabaseProviderFactory::GetInstance() {
-  return base::Singleton<ProtoDatabaseProviderFactory>::get();
+  static base::NoDestructor<ProtoDatabaseProviderFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/main/BUILD.gn b/ios/chrome/browser/main/BUILD.gn
new file mode 100644
index 0000000..9d64d50
--- /dev/null
+++ b/ios/chrome/browser/main/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2017 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.
+
+source_set("main") {
+  sources = [
+    "browser.h",
+    "browser_impl.h",
+    "browser_impl.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/web_state_list",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test_browser.h",
+    "test_browser.mm",
+  ]
+  deps = [
+    ":main",
+    "//base",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/web_state_list",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "browser_impl_unittest.mm",
+  ]
+  deps = [
+    ":main",
+    ":test_support",
+    "//base",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/web/public/test",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/chrome/browser/main/browser.h b/ios/chrome/browser/main/browser.h
new file mode 100644
index 0000000..100e738
--- /dev/null
+++ b/ios/chrome/browser/main/browser.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_MAIN_BROWSER_H_
+#define IOS_CHROME_BROWSER_MAIN_BROWSER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/supports_user_data.h"
+
+@class TabModel;
+class WebStateList;
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+// Browser is the model for a window containing multiple tabs. Instances
+// are owned by a BrowserList to allow multiple windows for a single user
+// session.
+//
+// See src/docs/ios/objects.md for more information.
+class Browser : public base::SupportsUserData {
+ public:
+  // Creates a new Browser attached to |browser_state|. The |tab_model| must be
+  // non-nil.
+  static std::unique_ptr<Browser> Create(ios::ChromeBrowserState* browser_state,
+                                         TabModel* tab_model);
+  ~Browser() override {}
+
+  // Accessor for the owning ChromeBrowserState.
+  virtual ios::ChromeBrowserState* GetBrowserState() const = 0;
+
+  // Accessor for the TabModel. DEPRECATED: prefer web_state_list() whenever
+  // possible.
+  virtual TabModel* GetTabModel() const = 0;
+
+  // Accessor for the WebStateList.
+  virtual WebStateList* GetWebStateList() const = 0;
+
+ protected:
+  Browser() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Browser);
+};
+
+#endif  // IOS_CHROME_BROWSER_MAIN_BROWSER_H_
diff --git a/ios/chrome/browser/main/browser_impl.h b/ios/chrome/browser/main/browser_impl.h
new file mode 100644
index 0000000..b9c8b3f
--- /dev/null
+++ b/ios/chrome/browser/main/browser_impl.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_MAIN_BROWSER_IMPL_H_
+#define IOS_CHROME_BROWSER_MAIN_BROWSER_IMPL_H_
+
+#import "ios/chrome/browser/main/browser.h"
+
+#include "base/macros.h"
+
+@class TabModel;
+class WebStateList;
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+// Browser is the model for a window containing multiple tabs. Instances
+// are owned by a BrowserList to allow multiple windows for a single user
+// session.
+//
+// See src/docs/ios/objects.md for more information.
+class BrowserImpl : public Browser {
+ public:
+  // Constructs a BrowserImpl attached to |browser_state|. The |tab_model| must
+  // be non-nil.
+  BrowserImpl(ios::ChromeBrowserState* browser_state, TabModel* tab_model);
+  ~BrowserImpl() override;
+
+  // Accessor for the owning ChromeBrowserState.
+  ios::ChromeBrowserState* GetBrowserState() const override;
+
+  // Accessor for the TabModel. DEPRECATED: prefer web_state_list() whenever
+  // possible.
+  TabModel* GetTabModel() const override;
+
+  // Accessor for the WebStateList.
+  WebStateList* GetWebStateList() const override;
+
+ private:
+  ios::ChromeBrowserState* browser_state_;
+  __strong TabModel* tab_model_;
+  WebStateList* web_state_list_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(BrowserImpl);
+};
+
+#endif  // IOS_CHROME_BROWSER_MAIN_BROWSER_IMPL_H_
diff --git a/ios/chrome/browser/main/browser_impl.mm b/ios/chrome/browser/main/browser_impl.mm
new file mode 100644
index 0000000..ddba98e
--- /dev/null
+++ b/ios/chrome/browser/main/browser_impl.mm
@@ -0,0 +1,41 @@
+// 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.
+
+#import "ios/chrome/browser/main/browser_impl.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+BrowserImpl::BrowserImpl(ios::ChromeBrowserState* browser_state,
+                         TabModel* tab_model)
+    : browser_state_(browser_state), tab_model_(tab_model) {
+  DCHECK(browser_state_);
+  DCHECK(tab_model_);
+  web_state_list_ = tab_model_.webStateList;
+}
+
+BrowserImpl::~BrowserImpl() = default;
+
+ios::ChromeBrowserState* BrowserImpl::GetBrowserState() const {
+  return browser_state_;
+}
+
+TabModel* BrowserImpl::GetTabModel() const {
+  return tab_model_;
+}
+
+WebStateList* BrowserImpl::GetWebStateList() const {
+  return web_state_list_;
+}
+
+// static
+std::unique_ptr<Browser> Browser::Create(ios::ChromeBrowserState* browser_state,
+                                         TabModel* tab_model) {
+  return std::make_unique<BrowserImpl>(browser_state, tab_model);
+}
diff --git a/ios/chrome/browser/main/browser_impl_unittest.mm b/ios/chrome/browser/main/browser_impl_unittest.mm
new file mode 100644
index 0000000..fc3fe63
--- /dev/null
+++ b/ios/chrome/browser/main/browser_impl_unittest.mm
@@ -0,0 +1,59 @@
+// 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.
+
+#import "ios/chrome/browser/main/browser_impl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#include "ios/chrome/browser/web_state_list/web_state_list.h"
+#include "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class TestWebStateListDelegate : public WebStateListDelegate {
+ public:
+  ~TestWebStateListDelegate() override {}
+  void WillAddWebState(web::WebState* web_state) override {}
+  void WebStateDetached(web::WebState* web_state) override {}
+};
+
+class BrowserImplTest : public PlatformTest {
+ protected:
+  BrowserImplTest()
+      : web_state_list_delegate_(), web_state_list_(&web_state_list_delegate_) {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    chrome_browser_state_ = test_cbs_builder.Build();
+
+    tab_model_ = [OCMockObject mockForClass:[TabModel class]];
+    OCMStub([tab_model_ webStateList]).andReturn(&web_state_list_);
+  }
+
+  web::TestWebThreadBundle thread_bundle_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+
+  TestWebStateListDelegate web_state_list_delegate_;
+  WebStateList web_state_list_;
+  id tab_model_;
+};
+
+TEST_F(BrowserImplTest, TestAccessors) {
+  BrowserImpl browser(chrome_browser_state_.get(), tab_model_);
+
+  EXPECT_EQ(chrome_browser_state_.get(), browser.GetBrowserState());
+  EXPECT_EQ(tab_model_, browser.GetTabModel());
+  EXPECT_EQ(&web_state_list_, browser.GetWebStateList());
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/main/test_browser.h b/ios/chrome/browser/main/test_browser.h
new file mode 100644
index 0000000..c1fe508
--- /dev/null
+++ b/ios/chrome/browser/main/test_browser.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_MAIN_TEST_BROWSER_H_
+#define IOS_CHROME_BROWSER_MAIN_TEST_BROWSER_H_
+
+#include "ios/chrome/browser/main/browser.h"
+
+#include "base/macros.h"
+
+class TestBrowser : public Browser {
+ public:
+  TestBrowser(ios::ChromeBrowserState* browser_state, TabModel* tab_model);
+  ~TestBrowser() override;
+
+  // Browser.
+  ios::ChromeBrowserState* GetBrowserState() const override;
+  TabModel* GetTabModel() const override;
+  WebStateList* GetWebStateList() const override;
+
+ private:
+  ios::ChromeBrowserState* browser_state_;
+  TabModel* tab_model_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestBrowser);
+};
+
+#endif  // IOS_CHROME_BROWSER_MAIN_TEST_BROWSER_H_
diff --git a/ios/chrome/browser/main/test_browser.mm b/ios/chrome/browser/main/test_browser.mm
new file mode 100644
index 0000000..4c1727bb
--- /dev/null
+++ b/ios/chrome/browser/main/test_browser.mm
@@ -0,0 +1,29 @@
+// 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.
+
+#import "ios/chrome/browser/main/test_browser.h"
+
+#import "ios/chrome/browser/tabs/tab_model.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+TestBrowser::TestBrowser(ios::ChromeBrowserState* browser_state,
+                         TabModel* tab_model)
+    : browser_state_(browser_state), tab_model_(tab_model) {}
+
+TestBrowser::~TestBrowser() {}
+
+ios::ChromeBrowserState* TestBrowser::GetBrowserState() const {
+  return browser_state_;
+}
+
+TabModel* TestBrowser::GetTabModel() const {
+  return tab_model_;
+}
+
+WebStateList* TestBrowser::GetWebStateList() const {
+  return tab_model_.webStateList;
+}
diff --git a/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h b/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h
index bfbcfcc..42abfd7 100644
--- a/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h
+++ b/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h
@@ -6,14 +6,9 @@
 #define IOS_CHROME_BROWSER_METRICS_IOS_PROFILE_SESSION_DURATIONS_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
-
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class IOSProfileSessionDurationsService;
 namespace ios {
 class ChromeBrowserState;
@@ -29,8 +24,7 @@
   static IOSProfileSessionDurationsServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      IOSProfileSessionDurationsServiceFactory>;
+  friend class base::NoDestructor<IOSProfileSessionDurationsServiceFactory>;
 
   IOSProfileSessionDurationsServiceFactory();
   ~IOSProfileSessionDurationsServiceFactory() override;
diff --git a/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.mm b/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.mm
index aa26a02..a170495 100644
--- a/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.mm
+++ b/ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.mm
@@ -29,7 +29,8 @@
 // static
 IOSProfileSessionDurationsServiceFactory*
 IOSProfileSessionDurationsServiceFactory::GetInstance() {
-  return base::Singleton<IOSProfileSessionDurationsServiceFactory>::get();
+  static base::NoDestructor<IOSProfileSessionDurationsServiceFactory> instance;
+  return instance.get();
 }
 
 IOSProfileSessionDurationsServiceFactory::
diff --git a/ios/chrome/browser/omaha/omaha_service.h b/ios/chrome/browser/omaha/omaha_service.h
index ea79fb9a0..784d16f 100644
--- a/ios/chrome/browser/omaha/omaha_service.h
+++ b/ios/chrome/browser/omaha/omaha_service.h
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/scoped_observer.h"
 #include "base/timer/timer.h"
 #include "base/version.h"
@@ -66,9 +66,9 @@
   FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, InstallRetryTest);
   FRIEND_TEST_ALL_PREFIXES(OmahaServiceInternalTest,
                            PingMessageTestWithProfileData);
+
   // For the singleton:
-  friend struct base::DefaultSingletonTraits<OmahaService>;
-  friend class base::Singleton<OmahaService>;
+  friend class base::NoDestructor<OmahaService>;
 
   // Enum for the |GetPingContent| and |GetNextPingRequestId| method.
   enum PingContent {
diff --git a/ios/chrome/browser/omaha/omaha_service.mm b/ios/chrome/browser/omaha/omaha_service.mm
index bf13de5..3d94c71b 100644
--- a/ios/chrome/browser/omaha/omaha_service.mm
+++ b/ios/chrome/browser/omaha/omaha_service.mm
@@ -292,7 +292,8 @@
 
 // static
 OmahaService* OmahaService::GetInstance() {
-  return base::Singleton<OmahaService>::get();
+  static base::NoDestructor<OmahaService> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/passwords/password_controller.h b/ios/chrome/browser/passwords/password_controller.h
index ae43d17..f6bbc86 100644
--- a/ios/chrome/browser/passwords/password_controller.h
+++ b/ios/chrome/browser/passwords/password_controller.h
@@ -79,10 +79,14 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Generates and offers a password to the user based on given |formName|.
-// |completionHandler| called with YES if user accepted the generated password.
-- (void)generatePasswordForForm:(NSString*)formName
-              completionHandler:(void (^)(BOOL))completionHandler;
+// Generates and offers a password to the user based on given |formName| for the
+// given (optional) fields |newPasswordIdentfier| and
+// |confirmPasswordIdentfier|. |completionHandler| called with YES if user
+// accepted the generated password.
+- (void)generatePasswordForFormName:(NSString*)formName
+              newPasswordIdentifier:(NSString*)newPasswordIdentifier
+          confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+                  completionHandler:(void (^)(BOOL))completionHandler;
 
 @end
 
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 2e71b2b..a73ac1f 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -73,7 +73,10 @@
 #endif
 
 using autofill::FormData;
+using autofill::NewPasswordFormGenerationData;
 using autofill::PasswordForm;
+using base::SysNSStringToUTF16;
+using base::SysUTF16ToNSString;
 using password_manager::AccountSelectFillData;
 using password_manager::FillData;
 using password_manager::GetPageURLAndCheckTrustLevel;
@@ -180,8 +183,7 @@
   std::unique_ptr<autofill::PasswordForm> _pendingAutoSigninPasswordForm;
 
   // Form data for password generation on this page.
-  std::vector<const autofill::NewPasswordFormGenerationData*>
-      _formGenerationData;
+  std::map<base::string16, NewPasswordFormGenerationData> _formGenerationData;
 }
 
 - (instancetype)initWithWebState:(web::WebState*)webState {
@@ -432,11 +434,11 @@
       return;
     }
     case autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY: {
-      [self generatePasswordForForm:formName
-                  completionHandler:^(BOOL injected) {
-                    if (injected)
-                      completion();
-                  }];
+      [self generatePasswordForFormName:formName
+                      completionHandler:^(BOOL injected) {
+                        if (injected)
+                          completion();
+                      }];
       // TODO(crbug.com/886583): add metrics.
       return;
     }
@@ -505,12 +507,12 @@
   }
 
   // Creates view controller then shows the subview.
-  self.notifyAutoSigninViewController = [
-      [NotifyUserAutoSigninViewController alloc]
-      initWithUsername:base::SysUTF16ToNSString(formSignedIn->username_value)
-               iconURL:formSignedIn->icon_url
-      URLLoaderFactory:_webState->GetBrowserState()
-                           ->GetSharedURLLoaderFactory()];
+  self.notifyAutoSigninViewController =
+      [[NotifyUserAutoSigninViewController alloc]
+          initWithUsername:SysUTF16ToNSString(formSignedIn->username_value)
+                   iconURL:formSignedIn->icon_url
+          URLLoaderFactory:_webState->GetBrowserState()
+                               ->GetSharedURLLoaderFactory()];
   TabIdTabHelper* tabIdHelper = TabIdTabHelper::FromWebState(_webState);
   if (![_delegate displaySignInNotification:self.notifyAutoSigninViewController
                                   fromTabId:tabIdHelper->tab_id()]) {
@@ -548,9 +550,8 @@
 }
 
 - (void)formEligibleForGenerationFound:
-    (const autofill::NewPasswordFormGenerationData&)form {
-  _formGenerationData.push_back(
-      new autofill::NewPasswordFormGenerationData(form));
+    (const NewPasswordFormGenerationData&)form {
+  _formGenerationData[form.form_name] = form;
 }
 
 #pragma mark - PasswordFormHelperDelegate
@@ -656,14 +657,45 @@
     return NO;
   if (![fieldType isEqualToString:@"password"])
     return NO;
+  if (![self hasFormForGenerationForFormName:formName])
+    return NO;
 
   // TODO(crbug.com/886583): validate field against _formGenerationData
 
   return YES;
 }
 
-- (void)generatePasswordForForm:(NSString*)formName
-              completionHandler:(void (^)(BOOL))completionHandler {
+- (BOOL)hasFormForGenerationForFormName:(NSString*)formName {
+  const base::string16 name = SysNSStringToUTF16(formName);
+  return _formGenerationData.find(name) != _formGenerationData.end();
+}
+
+- (const NewPasswordFormGenerationData)getFormForGenerationFromFormName:
+    (NSString*)formName {
+  const base::string16 name = SysNSStringToUTF16(formName);
+  return _formGenerationData[name];
+}
+
+- (void)generatePasswordForFormName:(NSString*)formName
+                  completionHandler:(void (^)(BOOL))completionHandler {
+  if (![self hasFormForGenerationForFormName:formName])
+    return;
+  const NewPasswordFormGenerationData form =
+      [self getFormForGenerationFromFormName:formName];
+  NSString* newPasswordIdentifier =
+      SysUTF16ToNSString(form.new_password_element);
+  NSString* confirmPasswordIdentifier =
+      SysUTF16ToNSString(form.confirmation_password_element);
+  [self generatePasswordForFormName:formName
+              newPasswordIdentifier:newPasswordIdentifier
+          confirmPasswordIdentifier:confirmPasswordIdentifier
+                  completionHandler:completionHandler];
+}
+
+- (void)generatePasswordForFormName:(NSString*)formName
+              newPasswordIdentifier:(NSString*)newPasswordIdentifier
+          confirmPasswordIdentifier:(NSString*)confirmPasswordIdentifier
+                  completionHandler:(void (^)(BOOL))completionHandler {
   // TODO(crbug.com/886583): form_signature, field_signature, max_length and
   // spec_priority in PGM::GeneratePassword are being refactored, passing 0 for
   // now to get a generic random password.
@@ -671,7 +703,7 @@
       _passwordGenerationManager->GeneratePassword([self lastCommittedURL], 0,
                                                    0, 0, nullptr);
 
-  NSString* displayPassword = base::SysUTF16ToNSString(generatedPassword);
+  NSString* displayPassword = SysUTF16ToNSString(generatedPassword);
 
   // TODO(crbug.com/886583): i18n
   NSString* title = [NSString
@@ -679,6 +711,7 @@
   NSString* message = @"Chrome will remember this password for you. You don't "
                       @"have to remember it.";
 
+  // TODO(crbug.com/886583): add eg tests
   self.actionSheetCoordinator = [[ActionSheetCoordinator alloc]
       initWithBaseViewController:self.baseViewController
                            title:title
@@ -690,14 +723,29 @@
       IsIPadIdiom() ? UIAlertControllerStyleAlert
                     : UIAlertControllerStyleActionSheet;
 
-  [self.actionSheetCoordinator
-      addItemWithTitle:l10n_util::GetNSString(IDS_IOS_SUGGEST_PASSWORD)
-                action:^{
-                  // TODO(crbug.com/886583): inject in password form
-                  if (completionHandler)
-                    completionHandler(YES);
-                }
-                 style:UIAlertActionStyleDefault];
+  auto generatedPasswordInjected = ^(BOOL success) {
+    if (success) {
+      // TODO(crbug.com/886583) do not call presaved if username hasn't been
+      // filled in.
+      // TODO(crbug.com/886583) call _pM::OnPresaveGeneratedPassword once it has
+      // been refactored not to need a full form.
+    }
+    if (completionHandler)
+      completionHandler(YES);
+  };
+
+  auto injectGeneratedPassword = ^{
+    [self.formHelper fillPasswordForm:formName
+                newPasswordIdentifier:newPasswordIdentifier
+            confirmPasswordIdentifier:confirmPasswordIdentifier
+                    generatedPassword:displayPassword
+                    completionHandler:generatedPasswordInjected];
+  };
+
+  // TODO(crbug.com/886583): i18n
+  [self.actionSheetCoordinator addItemWithTitle:@"Use Suggested Password"
+                                         action:injectGeneratedPassword
+                                          style:UIAlertActionStyleDefault];
 
   [self.actionSheetCoordinator
       addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
diff --git a/ios/chrome/browser/passwords/password_controller_js_unittest.mm b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
index 34db38c..51a17ea9 100644
--- a/ios/chrome/browser/passwords/password_controller_js_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
@@ -14,7 +14,8 @@
 #error "This file requires ARC support."
 #endif
 
-// Unit tests for ios/chrome/browser/web/resources/password_controller.js
+// Unit tests for
+// components/password_manager/ios/resources/password_controller.js
 namespace {
 
 // Text fixture to test password controller.
@@ -392,4 +393,183 @@
                                       @[ @"name", @"password" ],
                                       @[ username, password ]);
 }
+
+// Check that when instructed to fill a form named "bar", a form named "foo"
+// is not filled with generated password.
+TEST_F(PasswordControllerJsTest,
+       FillPasswordFormWithGeneratedPassword_FailsWhenFormNotFound) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"password\" id=\"ps1\" name=\"ps\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"bar";
+  NSString* const password = @"abc";
+  NSString* const newPasswordIdentifier = @"ps1";
+  EXPECT_NSEQ(
+      @NO,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords."
+          @"fillPasswordFormWithGeneratedPassword('%@',  '%@', '%@', '%@')",
+          formName, newPasswordIdentifier, @"", password));
+}
+
+// Check that filling a form without password fields fails.
+TEST_F(PasswordControllerJsTest,
+       FillPasswordFormWithGeneratedPassword_FailsWhenNoPasswordFields) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"text\" name=\"user\">"
+                     "      <input type=\"submit\" name=\"go\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"foo";
+  NSString* const password = @"abc";
+  NSString* const newPasswordIdentifier = @"ps1";
+  NSString* const confirmPasswordIdentifier = @"ps2";
+  EXPECT_NSEQ(
+      @NO, ExecuteJavaScriptWithFormat(
+               @"__gCrWeb.passwords."
+               @"fillPasswordFormWithGeneratedPassword('%@', '%@', '%@', '%@')",
+               formName, newPasswordIdentifier, confirmPasswordIdentifier,
+               password));
+}
+
+// Check that a matching and complete password form is successfully filled
+// with the generated password.
+TEST_F(PasswordControllerJsTest,
+       FillPasswordFormWithGeneratedPassword_SucceedsWhenFieldsFilled) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"text\" id=\"user\" name=\"user\">"
+                     "      <input type=\"password\" id=\"ps1\" name=\"ps1\">"
+                     "      <input type=\"password\" id=\"ps2\" name=\"ps2\">"
+                     "      <input type=\"submit\" name=\"go\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"foo";
+  NSString* const password = @"abc";
+  NSString* const newPasswordIdentifier = @"ps1";
+  NSString* const confirmPasswordIdentifier = @"ps2";
+  EXPECT_NSEQ(
+      @YES,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords."
+          @"fillPasswordFormWithGeneratedPassword('%@', '%@', '%@', '%@')",
+          formName, newPasswordIdentifier, confirmPasswordIdentifier,
+          password));
+  EXPECT_NSEQ(@YES,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('ps1').value == '%@'", password));
+  EXPECT_NSEQ(@YES,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('ps2').value == '%@'", password));
+  EXPECT_NSEQ(@NO,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('user').value == '%@'", password));
+}
+
+// Check that a matching and complete password field is successfully filled
+// with the generated password and that confirm field is untouched.
+TEST_F(
+    PasswordControllerJsTest,
+    FillPasswordFormWithGeneratedPassword_SucceedsWhenOnlyNewPasswordFilled) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"text\" id=\"user\" name=\"user\">"
+                     "      <input type=\"password\" id=\"ps1\" name=\"ps1\">"
+                     "      <input type=\"password\" id=\"ps2\" name=\"ps2\">"
+                     "      <input type=\"submit\" name=\"go\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"foo";
+  NSString* const password = @"abc";
+  NSString* const newPasswordIdentifier = @"ps1";
+  EXPECT_NSEQ(
+      @YES,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords."
+          @"fillPasswordFormWithGeneratedPassword('%@', '%@', '%@', '%@')",
+          formName, newPasswordIdentifier, @"", password));
+  EXPECT_NSEQ(@YES,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('ps1').value == '%@'", password));
+  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
+                        @"document.getElementById('ps2').value == '%@'", @""));
+  EXPECT_NSEQ(@NO,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('user').value == '%@'", password));
+}
+
+// Check that a matching and complete confirm password field is successfully
+// filled with the generated password and that new password field is untouched.
+TEST_F(
+    PasswordControllerJsTest,
+    FillPasswordFormWithGeneratedPassword_SucceedsWhenOnlyConfirmPasswordFilled) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"text\" id=\"user\" name=\"user\">"
+                     "      <input type=\"password\" id=\"ps1\" name=\"ps1\">"
+                     "      <input type=\"password\" id=\"ps2\" name=\"ps2\">"
+                     "      <input type=\"submit\" name=\"go\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"foo";
+  NSString* const password = @"abc";
+  NSString* const confirmPasswordIdentifier = @"ps2";
+  EXPECT_NSEQ(
+      @YES,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords."
+          @"fillPasswordFormWithGeneratedPassword('%@', '%@', '%@', '%@')",
+          formName, @"", confirmPasswordIdentifier, password));
+  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
+                        @"document.getElementById('ps1').value == '%@'", @""));
+  EXPECT_NSEQ(@YES,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('ps2').value == '%@'", password));
+  EXPECT_NSEQ(@NO,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('user').value == '%@'", password));
+}
+
+// Check that unknown or null identifiers are handled gracefully.
+TEST_F(
+    PasswordControllerJsTest,
+    FillPasswordFormWithGeneratedPassword_SucceedsOnUnknownOrNullIdentifiers) {
+  LoadHtmlAndInject(@"<html>"
+                     "  <body>"
+                     "    <form name=\"foo\">"
+                     "      <input type=\"text\" id=\"user\" name=\"user\">"
+                     "      <input type=\"password\" id=\"ps1\" name=\"ps1\">"
+                     "      <input type=\"password\" id=\"ps2\" name=\"ps2\">"
+                     "      <input type=\"submit\" name=\"go\">"
+                     "    </form>"
+                     "  </body"
+                     "</html>");
+  NSString* const formName = @"foo";
+  NSString* const password = @"abc";
+  EXPECT_NSEQ(
+      @NO, ExecuteJavaScriptWithFormat(
+               @"__gCrWeb.passwords."
+               @"fillPasswordFormWithGeneratedPassword('%@', '%@', null, '%@')",
+               formName, @"hello", password));
+  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
+                        @"document.getElementById('ps1').value == '%@'", @""));
+  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
+                        @"document.getElementById('ps2').value == '%@'", @""));
+  EXPECT_NSEQ(@NO,
+              ExecuteJavaScriptWithFormat(
+                  @"document.getElementById('user').value == '%@'", password));
+}
 }  // namespace
diff --git a/ios/chrome/browser/passwords/password_tab_helper.h b/ios/chrome/browser/passwords/password_tab_helper.h
index aed2224..aff9972 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.h
+++ b/ios/chrome/browser/passwords/password_tab_helper.h
@@ -39,8 +39,11 @@
   // Sets the PasswordController delegate.
   void SetPasswordControllerDelegate(id<PasswordControllerDelegate> delegate);
 
-  // Generate and offer to user a password for the given |formName|.
-  void GenerateAndOfferPassword(NSString* formName);
+  // Generate and offer to user a password for the given |formName| on given
+  // (optional) fields |newPasswordIdentifier| and |confirmPasswordIdentifier|.
+  void GenerateAndOfferPassword(NSString* formName,
+                                NSString* newPasswordIdentifier,
+                                NSString* confirmPasswordIdentifier);
 
   // Returns an object that can provide suggestions from the PasswordController.
   // May return nil.
diff --git a/ios/chrome/browser/passwords/password_tab_helper.mm b/ios/chrome/browser/passwords/password_tab_helper.mm
index fd2f9cd..a1e9ea7 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper.mm
@@ -37,8 +37,14 @@
   controller_.delegate = delegate;
 }
 
-void PasswordTabHelper::GenerateAndOfferPassword(NSString* formName) {
-  [controller_ generatePasswordForForm:formName completionHandler:nil];
+void PasswordTabHelper::GenerateAndOfferPassword(
+    NSString* formName,
+    NSString* newPasswordIdentifier,
+    NSString* confirmPasswordIdentifier) {
+  [controller_ generatePasswordForFormName:formName
+                     newPasswordIdentifier:newPasswordIdentifier
+                 confirmPasswordIdentifier:confirmPasswordIdentifier
+                         completionHandler:nil];
 }
 
 id<FormSuggestionProvider> PasswordTabHelper::GetSuggestionProvider() {
diff --git a/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.cc b/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.cc
index b2dd1f01..b237860 100644
--- a/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.cc
+++ b/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.cc
@@ -4,14 +4,15 @@
 
 #include "ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/content_settings/core/browser/website_settings_info.h"
 #include "components/content_settings/core/browser/website_settings_registry.h"
 
 // static
 IOSChromePrefModelAssociatorClient*
 IOSChromePrefModelAssociatorClient::GetInstance() {
-  return base::Singleton<IOSChromePrefModelAssociatorClient>::get();
+  static base::NoDestructor<IOSChromePrefModelAssociatorClient> instance;
+  return instance.get();
 }
 
 IOSChromePrefModelAssociatorClient::IOSChromePrefModelAssociatorClient() {}
diff --git a/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.h b/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.h
index 8f4adab1..a876b8b 100644
--- a/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.h
+++ b/ios/chrome/browser/prefs/ios_chrome_pref_model_associator_client.h
@@ -8,13 +8,9 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/sync_preferences/pref_model_associator_client.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 class IOSChromePrefModelAssociatorClient
     : public sync_preferences::PrefModelAssociatorClient {
  public:
@@ -22,8 +18,7 @@
   static IOSChromePrefModelAssociatorClient* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      IOSChromePrefModelAssociatorClient>;
+  friend class base::NoDestructor<IOSChromePrefModelAssociatorClient>;
 
   IOSChromePrefModelAssociatorClient();
   ~IOSChromePrefModelAssociatorClient() override;
diff --git a/ios/chrome/browser/prerender/BUILD.gn b/ios/chrome/browser/prerender/BUILD.gn
index f5260b2e..f0f1def1 100644
--- a/ios/chrome/browser/prerender/BUILD.gn
+++ b/ios/chrome/browser/prerender/BUILD.gn
@@ -28,6 +28,7 @@
     "//ios/chrome/browser/history:tab_helper",
     "//ios/chrome/browser/itunes_urls:itunes_urls",
     "//ios/chrome/browser/net",
+    "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/snapshots",
     "//ios/chrome/browser/tabs",
diff --git a/ios/chrome/browser/prerender/prerender_service.h b/ios/chrome/browser/prerender/prerender_service.h
index fddf7c9..561a8466 100644
--- a/ios/chrome/browser/prerender/prerender_service.h
+++ b/ios/chrome/browser/prerender/prerender_service.h
@@ -13,15 +13,14 @@
 
 @class PreloadController;
 @protocol PreloadControllerDelegate;
-@class TabModel;
-
+@protocol SessionWindowRestoring;
 namespace ios {
 class ChromeBrowserState;
 }
-
 namespace web {
 class WebState;
 }
+class WebStateList;
 
 // PrerenderService manages a prerendered WebState.
 class PrerenderService : public KeyedService {
@@ -51,14 +50,16 @@
                       ui::PageTransition transition,
                       bool immediately);
 
-  // If |url| is prerendered, loads the prerendered web state into |tab_model|
-  // at the active index, replacing the existing active web state. If not, or if
-  // it isn't possible to replace the active web state, cancels the active
-  // preload. Metrics and snapshots are appropriately updated. Returns true if
-  // the active webstate was replaced, false otherwise.
+  // If |url| is prerendered, loads the prerendered web state into
+  // |web_state_list| at the active index, replacing the existing active web
+  // state and saving the session (via |restorer|). If not, or if it isn't
+  // possible to replace the active web state, cancels the active preload.
+  // Metrics and snapshots are appropriately updated. Returns true if the active
+  // webstate was replaced, false otherwise.
   bool MaybeLoadPrerenderedURL(const GURL& url,
                                ui::PageTransition transition,
-                               TabModel* tab_model);
+                               WebStateList* web_state_list,
+                               id<SessionWindowRestoring> restorer);
 
   // |true| while a prerendered webstate is being inserted into a webStateList.
   bool IsLoadingPrerender() { return loading_prerender_; }
diff --git a/ios/chrome/browser/prerender/prerender_service.mm b/ios/chrome/browser/prerender/prerender_service.mm
index 7d6c89f..59876a4 100644
--- a/ios/chrome/browser/prerender/prerender_service.mm
+++ b/ios/chrome/browser/prerender/prerender_service.mm
@@ -6,10 +6,8 @@
 
 #include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/prerender/preload_controller.h"
+#import "ios/chrome/browser/sessions/session_window_restoring.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
-#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
-#import "ios/chrome/browser/tabs/tab.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/web/load_timing_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -55,9 +53,11 @@
                 immediately:immediately];
 }
 
-bool PrerenderService::MaybeLoadPrerenderedURL(const GURL& url,
-                                               ui::PageTransition transition,
-                                               TabModel* tab_model) {
+bool PrerenderService::MaybeLoadPrerenderedURL(
+    const GURL& url,
+    ui::PageTransition transition,
+    WebStateList* web_state_list,
+    id<SessionWindowRestoring> restorer) {
   if (!HasPrerenderForUrl(url)) {
     CancelPrerender();
     return false;
@@ -65,8 +65,6 @@
 
   std::unique_ptr<web::WebState> new_web_state =
       [controller_ releasePrerenderContents];
-  DCHECK(new_web_state);
-  WebStateList* web_state_list = tab_model.webStateList;
   DCHECK_NE(WebStateList::kInvalidIndex, web_state_list->active_index());
 
   web::NavigationManager* active_navigation_manager =
@@ -106,7 +104,7 @@
           ->DidPromotePrerenderTab();
     }
 
-    [tab_model saveSessionImmediately:NO];
+    [restorer saveSessionImmediately:NO];
     return true;
   }
 
diff --git a/ios/chrome/browser/prerender/prerender_service_factory.h b/ios/chrome/browser/prerender/prerender_service_factory.h
index 394e749c..0cf3d62 100644
--- a/ios/chrome/browser/prerender/prerender_service_factory.h
+++ b/ios/chrome/browser/prerender/prerender_service_factory.h
@@ -8,15 +8,11 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
 class PrerenderService;
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }  // namespace ios
@@ -30,7 +26,7 @@
   static PrerenderServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<PrerenderServiceFactory>;
+  friend class base::NoDestructor<PrerenderServiceFactory>;
 
   PrerenderServiceFactory();
   ~PrerenderServiceFactory() override;
diff --git a/ios/chrome/browser/prerender/prerender_service_factory.mm b/ios/chrome/browser/prerender/prerender_service_factory.mm
index 5a30fb1..42725ad 100644
--- a/ios/chrome/browser/prerender/prerender_service_factory.mm
+++ b/ios/chrome/browser/prerender/prerender_service_factory.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/prerender/prerender_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/prerender/prerender_service.h"
@@ -23,7 +23,8 @@
 
 // static
 PrerenderServiceFactory* PrerenderServiceFactory::GetInstance() {
-  return base::Singleton<PrerenderServiceFactory>::get();
+  static base::NoDestructor<PrerenderServiceFactory> instance;
+  return instance.get();
 }
 
 PrerenderServiceFactory::PrerenderServiceFactory()
diff --git a/ios/chrome/browser/search_engines/template_url_fetcher_factory.cc b/ios/chrome/browser/search_engines/template_url_fetcher_factory.cc
index ce72911..5d4038c 100644
--- a/ios/chrome/browser/search_engines/template_url_fetcher_factory.cc
+++ b/ios/chrome/browser/search_engines/template_url_fetcher_factory.cc
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/search_engines/template_url_fetcher_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/search_engines/template_url_fetcher.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
@@ -22,7 +22,8 @@
 
 // static
 TemplateURLFetcherFactory* TemplateURLFetcherFactory::GetInstance() {
-  return base::Singleton<TemplateURLFetcherFactory>::get();
+  static base::NoDestructor<TemplateURLFetcherFactory> instance;
+  return instance.get();
 }
 
 TemplateURLFetcherFactory::TemplateURLFetcherFactory()
diff --git a/ios/chrome/browser/search_engines/template_url_fetcher_factory.h b/ios/chrome/browser/search_engines/template_url_fetcher_factory.h
index 5043c42..d1b3db9 100644
--- a/ios/chrome/browser/search_engines/template_url_fetcher_factory.h
+++ b/ios/chrome/browser/search_engines/template_url_fetcher_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class TemplateURLFetcher;
 
 namespace ios {
@@ -31,7 +27,7 @@
   static TemplateURLFetcherFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<TemplateURLFetcherFactory>;
+  friend class base::NoDestructor<TemplateURLFetcherFactory>;
 
   TemplateURLFetcherFactory();
   ~TemplateURLFetcherFactory() override;
diff --git a/ios/chrome/browser/search_engines/template_url_service_factory.cc b/ios/chrome/browser/search_engines/template_url_service_factory.cc
index c2c3d5d..a87287d 100644
--- a/ios/chrome/browser/search_engines/template_url_service_factory.cc
+++ b/ios/chrome/browser/search_engines/template_url_service_factory.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/search_engines/default_search_manager.h"
@@ -67,7 +67,8 @@
 
 // static
 TemplateURLServiceFactory* TemplateURLServiceFactory::GetInstance() {
-  return base::Singleton<TemplateURLServiceFactory>::get();
+  static base::NoDestructor<TemplateURLServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/search_engines/template_url_service_factory.h b/ios/chrome/browser/search_engines/template_url_service_factory.h
index c58b108..574dbbe 100644
--- a/ios/chrome/browser/search_engines/template_url_service_factory.h
+++ b/ios/chrome/browser/search_engines/template_url_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class TemplateURLService;
 
 namespace ios {
@@ -35,7 +31,7 @@
   static TestingFactory GetDefaultFactory();
 
  private:
-  friend struct base::DefaultSingletonTraits<TemplateURLServiceFactory>;
+  friend class base::NoDestructor<TemplateURLServiceFactory>;
 
   TemplateURLServiceFactory();
   ~TemplateURLServiceFactory() override;
diff --git a/ios/chrome/browser/sessions/BUILD.gn b/ios/chrome/browser/sessions/BUILD.gn
index 9248aa7..e72675c19 100644
--- a/ios/chrome/browser/sessions/BUILD.gn
+++ b/ios/chrome/browser/sessions/BUILD.gn
@@ -46,6 +46,7 @@
     "session_util.mm",
     "session_window_ios.h",
     "session_window_ios.mm",
+    "session_window_restoring.h",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.cc b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.cc
index 1af565e..95adc479 100644
--- a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.cc
+++ b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/sessions/core/tab_restore_service_impl.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -37,7 +37,8 @@
 // static
 IOSChromeTabRestoreServiceFactory*
 IOSChromeTabRestoreServiceFactory::GetInstance() {
-  return base::Singleton<IOSChromeTabRestoreServiceFactory>::get();
+  static base::NoDestructor<IOSChromeTabRestoreServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
index a28a4c3..99b57ba 100644
--- a/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
+++ b/ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace sessions {
 class TabRestoreService;
 }
@@ -38,7 +34,7 @@
   static TestingFactory GetDefaultFactory();
 
  private:
-  friend struct base::DefaultSingletonTraits<IOSChromeTabRestoreServiceFactory>;
+  friend class base::NoDestructor<IOSChromeTabRestoreServiceFactory>;
 
   IOSChromeTabRestoreServiceFactory();
   ~IOSChromeTabRestoreServiceFactory() override;
diff --git a/ios/chrome/browser/sessions/session_window_restoring.h b/ios/chrome/browser/sessions/session_window_restoring.h
new file mode 100644
index 0000000..2bb3443
--- /dev/null
+++ b/ios/chrome/browser/sessions/session_window_restoring.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef IOS_CHROME_BROWSER_SESSIONS_SESSION_WINDOW_RESTORING_H_
+#define IOS_CHROME_BROWSER_SESSIONS_SESSION_WINDOW_RESTORING_H_
+
+@class SessionWindowIOS;
+
+// API for an object that can save and restore session windows. Typically an
+// object confoming to this protocol is responsible for a list of tabs, and
+// calls to these methopds affect that list.
+@protocol SessionWindowRestoring
+
+// Restores the |window| (for example, after a crash). If there is only one tab,
+// showing the NTP, then this tab should be clobbered, otherwise, the tabs from
+// the restored sessions should be added at the end of the current list of tabs.
+// Returns YES if the single NTP tab is closed.
+- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window;
+
+// Persists the current list of tabs to disk, either immediately or deferred
+// based on the value of |immediately|.
+- (void)saveSessionImmediately:(BOOL)immediately;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_SESSIONS_SESSION_WINDOW_RESTORING_H_
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
index af83912..96c6227 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -30,8 +26,7 @@
   static TabRestoreServiceDelegateImplIOSFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      TabRestoreServiceDelegateImplIOSFactory>;
+  friend class base::NoDestructor<TabRestoreServiceDelegateImplIOSFactory>;
 
   TabRestoreServiceDelegateImplIOSFactory();
   ~TabRestoreServiceDelegateImplIOSFactory() override;
diff --git a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm
index 3df9e04..4f4aaaa 100644
--- a/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm
+++ b/ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.mm
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
@@ -26,7 +26,8 @@
 // static
 TabRestoreServiceDelegateImplIOSFactory*
 TabRestoreServiceDelegateImplIOSFactory::GetInstance() {
-  return base::Singleton<TabRestoreServiceDelegateImplIOSFactory>::get();
+  static base::NoDestructor<TabRestoreServiceDelegateImplIOSFactory> instance;
+  return instance.get();
 }
 
 TabRestoreServiceDelegateImplIOSFactory::
diff --git a/ios/chrome/browser/share_extension/share_extension_service_factory.h b/ios/chrome/browser/share_extension/share_extension_service_factory.h
index 215905b..124c960 100644
--- a/ios/chrome/browser/share_extension/share_extension_service_factory.h
+++ b/ios/chrome/browser/share_extension/share_extension_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class ShareExtensionService;
 
 namespace ios {
@@ -32,7 +28,7 @@
   static ShareExtensionServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<ShareExtensionServiceFactory>;
+  friend class base::NoDestructor<ShareExtensionServiceFactory>;
 
   ShareExtensionServiceFactory();
   ~ShareExtensionServiceFactory() override;
diff --git a/ios/chrome/browser/share_extension/share_extension_service_factory.mm b/ios/chrome/browser/share_extension/share_extension_service_factory.mm
index 234d0bc0..5ad4f3c 100644
--- a/ios/chrome/browser/share_extension/share_extension_service_factory.mm
+++ b/ios/chrome/browser/share_extension/share_extension_service_factory.mm
@@ -4,7 +4,7 @@
 
 #include "ios/chrome/browser/share_extension/share_extension_service_factory.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
@@ -32,7 +32,8 @@
 
 // static
 ShareExtensionServiceFactory* ShareExtensionServiceFactory::GetInstance() {
-  return base::Singleton<ShareExtensionServiceFactory>::get();
+  static base::NoDestructor<ShareExtensionServiceFactory> instance;
+  return instance.get();
 }
 
 ShareExtensionServiceFactory::ShareExtensionServiceFactory()
diff --git a/ios/chrome/browser/tab_parenting_global_observer.cc b/ios/chrome/browser/tab_parenting_global_observer.cc
index 8c0ae3b..0f2c13f 100644
--- a/ios/chrome/browser/tab_parenting_global_observer.cc
+++ b/ios/chrome/browser/tab_parenting_global_observer.cc
@@ -4,10 +4,11 @@
 
 #include "ios/chrome/browser/tab_parenting_global_observer.h"
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 
 TabParentingGlobalObserver* TabParentingGlobalObserver::GetInstance() {
-  return base::Singleton<TabParentingGlobalObserver>::get();
+  static base::NoDestructor<TabParentingGlobalObserver> instance;
+  return instance.get();
 }
 
 std::unique_ptr<base::CallbackList<void(web::WebState*)>::Subscription>
diff --git a/ios/chrome/browser/tab_parenting_global_observer.h b/ios/chrome/browser/tab_parenting_global_observer.h
index ff14a07..1af30b8 100644
--- a/ios/chrome/browser/tab_parenting_global_observer.h
+++ b/ios/chrome/browser/tab_parenting_global_observer.h
@@ -9,11 +9,7 @@
 
 #include "base/callback_list.h"
 #include "base/macros.h"
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
+#include "base/no_destructor.h"
 
 namespace web {
 class WebState;
@@ -37,7 +33,7 @@
   void OnTabParented(web::WebState* web_state);
 
  private:
-  friend struct base::DefaultSingletonTraits<TabParentingGlobalObserver>;
+  friend class base::NoDestructor<TabParentingGlobalObserver>;
 
   TabParentingGlobalObserver();
   ~TabParentingGlobalObserver();
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index 40711f7..397b8cd7 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -25,6 +25,7 @@
     "//components/sessions",
     "//components/signin/ios/browser",
     "//components/sync_sessions",
+    "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/web:tab_helper_delegates",
     "//ios/chrome/browser/web_state_list",
     "//ios/net",
diff --git a/ios/chrome/browser/tabs/tab_model.h b/ios/chrome/browser/tabs/tab_model.h
index 20d6b3a..71a3e73 100644
--- a/ios/chrome/browser/tabs/tab_model.h
+++ b/ios/chrome/browser/tabs/tab_model.h
@@ -10,7 +10,9 @@
 
 #include <memory>
 
+#import "ios/chrome/browser/sessions/session_window_restoring.h"
 #import "ios/web/public/navigation_manager.h"
+
 #include "ui/base/page_transition_types.h"
 
 class GURL;
@@ -45,7 +47,7 @@
 // The model knows about the currently selected tab in order to maintain
 // consistency between multiple views that need the current tab to be
 // synchronized.
-@interface TabModel : NSObject
+@interface TabModel : NSObject <SessionWindowRestoring>
 
 // Currently active tab.
 @property(nonatomic, weak) Tab* currentTab;
@@ -86,16 +88,6 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Restores the given session window after a crash. If there is only one tab,
-// showing the NTP, then this tab is clobberred, otherwise, the tab from the
-// sessions are added at the end of the tab model.
-// Returns YES if the single NTP tab is closed.
-- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window;
-
-// Uses the SessionServiceIOS to persist the tab model to disk, either
-// immediately or deferred based on the value of |immediately|.
-- (void)saveSessionImmediately:(BOOL)immediately;
-
 // Accesses the tab at the given index.
 - (Tab*)tabAtIndex:(NSUInteger)index;
 - (NSUInteger)indexOfTab:(Tab*)tab;
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 0563b0c..5616e8f 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -435,26 +435,6 @@
   return self;
 }
 
-- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window {
-  return [self restoreSessionWindow:window persistState:YES];
-}
-
-- (void)saveSessionImmediately:(BOOL)immediately {
-  // Do nothing if there are tabs in the model but no selected tab. This is
-  // a transitional state.
-  if ((!self.currentTab && _webStateList->count()) || !_browserState)
-    return;
-  NSString* statePath =
-      base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
-  __weak TabModel* weakSelf = self;
-  SessionIOSFactory sessionFactory = ^{
-    return weakSelf.sessionForSaving;
-  };
-  [_sessionService saveSession:sessionFactory
-                     directory:statePath
-                   immediately:immediately];
-}
-
 - (Tab*)tabAtIndex:(NSUInteger)index {
   DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
   return LegacyTabHelper::GetTabForWebState(
@@ -638,6 +618,28 @@
   _webStateObserver.reset();
 }
 
+#pragma mark - SessionWindowRestoring(public)
+
+- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window {
+  return [self restoreSessionWindow:window persistState:YES];
+}
+
+- (void)saveSessionImmediately:(BOOL)immediately {
+  // Do nothing if there are tabs in the model but no selected tab. This is
+  // a transitional state.
+  if ((!self.currentTab && _webStateList->count()) || !_browserState)
+    return;
+  NSString* statePath =
+      base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
+  __weak TabModel* weakSelf = self;
+  SessionIOSFactory sessionFactory = ^{
+    return weakSelf.sessionForSaving;
+  };
+  [_sessionService saveSession:sessionFactory
+                     directory:statePath
+                   immediately:immediately];
+}
+
 #pragma mark - Private methods
 
 - (SessionIOS*)sessionForSaving {
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
index 3f78726..78e5d12 100644
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ b/ios/chrome/browser/translate/translate_egtest.mm
@@ -556,6 +556,8 @@
                      return !webState->IsLoading();
                    }),
                @"Failed to load large page on iOS 12.");
+    if (webState->ContentIsHTML())
+      web::WaitUntilWindowIdInjected(webState);
   } else {
     [ChromeEarlGrey loadURL:URL];
   }
diff --git a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
index 654c538d..b2de2ad3 100644
--- a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
+++ b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
@@ -99,17 +99,6 @@
     self.isAccessibilityElement = YES;
     [self addSubviews];
     [self setViewConstraints];
-    _imageView.contentMode = UIViewContentModeCenter;
-    _imageView.layer.masksToBounds = YES;
-    _imageView.contentMode = UIViewContentModeScaleAspectFit;
-
-    _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-    _textLabel.adjustsFontForContentSizeCategory = YES;
-    _textLabel.textColor = UIColor.blackColor;
-    _detailTextLabel.font =
-        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
-    _detailTextLabel.adjustsFontForContentSizeCategory = YES;
-    _detailTextLabel.textColor = UIColorFromRGB(kSettingsCellsDetailTextColor);
   }
   return self;
 }
@@ -121,6 +110,11 @@
 
   _imageView = [[UIImageView alloc] init];
   _imageView.translatesAutoresizingMaskIntoConstraints = NO;
+  _imageView.contentMode = UIViewContentModeCenter;
+  _imageView.layer.masksToBounds = YES;
+  _imageView.contentMode = UIViewContentModeScaleAspectFit;
+  // Creates the image rounded corners.
+  _imageView.layer.cornerRadius = kHorizontalImageFixedSize / 2.0f;
   [contentView addSubview:_imageView];
 
   _errorIcon = [[UIImageView alloc] init];
@@ -129,10 +123,17 @@
 
   _textLabel = [[UILabel alloc] init];
   _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  _textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+  _textLabel.adjustsFontForContentSizeCategory = YES;
+  _textLabel.textColor = UIColor.blackColor;
   [contentView addSubview:_textLabel];
 
   _detailTextLabel = [[UILabel alloc] init];
   _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  _detailTextLabel.font =
+      [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
+  _detailTextLabel.adjustsFontForContentSizeCategory = YES;
+  _detailTextLabel.textColor = UIColorFromRGB(kSettingsCellsDetailTextColor);
   [contentView addSubview:_detailTextLabel];
 }
 
@@ -218,9 +219,6 @@
 - (void)layoutSubviews {
   [super layoutSubviews];
 
-  // Creates the image rounded corners.
-  _imageView.layer.cornerRadius = _imageView.bounds.size.width / 2.0f;
-
   // Adjust the leading margin depending on existence of image.
   if (_imageView.image) {
     _textLeadingAnchorConstraint.constant =
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
index 8ea61502..368ea24d 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
@@ -578,6 +578,9 @@
 - (void)handleTextInputDidEndEditing:(NSNotification*)notification {
   self.editingUIKitTextInput = NO;
   [self continueCustomKeyboardView];
+  if (IsIPadIdiom()) {
+    [self updateSuggestionsIfNeeded];
+  }
 }
 
 #pragma mark - PasswordFetcherDelegate
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
index 110c9f3..2b624cd 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
@@ -125,9 +125,12 @@
 }
 
 - (void)generateAndOfferPassword {
+  if (![self isLastFocusedElementPasswordField])
+    return;
   web::WebState* webState = self.webStateList->GetActiveWebState();
   PasswordTabHelper::FromWebState(webState)->GenerateAndOfferPassword(
-      base::SysUTF8ToNSString(self.lastFocusedFormName));
+      base::SysUTF8ToNSString(self.lastFocusedFormName),
+      base::SysUTF8ToNSString(self.lastFocusedElementIdentifier), nil);
 }
 
 #pragma mark - FormActivityObserver
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index af09e3c..2faf61b 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -42,7 +42,6 @@
 #include "ios/chrome/browser/first_run/first_run.h"
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
 #import "ios/chrome/browser/language/url_language_histogram_factory.h"
-#import "ios/chrome/browser/metrics/new_tab_page_uma.h"
 #import "ios/chrome/browser/metrics/size_class_recorder.h"
 #include "ios/chrome/browser/metrics/tab_usage_recorder.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
@@ -161,7 +160,6 @@
 #import "ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h"
 #include "ios/chrome/browser/upgrade/upgrade_center.h"
 #import "ios/chrome/browser/url_loading/url_loading_util.h"
-#import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h"
 #import "ios/chrome/browser/web/blocked_popup_tab_helper.h"
 #import "ios/chrome/browser/web/image_fetch_tab_helper.h"
 #import "ios/chrome/browser/web/load_timing_tab_helper.h"
@@ -2289,6 +2287,7 @@
       kForwardButtonGuide,
       kToolsMenuGuide,
       kTabSwitcherGuide,
+      kTranslateInfobarOptionsGuide,
       kSearchButtonGuide,
       kSecondaryToolbarGuide,
       kVoiceSearchButtonGuide,
@@ -3937,7 +3936,10 @@
 - (void)loadURLWithParams:(const ChromeLoadParams&)chromeParams {
   [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:YES];
 
-  switch (LoadURL(chromeParams, self.browserState, self.tabModel)) {
+  URLLoadResult result =
+      LoadURL(chromeParams, self.browserState, self.tabModel.webStateList,
+              /* SessionWindowRestoring */ self.tabModel);
+  switch (result) {
     case URLLoadResult::SWITCH_TO_TAB: {
       [self switchToTabWithParams:chromeParams.web_params];
       break;
diff --git a/ios/chrome/browser/ui/external_file_remover_factory.h b/ios/chrome/browser/ui/external_file_remover_factory.h
index 48b7faf..e277736 100644
--- a/ios/chrome/browser/ui/external_file_remover_factory.h
+++ b/ios/chrome/browser/ui/external_file_remover_factory.h
@@ -7,13 +7,9 @@
 
 #include <memory>
 
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -30,7 +26,7 @@
   static ExternalFileRemoverFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<ExternalFileRemoverFactory>;
+  friend class base::NoDestructor<ExternalFileRemoverFactory>;
 
   ExternalFileRemoverFactory();
   ~ExternalFileRemoverFactory() override;
diff --git a/ios/chrome/browser/ui/external_file_remover_factory.mm b/ios/chrome/browser/ui/external_file_remover_factory.mm
index 8773b65..be12b26 100644
--- a/ios/chrome/browser/ui/external_file_remover_factory.mm
+++ b/ios/chrome/browser/ui/external_file_remover_factory.mm
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
@@ -26,7 +26,8 @@
 
 // static
 ExternalFileRemoverFactory* ExternalFileRemoverFactory::GetInstance() {
-  return base::Singleton<ExternalFileRemoverFactory>::get();
+  static base::NoDestructor<ExternalFileRemoverFactory> instance;
+  return instance.get();
 }
 
 ExternalFileRemoverFactory::ExternalFileRemoverFactory()
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
index dcdf6430..6dbeae97 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #import "ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge.h"
+#import "ios/chrome/browser/ui/fullscreen/scoped_fullscreen_disabler.h"
 
 class FullscreenModelObserver;
 
@@ -161,6 +162,10 @@
   // and toolbar height.
   void UpdateProgress();
 
+  // Updates the disabled counter depending on the current values of
+  // |scroll_view_height_| and |content_height_|.
+  void UpdateDisabledCounterForContentHeight();
+
   // Setter for |progress_|.  Notifies observers of the new value if
   // |notify_observers| is true.
   void SetProgress(CGFloat progress);
@@ -199,6 +204,8 @@
   CGFloat top_inset_ = 0.0;
   // How many currently-running features require the toolbar be visible.
   size_t disabled_counter_ = 0;
+  // Whether fullscreen is disabled for short content.
+  bool disabled_for_short_content_ = false;
   // Whether the main content is being scrolled.
   bool scrolling_ = false;
   // Whether the scroll view is zooming.
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
index 5030ffd..c6a1133 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
@@ -128,6 +128,7 @@
 
 void FullscreenModel::SetScrollViewHeight(CGFloat scroll_view_height) {
   scroll_view_height_ = scroll_view_height;
+  UpdateDisabledCounterForContentHeight();
 }
 
 CGFloat FullscreenModel::GetScrollViewHeight() const {
@@ -136,6 +137,7 @@
 
 void FullscreenModel::SetContentHeight(CGFloat content_height) {
   content_height_ = content_height;
+  UpdateDisabledCounterForContentHeight();
 }
 
 CGFloat FullscreenModel::GetContentHeight() const {
@@ -252,13 +254,24 @@
                            : ScrollAction::kUpdateBaseOffsetAndProgress;
 }
 
+void FullscreenModel::UpdateBaseOffset() {
+  base_offset_ = y_content_offset_ - (1.0 - progress_) * toolbar_height_delta();
+}
+
 void FullscreenModel::UpdateProgress() {
   CGFloat delta = base_offset_ - y_content_offset_;
   SetProgress(1.0 + delta / toolbar_height_delta());
 }
 
-void FullscreenModel::UpdateBaseOffset() {
-  base_offset_ = y_content_offset_ - (1.0 - progress_) * toolbar_height_delta();
+void FullscreenModel::UpdateDisabledCounterForContentHeight() {
+  bool disable = content_height_ < scroll_view_height_;
+  if (disabled_for_short_content_ == disable)
+    return;
+  disabled_for_short_content_ = disable;
+  if (disable)
+    IncrementDisabledCounter();
+  else
+    DecrementDisabledCounter();
 }
 
 void FullscreenModel::SetProgress(CGFloat progress) {
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
index 4d52924..a763d78 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
@@ -249,3 +249,16 @@
   check_insets(model().current_toolbar_insets(), kHiddenProgress);
   check_insets(model().min_toolbar_insets(), kHiddenProgress);
 }
+
+// Tests that the model is disabled when the content height is less than the
+// scroll view height.
+TEST_F(FullscreenModelTest, DisableForShortContent) {
+  ASSERT_TRUE(model().enabled());
+  // The model should be disabled when the rendered content height is less than
+  // the height of the scroll view.
+  model().SetContentHeight(model().GetScrollViewHeight() - 1.0);
+  EXPECT_FALSE(model().enabled());
+  // Reset the height to kContentHeight and verify that the model is re-enabled.
+  model().SetContentHeight(model().GetScrollViewHeight() + 1.0);
+  EXPECT_TRUE(model().enabled());
+}
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer_unittest.mm
index fb5ee56..5a2d43e 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer_unittest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer_unittest.mm
@@ -34,8 +34,8 @@
     _model.SetExpandedToolbarHeight(kTopToolbarExpandedHeight);
     _model.SetCollapsedToolbarHeight(kTopToolbarCollapsedHeight);
     _model.SetBottomToolbarHeight(kBottomToolbarHeight);
-    _model.SetScrollViewHeight(700);
     _model.SetContentHeight(1000);
+    _model.SetScrollViewHeight(700);
     _model.SetScrollViewIsScrolling(true);
     _model.SetYContentOffset(10);
 
diff --git a/ios/chrome/browser/ui/infobars/BUILD.gn b/ios/chrome/browser/ui/infobars/BUILD.gn
index 118130c..2de84bac 100644
--- a/ios/chrome/browser/ui/infobars/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/BUILD.gn
@@ -17,7 +17,6 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/tabs",
-    "//ios/chrome/browser/translate",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 9341a97c..a873d44 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -17,6 +17,7 @@
   deps = [
     ":tab_switcher",
     "//base",
+    "//components/translate/core/browser",
     "//ios/chrome/app/resources:launchscreen_xib",
     "//ios/chrome/browser",
     "//ios/chrome/browser/app_launcher",
@@ -27,6 +28,7 @@
     "//ios/chrome/browser/device_sharing",
     "//ios/chrome/browser/download",
     "//ios/chrome/browser/download:features",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/store_kit",
diff --git a/ios/chrome/browser/ui/main/browser_coordinator.mm b/ios/chrome/browser/ui/main/browser_coordinator.mm
index e736d3d5..4c443d1 100644
--- a/ios/chrome/browser/ui/main/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/main/browser_coordinator.mm
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/scoped_observer.h"
+#include "components/translate/core/browser/translate_prefs.h"
 #import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
 #import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
 #import "ios/chrome/browser/autofill/autofill_tab_helper.h"
@@ -37,6 +38,7 @@
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
 #import "ios/chrome/browser/ui/snackbar/snackbar_coordinator.h"
 #import "ios/chrome/browser/ui/translate/language_selection_coordinator.h"
+#import "ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.h"
 #import "ios/chrome/browser/web/repost_form_tab_helper.h"
 #import "ios/chrome/browser/web/repost_form_tab_helper_delegate.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -108,6 +110,11 @@
 // Coordinator for presenting SKStoreProductViewController.
 @property(nonatomic, strong) StoreKitCoordinator* storeKitCoordinator;
 
+// Coordinator for the translate infobar's language selection and translate
+// option popup menus.
+@property(nonatomic, strong)
+    TranslatePopupMenuCoordinator* translatePopupMenuCoordinator;
+
 @end
 
 @implementation BrowserCoordinator {
@@ -238,11 +245,19 @@
   self.formInputAccessoryCoordinator.delegate = self;
   [self.formInputAccessoryCoordinator start];
 
-  self.languageSelectionCoordinator = [[LanguageSelectionCoordinator alloc]
-      initWithBaseViewController:self.viewController
-                    browserState:self.browserState
-                    webStateList:self.tabModel.webStateList];
-  [self.languageSelectionCoordinator start];
+  if (base::FeatureList::IsEnabled(translate::kCompactTranslateInfobarIOS)) {
+    self.translatePopupMenuCoordinator = [[TranslatePopupMenuCoordinator alloc]
+        initWithBaseViewController:self.viewController
+                      browserState:self.browserState
+                      webStateList:self.tabModel.webStateList];
+    [self.translatePopupMenuCoordinator start];
+  } else {
+    self.languageSelectionCoordinator = [[LanguageSelectionCoordinator alloc]
+        initWithBaseViewController:self.viewController
+                      browserState:self.browserState
+                      webStateList:self.tabModel.webStateList];
+    [self.languageSelectionCoordinator start];
+  }
 
   self.pageInfoCoordinator = [[PageInfoLegacyCoordinator alloc]
       initWithBaseViewController:self.viewController
@@ -315,6 +330,9 @@
 
   [self.storeKitCoordinator stop];
   self.storeKitCoordinator = nil;
+
+  [self.translatePopupMenuCoordinator stop];
+  self.translatePopupMenuCoordinator = nil;
 }
 
 #pragma mark - BrowserCoordinatorCommands
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.h b/ios/chrome/browser/ui/main/browser_view_wrangler.h
index 8a356a60..06fe485 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.h
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.h
@@ -47,17 +47,17 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Creates the main tab model used by the receiver, using the browser state
+// Creates the main Browser used by the receiver, using the browser state
 // and tab model observer it was configured with. This should be done before
-// the main interface is accessed, usually immediatley after initialization.
-- (void)createMainTabModel;
+// the main interface is accessed, usually immediately after initialization.
+- (void)createMainBrowser;
 
 // Update the device sharing manager. This should be done after updates to the
 // tab model. This class creates and manages the state of the sharing manager.
 - (void)updateDeviceSharingManager;
 
-// Destroy and rebuild the incognito tab model.
-- (void)destroyAndRebuildIncognitoTabModel;
+// Destroy and rebuild the incognito Browser.
+- (void)destroyAndRebuildIncognitoBrowser;
 
 // Called before the instance is deallocated.
 - (void)shutdown;
@@ -66,8 +66,6 @@
 
 @interface BrowserViewWrangler (Testing)
 @property(nonatomic, readonly) DeviceSharingManager* deviceSharingManager;
-@property(nonatomic) TabModel* mainTabModel;
-@property(nonatomic) TabModel* otrTabModel;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_MAIN_BROWSER_VIEW_WRANGLER_H_
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.mm b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
index a65ed4c..94026829 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
@@ -10,6 +10,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/crash_report/crash_report_helper.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
@@ -87,6 +88,9 @@
   __weak id<ApplicationCommands> _applicationCommandEndpoint;
   __weak id<BrowserStateStorageSwitching> _storageSwitcher;
   BOOL _isShutdown;
+
+  std::unique_ptr<Browser> _mainBrowser;
+  std::unique_ptr<Browser> _otrBrowser;
 }
 
 @property(nonatomic, strong, readwrite) WrangledBrowser* mainInterface;
@@ -95,8 +99,10 @@
 // Backing objects.
 @property(nonatomic) BrowserCoordinator* mainBrowserCoordinator;
 @property(nonatomic) BrowserCoordinator* incognitoBrowserCoordinator;
-@property(nonatomic) TabModel* mainTabModel;
-@property(nonatomic) TabModel* otrTabModel;
+//@property(nonatomic, readonly) TabModel* mainTabModel;
+//@property(nonatomic, readonly) TabModel* otrTabModel;
+@property(nonatomic, readonly) Browser* mainBrowser;
+@property(nonatomic, readonly) Browser* otrBrowser;
 
 // Responsible for maintaining all state related to sharing to other devices.
 // Redeclared readwrite from the readonly declaration in the Testing interface.
@@ -109,23 +115,23 @@
 - (TabModel*)tabModelForBrowserState:(ios::ChromeBrowserState*)browserState
                                empty:(BOOL)empty;
 
+// Setters for the main and otr Browsers.
+- (void)setMainBrowser:(std::unique_ptr<Browser>)browser;
+- (void)setOtrBrowser:(std::unique_ptr<Browser>)browser;
+
 // Creates a new off-the-record ("incognito") browser state for |_browserState|,
-// then calls -tabModelForBrowserState:empty: and returns the (autoreleased)
+// then calls -tabModelForBrowserState:empty: and returns a Browser for the
 // result.
-- (TabModel*)buildOtrTabModel:(BOOL)empty;
+- (std::unique_ptr<Browser>)buildOtrBrowser:(BOOL)empty;
 
 // Creates the correct BrowserCoordinator for the corresponding browser state
-// and tab model.
-- (BrowserCoordinator*)coordinatorForBrowserState:
-                           (ios::ChromeBrowserState*)browserState
-                                         tabModel:(TabModel*)tabModel;
+// and Browser.
+- (BrowserCoordinator*)coordinatorForBrowser:(Browser*)browser;
 @end
 
 @implementation BrowserViewWrangler
 
 @synthesize currentInterface = _currentInterface;
-@synthesize mainTabModel = _mainTabModel;
-@synthesize otrTabModel = _otrTabModel;
 
 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
                     tabModelObserver:(id<TabModelObserver>)tabModelObserver
@@ -146,12 +152,15 @@
   DCHECK(_isShutdown) << "-shutdown must be called before -dealloc";
 }
 
-- (void)createMainTabModel {
-  self.mainTabModel = [self tabModelForBrowserState:_browserState empty:NO];
+- (void)createMainBrowser {
+  TabModel* tabModel = [self tabModelForBrowserState:_browserState empty:NO];
+
+  _mainBrowser = Browser::Create(_browserState, tabModel);
   // Follow loaded URLs in the main tab model to send those in case of
   // crashes.
-  breakpad::MonitorURLsForTabModel(_mainTabModel);
-  ios::GetChromeBrowserProvider()->InitializeCastService(_mainTabModel);
+  breakpad::MonitorURLsForTabModel(self.mainBrowser->GetTabModel());
+  ios::GetChromeBrowserProvider()->InitializeCastService(
+      self.mainBrowser->GetTabModel());
 }
 
 #pragma mark - BrowserViewInformation property implementations
@@ -186,9 +195,7 @@
   if (!_mainInterface) {
     // The backing coordinator should not have been created yet.
     DCHECK(!_mainBrowserCoordinator);
-    _mainBrowserCoordinator =
-        [self coordinatorForBrowserState:_browserState
-                                tabModel:self.mainTabModel];
+    _mainBrowserCoordinator = [self coordinatorForBrowser:self.mainBrowser];
     [_mainBrowserCoordinator start];
     DCHECK(_mainBrowserCoordinator.viewController);
     _mainInterface =
@@ -204,9 +211,7 @@
     ios::ChromeBrowserState* otrBrowserState =
         _browserState->GetOffTheRecordChromeBrowserState();
     DCHECK(otrBrowserState);
-    _incognitoBrowserCoordinator =
-        [self coordinatorForBrowserState:otrBrowserState
-                                tabModel:self.otrTabModel];
+    _incognitoBrowserCoordinator = [self coordinatorForBrowser:self.otrBrowser];
     [_incognitoBrowserCoordinator start];
     DCHECK(_incognitoBrowserCoordinator.viewController);
     _incognitoInterface = [[WrangledBrowser alloc]
@@ -215,57 +220,53 @@
   return _incognitoInterface;
 }
 
-- (TabModel*)mainTabModel {
-  DCHECK(_mainTabModel)
-      << "-createMainTabModel must be called before -mainTabModel is accessed.";
-  return _mainTabModel;
+- (Browser*)mainBrowser {
+  DCHECK(_mainBrowser.get())
+      << "-createMainBrowser must be called before -mainBrowser is accessed.";
+  return _mainBrowser.get();
 }
 
-- (void)setMainTabModel:(TabModel*)mainTabModel {
-  if (_mainTabModel == mainTabModel)
-    return;
+- (Browser*)otrBrowser {
+  if (!_otrBrowser) {
+    _otrBrowser = [self buildOtrBrowser:NO];
+  }
+  return _otrBrowser.get();
+}
 
-  if (_mainTabModel) {
-    breakpad::StopMonitoringTabStateForTabModel(_mainTabModel);
-    breakpad::StopMonitoringURLsForTabModel(_mainTabModel);
-    [_mainTabModel browserStateDestroyed];
+- (void)setMainBrowser:(std::unique_ptr<Browser>)mainBrowser {
+  if (_mainBrowser.get()) {
+    TabModel* tabModel = self.mainBrowser->GetTabModel();
+    breakpad::StopMonitoringTabStateForTabModel(tabModel);
+    breakpad::StopMonitoringURLsForTabModel(tabModel);
+    [tabModel browserStateDestroyed];
     if (_tabModelObserver) {
-      [_mainTabModel removeObserver:_tabModelObserver];
+      [tabModel removeObserver:_tabModelObserver];
     }
-    [_mainTabModel removeObserver:self];
+    [tabModel removeObserver:self];
   }
 
-  _mainTabModel = mainTabModel;
+  _mainBrowser = std::move(mainBrowser);
 }
 
-- (TabModel*)otrTabModel {
-  if (!_otrTabModel) {
-    self.otrTabModel = [self buildOtrTabModel:NO];
-  }
-  return _otrTabModel;
-}
-
-- (void)setOtrTabModel:(TabModel*)otrTabModel {
-  if (_otrTabModel == otrTabModel)
-    return;
-
-  if (_otrTabModel) {
-    breakpad::StopMonitoringTabStateForTabModel(_otrTabModel);
-    [_otrTabModel browserStateDestroyed];
+- (void)setOtrBrowser:(std::unique_ptr<Browser>)otrBrowser {
+  if (_otrBrowser.get()) {
+    TabModel* tabModel = self.otrBrowser->GetTabModel();
+    breakpad::StopMonitoringTabStateForTabModel(tabModel);
+    [tabModel browserStateDestroyed];
     if (_tabModelObserver) {
-      [_otrTabModel removeObserver:_tabModelObserver];
+      [tabModel removeObserver:_tabModelObserver];
     }
-    [_otrTabModel removeObserver:self];
+    [tabModel removeObserver:self];
   }
 
-  _otrTabModel = otrTabModel;
+  _otrBrowser = std::move(otrBrowser);
 }
 
 #pragma mark - BrowserViewInformation methods
 
 - (void)haltAllTabs {
-  [self.mainTabModel haltAllTabs];
-  [self.otrTabModel haltAllTabs];
+  [self.mainBrowser->GetTabModel() haltAllTabs];
+  [self.otrBrowser->GetTabModel() haltAllTabs];
 }
 
 - (void)cleanDeviceSharingManager {
@@ -302,15 +303,15 @@
   [self.deviceSharingManager updateActiveURL:activeURL];
 }
 
-- (void)destroyAndRebuildIncognitoTabModel {
+- (void)destroyAndRebuildIncognitoBrowser {
   // It is theoretically possible that a Tab has been added to |_otrTabModel|
   // since the deletion has been scheduled. It is unlikely to happen for real
   // because it would require superhuman speed.
-  DCHECK(![_otrTabModel count]);
+  DCHECK(![self.otrBrowser->GetTabModel() count]);
   DCHECK(_browserState);
 
   // Stop watching the OTR tab model's state for crashes.
-  breakpad::StopMonitoringTabStateForTabModel(self.otrTabModel);
+  breakpad::StopMonitoringTabStateForTabModel(self.otrBrowser->GetTabModel());
 
   // At this stage, a new incognitoBrowserCoordinator shouldn't be lazily
   // constructed by calling the property getter.
@@ -324,7 +325,7 @@
 
     // There's no guarantee the tab model was ever added to the BVC (or even
     // that the BVC was created), so ensure the tab model gets notified.
-    self.otrTabModel = nil;
+    [self setOtrBrowser:nullptr];
     if (otrBVCIsCurrent) {
       _currentInterface = nil;
     }
@@ -336,8 +337,8 @@
   // possible to prevent the tabChanged notification being sent. Otherwise,
   // when it is created, a notification with no tabs will be sent, and it will
   // be immediately deleted.
-  self.otrTabModel = [self buildOtrTabModel:YES];
-  DCHECK(![self.otrTabModel count]);
+  [self setOtrBrowser:[self buildOtrBrowser:YES]];
+  DCHECK(![self.otrBrowser->GetTabModel() count]);
   DCHECK(_browserState->HasOffTheRecordChromeBrowserState());
 
   if (otrBVCIsCurrent) {
@@ -359,24 +360,26 @@
   [_incognitoBrowserCoordinator stop];
   _incognitoBrowserCoordinator = nil;
 
-  [_mainTabModel closeAllTabs];
-  [_otrTabModel closeAllTabs];
+  [self.mainBrowser->GetTabModel() closeAllTabs];
+  [self.otrBrowser->GetTabModel() closeAllTabs];
   // Handles removing observers and stopping breakpad monitoring.
-  [self setMainTabModel:nil];
-  [self setOtrTabModel:nil];
+  [self setMainBrowser:nullptr];
+  [self setOtrBrowser:nullptr];
 
   _browserState = nullptr;
 }
 
 #pragma mark - Internal methods
 
-- (TabModel*)buildOtrTabModel:(BOOL)empty {
+- (std::unique_ptr<Browser>)buildOtrBrowser:(BOOL)empty {
   DCHECK(_browserState);
   // Ensure that the OTR ChromeBrowserState is created.
   ios::ChromeBrowserState* otrBrowserState =
       _browserState->GetOffTheRecordChromeBrowserState();
   DCHECK(otrBrowserState);
-  return [self tabModelForBrowserState:otrBrowserState empty:empty];
+  TabModel* tabModel = [self tabModelForBrowserState:otrBrowserState
+                                               empty:empty];
+  return Browser::Create(otrBrowserState, tabModel);
 }
 
 - (TabModel*)tabModelForBrowserState:(ios::ChromeBrowserState*)browserState
@@ -409,13 +412,11 @@
   return tabModel;
 }
 
-- (BrowserCoordinator*)coordinatorForBrowserState:
-                           (ios::ChromeBrowserState*)browserState
-                                         tabModel:(TabModel*)tabModel {
-  BrowserCoordinator* coordinator =
-      [[BrowserCoordinator alloc] initWithBaseViewController:nil
-                                                browserState:browserState];
-  coordinator.tabModel = tabModel;
+- (BrowserCoordinator*)coordinatorForBrowser:(Browser*)browser {
+  BrowserCoordinator* coordinator = [[BrowserCoordinator alloc]
+      initWithBaseViewController:nil
+                    browserState:browser->GetBrowserState()];
+  coordinator.tabModel = browser->GetTabModel();
   coordinator.applicationCommandHandler = _applicationCommandEndpoint;
   return coordinator;
 }
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
index ecdac90a..c4a0e2b 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
@@ -38,7 +38,7 @@
                   tabModelObserver:nil
         applicationCommandEndpoint:(id<ApplicationCommands>)nil
                    storageSwitcher:nil];
-    [wrangler createMainTabModel];
+    [wrangler createMainBrowser];
     // Test that BVC is created on demand.
     BrowserViewController* bvc = wrangler.mainInterface.bvc;
     EXPECT_NE(bvc, nil);
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index 3e4d781..02267bd 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -159,6 +159,9 @@
   // This is used to always process contentOffset changes on specific cases like
   // when playing the bounce back animation if no actions has been triggered.
   BOOL _forceStateUpdate;
+  // Instructs the controller to ignore the scroll event resulting from setting
+  // |disablingFullscreen| to YES.
+  BOOL _ignoreScrollForDisabledFullscreen;
   // True when the overscroll actions are disabled for loading.
   BOOL _isOverscrollActionsDisabledForLoading;
   // True when the pull gesture started close enough from the top and the
@@ -221,6 +224,9 @@
 @property(nonatomic, assign) BOOL scrollViewDragged;
 // Whether the scroll view's viewport is being adjusted by the content inset.
 @property(nonatomic, readonly) BOOL viewportAdjustsContentInset;
+// Whether fullscreen is disabled.
+@property(nonatomic, assign, getter=isDisablingFullscreen)
+    BOOL disablingFullscreen;
 
 // Registers notifications to lock the overscroll actions on certain UI states.
 - (void)registerNotifications;
@@ -369,8 +375,10 @@
 
 - (void)scrollViewDidScroll {
   if (!_forceStateUpdate && (![self isOverscrollActionEnabled] ||
-                             _performingScrollViewIndependentAnimation))
+                             _performingScrollViewIndependentAnimation ||
+                             _ignoreScrollForDisabledFullscreen)) {
     return;
+  }
 
   const UIEdgeInsets insets =
       TopContentInset(self.scrollView, -[self scrollView].contentOffset.y);
@@ -779,7 +787,7 @@
           postNotificationName:kOverscrollActionsDidEnd
                         object:self];
       [self resetScrollViewTopContentInset];
-      _fullscreenDisabler = nullptr;
+      self.disablingFullscreen = NO;
       if (_shouldInvalidate) {
         [self invalidate];
       }
@@ -792,13 +800,7 @@
           [[NSNotificationCenter defaultCenter]
               postNotificationName:kOverscrollActionsWillStart
                             object:self];
-          if (self.browserState) {
-            FullscreenController* fullscreenController =
-                FullscreenControllerFactory::GetInstance()->GetForBrowserState(
-                    self.browserState);
-            _fullscreenDisabler = std::make_unique<ScopedFullscreenDisabler>(
-                fullscreenController);
-          }
+          self.disablingFullscreen = YES;
         }
         [CATransaction begin];
         [CATransaction setDisableActions:YES];
@@ -874,6 +876,32 @@
   return [self.delegate overscrollActionsControllerHeaderInset:self];
 }
 
+- (BOOL)isDisablingFullscreen {
+  return _fullscreenDisabler.get() != nullptr;
+}
+
+- (void)setDisablingFullscreen:(BOOL)disablingFullscreen {
+  if (self.disablingFullscreen == disablingFullscreen)
+    return;
+  _fullscreenDisabler = nullptr;
+  // Don't try to disable fullscreen if the BrowserState has been detached, such
+  // as when an overscroll that begins just as the last incognito tab is closed.
+  if (!disablingFullscreen || !self.browserState)
+    return;
+  FullscreenController* fullscreenController =
+      FullscreenControllerFactory::GetInstance()->GetForBrowserState(
+          self.browserState);
+  // Disabling fullscreen will show the toolbars, which may potentially produce
+  // a |-scrollViewDidScroll| event if the browser viewport insets need to be
+  // updated.  |_ignoreScrollForDisabledFullscreen| is set to YES while the
+  // viewport insets are being updated for the disabled state so that this
+  // scroll event can be ignored.
+  _ignoreScrollForDisabledFullscreen = YES;
+  _fullscreenDisabler =
+      std::make_unique<ScopedFullscreenDisabler>(fullscreenController);
+  _ignoreScrollForDisabledFullscreen = NO;
+}
+
 #pragma mark - Bounce dynamic
 
 - (void)startBounceWithInitialVelocity:(CGPoint)velocity {
@@ -884,7 +912,6 @@
   CADisplayLink* dpLink =
       [CADisplayLink displayLinkWithTarget:self
                                   selector:@selector(updateBounce)];
-  [dpLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
   _dpLink = dpLink;
   memset(&_bounceState, 0, sizeof(_bounceState));
   if (self.overscrollState == OverscrollState::ACTION_READY) {
@@ -900,6 +927,16 @@
   _bounceState.headerInset = self.initialContentInset;
   _bounceState.time = CACurrentMediaTime();
   _bounceState.velocityInset = -velocity.y * 1000.0;
+
+  if (fabs(_bounceState.yInset - _bounceState.headerInset) < 0.5) {
+    // If no bounce is required, then clear state, as the necessary
+    // |-scrollViewDidScroll| callback will not be triggered to reset
+    // |overscrollState| to NO_PULL_STARTED.
+    [self stopBounce];
+    [self clear];
+  } else {
+    [dpLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+  }
 }
 
 - (void)stopBounce {
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 930d934..eaca48b5 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -9,8 +9,6 @@
     "about_chrome_table_view_controller.mm",
     "accounts_table_view_controller.h",
     "accounts_table_view_controller.mm",
-    "alpha_animated_collection_view_flow_layout.h",
-    "alpha_animated_collection_view_flow_layout.mm",
     "autofill_credit_card_edit_table_view_controller.h",
     "autofill_credit_card_edit_table_view_controller.mm",
     "autofill_credit_card_table_view_controller.h",
@@ -58,9 +56,6 @@
     "handoff_table_view_controller.mm",
     "import_data_table_view_controller.h",
     "import_data_table_view_controller.mm",
-    "legacy_autofill_edit_collection_view_controller+protected.h",
-    "legacy_autofill_edit_collection_view_controller.h",
-    "legacy_autofill_edit_collection_view_controller.mm",
     "material_cell_catalog_view_controller.h",
     "material_cell_catalog_view_controller.mm",
     "password_details_table_view_controller.h",
@@ -146,7 +141,6 @@
     "//components/resources",
     "//components/search_engines",
     "//components/signin/core/browser",
-    "//components/signin/ios/browser",
     "//components/strings",
     "//components/sync",
     "//components/translate/core/browser",
@@ -449,7 +443,7 @@
     ":settings",
     "//base",
     "//base/test:test_support",
-    "//components/signin/core/browser",
+    "//components/prefs",
     "//ios/chrome/app/strings:ios_chromium_strings_grit",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/browser_state",
diff --git a/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.h b/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.h
deleted file mode 100644
index a3e5ce9..0000000
--- a/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_ALPHA_ANIMATED_COLLECTION_VIEW_FLOW_LAYOUT_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_ALPHA_ANIMATED_COLLECTION_VIEW_FLOW_LAYOUT_H_
-
-#import "ios/third_party/material_components_ios/src/components/Collections/src/MDCCollectionViewFlowLayout.h"
-
-// An |MDCCollectionViewFlowLayout| that uses alpha for animation, which makes
-// rotation on the device look smoother.
-@interface AlphaAnimatedCollectionViewFlowLayout : MDCCollectionViewFlowLayout
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_ALPHA_ANIMATED_COLLECTION_VIEW_FLOW_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.mm b/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.mm
deleted file mode 100644
index 313bddc..0000000
--- a/ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.mm
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/alpha_animated_collection_view_flow_layout.h"
-
-#import <UIKit/UIKit.h>
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation AlphaAnimatedCollectionViewFlowLayout
-
-- (UICollectionViewLayoutAttributes*)
-initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath*)itemIndexPath {
-  UICollectionViewLayoutAttributes* attr = [[super
-      initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
-  attr.alpha = 0;
-  return attr;
-}
-
-- (UICollectionViewLayoutAttributes*)
-initialLayoutAttributesForAppearingSupplementaryElementOfKind:
-    (NSString*)elementKind
-                                                  atIndexPath:
-                                                      (NSIndexPath*)
-                                                          elementIndexPath {
-  UICollectionViewLayoutAttributes* attr = [[super
-      initialLayoutAttributesForAppearingSupplementaryElementOfKind:elementKind
-                                                        atIndexPath:
-                                                            elementIndexPath]
-      copy];
-  attr.alpha = 0;
-  return attr;
-}
-
-- (UICollectionViewLayoutAttributes*)
-finalLayoutAttributesForDisappearingItemAtIndexPath:
-    (NSIndexPath*)itemIndexPath {
-  UICollectionViewLayoutAttributes* attr = [[super
-      finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
-  attr.alpha = 0;
-  return attr;
-}
-
-- (UICollectionViewLayoutAttributes*)
-finalLayoutAttributesForDisappearingSupplementaryElementOfKind:
-    (NSString*)elementKind
-                                                   atIndexPath:
-                                                       (NSIndexPath*)
-                                                           elementIndexPath {
-  UICollectionViewLayoutAttributes* attr = [[super
-      finalLayoutAttributesForDisappearingSupplementaryElementOfKind:elementKind
-                                                         atIndexPath:
-                                                             elementIndexPath]
-      copy];
-  attr.alpha = 0;
-  return attr;
-}
-@end
diff --git a/ios/chrome/browser/ui/settings/cells/BUILD.gn b/ios/chrome/browser/ui/settings/cells/BUILD.gn
index e6c01298..77a76d9 100644
--- a/ios/chrome/browser/ui/settings/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/cells/BUILD.gn
@@ -22,8 +22,6 @@
     "encryption_item.mm",
     "passphrase_error_item.h",
     "passphrase_error_item.mm",
-    "password_details_item.h",
-    "password_details_item.mm",
     "settings_cells_constants.h",
     "settings_cells_constants.mm",
     "settings_detail_item.h",
@@ -86,7 +84,6 @@
     "copied_to_chrome_item_unittest.mm",
     "encryption_item_unittest.mm",
     "passphrase_error_item_unittest.mm",
-    "password_details_item_unittest.mm",
     "settings_multiline_detail_item_unittest.mm",
     "text_and_error_item_unittest.mm",
     "version_item_unittest.mm",
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/BUILD.gn b/ios/chrome/browser/ui/settings/cells/legacy/BUILD.gn
index 70633ec..78eac0e 100644
--- a/ios/chrome/browser/ui/settings/cells/legacy/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/cells/legacy/BUILD.gn
@@ -6,14 +6,8 @@
   sources = [
     "legacy_account_signin_item.h",
     "legacy_account_signin_item.mm",
-    "legacy_autofill_data_item.h",
-    "legacy_autofill_data_item.mm",
     "legacy_settings_detail_item.h",
     "legacy_settings_detail_item.mm",
-    "legacy_settings_image_detail_text_item.h",
-    "legacy_settings_image_detail_text_item.mm",
-    "legacy_settings_switch_item.h",
-    "legacy_settings_switch_item.mm",
     "legacy_sync_switch_item.h",
     "legacy_sync_switch_item.mm",
   ]
@@ -36,7 +30,6 @@
   testonly = true
   sources = [
     "legacy_account_signin_item_unittest.mm",
-    "legacy_autofill_data_item_unittest.mm",
     "legacy_sync_switch_item_unittest.mm",
   ]
 
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h b/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h
deleted file mode 100644
index 2d19770..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h
+++ /dev/null
@@ -1,44 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_AUTOFILL_DATA_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_AUTOFILL_DATA_ITEM_H_
-
-#import <UIKit/UIKit.h>
-
-#include <string>
-
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-// Item for autofill profile (address) or credit card.
-@interface LegacyAutofillDataItem : CollectionViewItem
-
-// Only deletable items will enter edit mode.
-@property(nonatomic, assign, getter=isDeletable) BOOL deletable;
-
-// The GUID used by the PersonalDataManager to identify data elements (e.g.
-// profiles and credit cards).
-@property(nonatomic, assign) std::string GUID;
-
-@property(nonatomic, copy) NSString* text;
-@property(nonatomic, copy) NSString* leadingDetailText;
-@property(nonatomic, copy) NSString* trailingDetailText;
-
-// The accessory type for the represented cell.
-@property(nonatomic) MDCCollectionViewCellAccessoryType accessoryType;
-
-@end
-
-// Cell for autofill data with two leading text labels and one trailing text
-// label.
-@interface LegacyAutofillDataCell : MDCCollectionViewCell
-
-@property(nonatomic, readonly, strong) UILabel* textLabel;
-@property(nonatomic, readonly, strong) UILabel* leadingDetailTextLabel;
-@property(nonatomic, readonly, strong) UILabel* trailingDetailTextLabel;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_AUTOFILL_DATA_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.mm b/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.mm
deleted file mode 100644
index 14a8e51..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.mm
+++ /dev/null
@@ -1,185 +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 "ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h"
-
-#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
-#include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// Padding used on the leading and trailing edges of the cell.
-const CGFloat kHorizontalPadding = 16;
-// Padding used on the top and bottom edges of the cell.
-const CGFloat kVerticalPadding = 16;
-}
-
-@implementation LegacyAutofillDataItem
-
-@synthesize deletable = _deletable;
-@synthesize GUID = _GUID;
-@synthesize text = _text;
-@synthesize leadingDetailText = _leadingDetailText;
-@synthesize trailingDetailText = _trailingDetailText;
-@synthesize accessoryType = _accessoryType;
-
-- (instancetype)initWithType:(NSInteger)type {
-  self = [super initWithType:type];
-  if (self) {
-    self.cellClass = [LegacyAutofillDataCell class];
-  }
-  return self;
-}
-
-#pragma mark - CollectionViewItem
-
-- (void)configureCell:(LegacyAutofillDataCell*)cell {
-  [super configureCell:cell];
-  cell.textLabel.text = self.text;
-  cell.leadingDetailTextLabel.text = self.leadingDetailText;
-  cell.trailingDetailTextLabel.text = self.trailingDetailText;
-  [cell cr_setAccessoryType:self.accessoryType];
-}
-
-@end
-
-@implementation LegacyAutofillDataCell {
-  NSLayoutConstraint* _textLabelWidthConstraint;
-}
-
-@synthesize textLabel = _textLabel;
-@synthesize leadingDetailTextLabel = _leadingDetailTextLabel;
-@synthesize trailingDetailTextLabel = _trailingDetailTextLabel;
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    self.isAccessibilityElement = YES;
-    [self addSubviews];
-    [self setDefaultViewStyling];
-    [self setViewConstraints];
-  }
-  return self;
-}
-
-// Creates and adds subviews.
-- (void)addSubviews {
-  UIView* contentView = self.contentView;
-
-  _textLabel = [[UILabel alloc] init];
-  _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  [contentView addSubview:_textLabel];
-
-  _leadingDetailTextLabel = [[UILabel alloc] init];
-  _leadingDetailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  [contentView addSubview:_leadingDetailTextLabel];
-
-  _trailingDetailTextLabel = [[UILabel alloc] init];
-  _trailingDetailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  [contentView addSubview:_trailingDetailTextLabel];
-}
-
-// Sets default font and text colors for labels.
-- (void)setDefaultViewStyling {
-  _textLabel.numberOfLines = 0;
-  _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
-  _textLabel.font = [UIFont systemFontOfSize:kUIKitMainFontSize];
-  _textLabel.textColor = UIColorFromRGB(kUIKitMainTextColor);
-
-  _leadingDetailTextLabel.numberOfLines = 0;
-  _leadingDetailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
-  _leadingDetailTextLabel.font =
-      [UIFont systemFontOfSize:kUIKitMultilineDetailFontSize];
-  _leadingDetailTextLabel.textColor =
-      UIColorFromRGB(kUIKitMultilineDetailTextColor);
-
-  _trailingDetailTextLabel.font =
-      [UIFont systemFontOfSize:kUIKitDetailFontSize];
-  _trailingDetailTextLabel.textColor = UIColorFromRGB(kUIKitDetailTextColor);
-}
-
-// Sets constraints on subviews.
-- (void)setViewConstraints {
-  UIView* contentView = self.contentView;
-
-  // Set up the width constraint for the text label. It is activated here
-  // and updated in layoutSubviews.
-  _textLabelWidthConstraint =
-      [_textLabel.widthAnchor constraintEqualToConstant:0];
-
-  [NSLayoutConstraint activateConstraints:@[
-    // Set horizontal anchors.
-    [_textLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor
-                                             constant:kHorizontalPadding],
-    [_leadingDetailTextLabel.leadingAnchor
-        constraintEqualToAnchor:_textLabel.leadingAnchor],
-    [_trailingDetailTextLabel.trailingAnchor
-        constraintEqualToAnchor:contentView.trailingAnchor
-                       constant:-kHorizontalPadding],
-
-    // Set width anchors.
-    [_leadingDetailTextLabel.widthAnchor
-        constraintEqualToAnchor:_textLabel.widthAnchor],
-    _textLabelWidthConstraint,
-
-    // Set vertical anchors.
-    [_leadingDetailTextLabel.topAnchor
-        constraintEqualToAnchor:_textLabel.bottomAnchor],
-    [_trailingDetailTextLabel.centerYAnchor
-        constraintEqualToAnchor:contentView.centerYAnchor],
-  ]];
-
-  AddOptionalVerticalPadding(contentView, _textLabel, _leadingDetailTextLabel,
-                             kVerticalPadding);
-}
-
-// Implement -layoutSubviews as per instructions in documentation for
-// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
-- (void)layoutSubviews {
-  [super layoutSubviews];
-
-  // Size the trailing detail label to determine how much width it wants.
-  [self.trailingDetailTextLabel sizeToFit];
-
-  // Update the text label's width constraint.
-  CGFloat availableWidth =
-      CGRectGetWidth(self.contentView.bounds) - (3 * kHorizontalPadding);
-  CGFloat trailingDetailLabelWidth =
-      CGRectGetWidth(self.trailingDetailTextLabel.frame);
-  _textLabelWidthConstraint.constant =
-      availableWidth - trailingDetailLabelWidth;
-
-  [super layoutSubviews];
-}
-
-#pragma mark - UICollectionReusableView
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.textLabel.text = nil;
-  self.leadingDetailTextLabel.text = nil;
-  self.trailingDetailTextLabel.text = nil;
-  self.accessoryType = MDCCollectionViewCellAccessoryNone;
-}
-
-#pragma mark - NSObject(Accessibility)
-
-- (NSString*)accessibilityLabel {
-  if (self.trailingDetailTextLabel.text) {
-    return [NSString stringWithFormat:@"%@, %@, %@", self.textLabel.text,
-                                      self.leadingDetailTextLabel.text,
-                                      self.trailingDetailTextLabel.text];
-  }
-  return [NSString stringWithFormat:@"%@, %@", self.textLabel.text,
-                                    self.leadingDetailTextLabel.text];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item_unittest.mm b/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item_unittest.mm
deleted file mode 100644
index d5141bba..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item_unittest.mm
+++ /dev/null
@@ -1,47 +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 "ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h"
-
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#import "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using LegacyAutofillDataItemTest = PlatformTest;
-
-// Tests that the UILabels are set properly after a call to |configureCell:|.
-TEST_F(LegacyAutofillDataItemTest, TextLabels) {
-  LegacyAutofillDataItem* item =
-      [[LegacyAutofillDataItem alloc] initWithType:0];
-  NSString* mainText = @"Main text";
-  NSString* leadingDetailText = @"Leading detail text";
-  NSString* trailingDetailText = @"Trailing detail text";
-
-  item.text = mainText;
-  item.leadingDetailText = leadingDetailText;
-  item.trailingDetailText = trailingDetailText;
-  item.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
-
-  id cell = [[[item cellClass] alloc] init];
-  ASSERT_TRUE([cell isMemberOfClass:[LegacyAutofillDataCell class]]);
-
-  LegacyAutofillDataCell* autofillDataCell = cell;
-  EXPECT_FALSE(autofillDataCell.textLabel.text);
-  EXPECT_FALSE(autofillDataCell.leadingDetailTextLabel.text);
-  EXPECT_FALSE(autofillDataCell.trailingDetailTextLabel.text);
-  EXPECT_EQ(MDCCollectionViewCellAccessoryNone, autofillDataCell.accessoryType);
-
-  [item configureCell:cell];
-  EXPECT_NSEQ(mainText, autofillDataCell.textLabel.text);
-  EXPECT_NSEQ(leadingDetailText, autofillDataCell.leadingDetailTextLabel.text);
-  EXPECT_NSEQ(trailingDetailText,
-              autofillDataCell.trailingDetailTextLabel.text);
-  EXPECT_EQ(MDCCollectionViewCellAccessoryCheckmark,
-            autofillDataCell.accessoryType);
-}
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.h b/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.h
deleted file mode 100644
index d2fb74e..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_IMAGE_DETAIL_TEXT_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_IMAGE_DETAIL_TEXT_ITEM_H_
-
-#import <Foundation/Foundation.h>
-
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-// LegacySettingsImageDetailTextItem is an item that displays an image, a title
-// and a detail text (optional). This item uses multi-lines text field. It also
-// contains acommand id.
-// This class is intended to be used with Google services settings view.
-@interface LegacySettingsImageDetailTextItem : CollectionViewItem
-
-// The image to display.
-@property(nonatomic, strong) UIImage* image;
-
-// The title text to display.
-@property(nonatomic, copy) NSString* text;
-
-// The detail text to display.
-@property(nonatomic, copy) NSString* detailText;
-
-// Command to trigger when the switch is toggled. The default value is 0.
-@property(nonatomic, assign) NSInteger commandID;
-
-@end
-
-// Cell representation for LegacySettingsImageDetailTextItem.
-//  +--------------------------------------------------+
-//  |  +-------+                                       |
-//  |  | image |   Multiline title                     |
-//  |  |       |   Optional multiline detail text      |
-//  |  +-------+                                       |
-//  +--------------------------------------------------+
-@interface LegacySettingsImageDetailTextCell : MDCCollectionViewCell
-
-// Cell image.
-@property(nonatomic, readonly, strong) UIImageView* imageView;
-
-// Cell title.
-@property(nonatomic, readonly, strong) UILabel* textLabel;
-
-// Cell subtitle.
-@property(nonatomic, readonly, strong) UILabel* detailTextLabel;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_IMAGE_DETAIL_TEXT_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.mm b/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.mm
deleted file mode 100644
index 7bd20b3c..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.mm
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.h"
-
-#include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-// Padding used on the leading and trailing edges of the cell.
-const CGFloat kHorizontalPadding = 16;
-
-// Padding used on the top and bottom edges of the cell.
-const CGFloat kVerticalPadding = 16;
-
-// Image height and width.
-const CGFloat kImageSize = 40;
-
-// Padding used between the image and text.
-const CGFloat kHorizontalImagePadding = 10;
-
-}  // namespace
-
-@interface LegacySettingsImageDetailTextCell ()
-
-// Container view which contains |self.textLabel| and |self.detailTextLabel|.
-@property(nonatomic, strong) UIStackView* textStackView;
-
-@end
-
-#pragma mark - LegacySettingsImageDetailTextItem
-
-@implementation LegacySettingsImageDetailTextItem
-
-@synthesize image = _image;
-@synthesize text = _text;
-@synthesize detailText = _detailText;
-@synthesize commandID = _commandID;
-
-- (instancetype)initWithType:(NSInteger)type {
-  self = [super initWithType:type];
-  if (self) {
-    self.cellClass = [LegacySettingsImageDetailTextCell class];
-  }
-  return self;
-}
-
-- (void)configureCell:(LegacySettingsImageDetailTextCell*)cell {
-  [super configureCell:cell];
-  cell.isAccessibilityElement = YES;
-  cell.textLabel.text = self.text;
-  cell.detailTextLabel.text = self.detailText;
-  cell.imageView.image = self.image;
-}
-
-@end
-
-#pragma mark - LegacySettingsImageDetailTextCell
-
-@implementation LegacySettingsImageDetailTextCell
-
-@synthesize imageView = _imageView;
-@synthesize textLabel = _textLabel;
-@synthesize detailTextLabel = _detailTextLabel;
-@synthesize textStackView = _textStackView;
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    UIView* contentView = self.contentView;
-
-    _imageView = [[UIImageView alloc] init];
-    _imageView.translatesAutoresizingMaskIntoConstraints = NO;
-    [contentView addSubview:_imageView];
-
-    _textLabel = [[UILabel alloc] init];
-    _textLabel.numberOfLines = 0;
-    _textLabel.font = [UIFont systemFontOfSize:kUIKitMainFontSize];
-    _textLabel.textColor = UIColorFromRGB(kUIKitMainTextColor);
-
-    _detailTextLabel = [[UILabel alloc] init];
-    _detailTextLabel.numberOfLines = 0;
-    _detailTextLabel.font =
-        [UIFont systemFontOfSize:kUIKitMultilineDetailFontSize];
-    _detailTextLabel.textColor = UIColorFromRGB(kUIKitMultilineDetailTextColor);
-
-    _textStackView = [[UIStackView alloc]
-        initWithArrangedSubviews:@[ _textLabel, _detailTextLabel ]];
-    _textStackView.axis = UILayoutConstraintAxisVertical;
-    _textStackView.translatesAutoresizingMaskIntoConstraints = NO;
-    [contentView addSubview:_textStackView];
-
-    [NSLayoutConstraint activateConstraints:@[
-      // Horizontal contraints for |_imageView| and |_textStackView|.
-      [_imageView.heightAnchor constraintEqualToConstant:kImageSize],
-      [_imageView.widthAnchor constraintEqualToConstant:kImageSize],
-      [_imageView.leadingAnchor
-          constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kHorizontalPadding],
-      [_textStackView.leadingAnchor
-          constraintEqualToAnchor:_imageView.trailingAnchor
-                         constant:kHorizontalImagePadding],
-      [contentView.trailingAnchor
-          constraintEqualToAnchor:_textStackView.trailingAnchor
-                         constant:kHorizontalPadding],
-      // Vertical contraints for |_imageView| and |_textStackView|.
-      [_imageView.centerYAnchor
-          constraintEqualToAnchor:contentView.centerYAnchor],
-      [_imageView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                      constant:kVerticalPadding],
-      [contentView.bottomAnchor
-          constraintGreaterThanOrEqualToAnchor:_imageView.bottomAnchor
-                                      constant:kVerticalPadding],
-      [_textStackView.centerYAnchor
-          constraintEqualToAnchor:contentView.centerYAnchor],
-      [_textStackView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:contentView.topAnchor
-                                      constant:kVerticalPadding],
-      [contentView.bottomAnchor
-          constraintGreaterThanOrEqualToAnchor:_textStackView.bottomAnchor
-                                      constant:kVerticalPadding],
-    ]];
-  }
-  return self;
-}
-
-// Implement -layoutSubviews as per instructions in documentation for
-// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:]."
-- (void)layoutSubviews {
-  [super layoutSubviews];
-  // Adjust the text and detailText label preferredMaxLayoutWidth when the
-  // parent's width changes, for instance on screen rotation.
-  CGFloat prefferedMaxLayoutWidth = CGRectGetWidth(self.contentView.frame) -
-                                    CGRectGetWidth(self.imageView.frame) -
-                                    2 * kHorizontalPadding -
-                                    kHorizontalImagePadding;
-  self.textLabel.preferredMaxLayoutWidth = prefferedMaxLayoutWidth;
-  self.detailTextLabel.preferredMaxLayoutWidth = prefferedMaxLayoutWidth;
-
-  // Re-layout with the new preferred width to allow the label to adjust its
-  // height.
-  [super layoutSubviews];
-}
-
-#pragma mark - UIAccessibility
-
-- (NSString*)accessibilityLabel {
-  if (self.detailTextLabel.text) {
-    return [NSString stringWithFormat:@"%@, %@", self.textLabel.text,
-                                      self.detailTextLabel.text];
-  }
-  return self.textLabel.text;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h b/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h
deleted file mode 100644
index 395417e..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_SWITCH_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_SWITCH_ITEM_H_
-
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-// LegacySettingsSwitchItem is a model class that uses
-// LegacySettingsSwitchCell.
-@interface LegacySettingsSwitchItem : CollectionViewItem
-
-// The filename for the leading icon.  If empty, no icon will be shown.
-@property(nonatomic, copy) NSString* iconImageName;
-
-// The text to display.
-@property(nonatomic, copy) NSString* text;
-
-// The current state of the switch.
-@property(nonatomic, assign, getter=isOn) BOOL on;
-
-// Whether or not the switch is enabled.  Disabled switches are automatically
-// drawn as in the "off" state, with dimmed text.
-@property(nonatomic, assign, getter=isEnabled) BOOL enabled;
-
-@end
-
-// LegacySettingsSwitchCell implements a UICollectionViewCell subclass
-// containing an icon, a text label, and a switch.
-@interface LegacySettingsSwitchCell : MDCCollectionViewCell
-
-// UILabel corresponding to |text| from the item.
-@property(nonatomic, readonly, strong) UILabel* textLabel;
-
-// The switch view.
-@property(nonatomic, readonly, strong) UISwitch* switchView;
-
-// Returns the default text color used for the given |state|.
-+ (UIColor*)defaultTextColorForState:(UIControlState)state;
-
-// Sets the image that should be displayed at the leading edge of the cell. If
-// set to nil, the icon will be hidden and the remaining content will expand to
-// fill the full width of the cell.
-- (void)setIconImage:(UIImage*)image;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_LEGACY_LEGACY_SETTINGS_SWITCH_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.mm b/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.mm
deleted file mode 100644
index 6d2b61b..0000000
--- a/ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.mm
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h"
-
-#include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h"
-#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// Padding used on the leading and trailing edges of the cell.
-const CGFloat kHorizontalPadding = 16;
-
-// Padding used between the icon and the text labels.
-const CGFloat kIconTrailingPadding = 12;
-
-// Padding used on the top and bottom edges of the cell.
-const CGFloat kVerticalPadding = 16;
-
-// Size of the icon image.
-const CGFloat kIconImageSize = 28;
-}  // namespace
-
-@implementation LegacySettingsSwitchItem
-
-@synthesize enabled = _enabled;
-@synthesize iconImageName = _iconImageName;
-@synthesize on = _on;
-@synthesize text = _text;
-
-- (instancetype)initWithType:(NSInteger)type {
-  self = [super initWithType:type];
-  if (self) {
-    self.cellClass = [LegacySettingsSwitchCell class];
-    self.enabled = YES;
-  }
-  return self;
-}
-
-#pragma mark CollectionViewItem
-
-- (void)configureCell:(LegacySettingsSwitchCell*)cell {
-  [super configureCell:cell];
-  cell.textLabel.text = self.text;
-  cell.switchView.enabled = self.isEnabled;
-  cell.switchView.on = self.isOn;
-  cell.textLabel.textColor =
-      [LegacySettingsSwitchCell defaultTextColorForState:cell.switchView.state];
-
-  // Update the icon image, if one is present.
-  UIImage* iconImage = nil;
-  if ([self.iconImageName length]) {
-    iconImage = [UIImage imageNamed:self.iconImageName];
-  }
-  [cell setIconImage:iconImage];
-}
-
-@end
-
-@interface LegacySettingsSwitchCell ()
-
-// The image view for the leading icon.
-@property(nonatomic, readonly, strong) UIImageView* iconImageView;
-
-// Constraints that are used when the iconImageView is visible and hidden.
-@property(nonatomic, strong) NSLayoutConstraint* iconVisibleConstraint;
-@property(nonatomic, strong) NSLayoutConstraint* iconHiddenConstraint;
-
-@end
-
-@implementation LegacySettingsSwitchCell
-
-@synthesize iconHiddenConstraint = _iconHiddenConstraint;
-@synthesize iconImageView = _iconImageView;
-@synthesize iconVisibleConstraint = _iconVisibleConstraint;
-@synthesize switchView = _switchView;
-@synthesize textLabel = _textLabel;
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    self.isAccessibilityElement = YES;
-
-    _iconImageView = [[UIImageView alloc] init];
-    _iconImageView.translatesAutoresizingMaskIntoConstraints = NO;
-    _iconImageView.hidden = YES;
-    [self.contentView addSubview:_iconImageView];
-
-    _textLabel = [[UILabel alloc] init];
-    _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
-    _textLabel.font = [UIFont systemFontOfSize:kUIKitMainFontSize];
-    _textLabel.textColor = UIColorFromRGB(kUIKitMainTextColor);
-    _textLabel.numberOfLines = 0;
-    [self.contentView addSubview:_textLabel];
-
-    _switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
-    _switchView.translatesAutoresizingMaskIntoConstraints = NO;
-    _switchView.onTintColor = UIColorFromRGB(kUIKitSwitchTintColor);
-    _switchView.accessibilityHint = l10n_util::GetNSString(
-        IDS_IOS_TOGGLE_SETTING_SWITCH_ACCESSIBILITY_HINT);
-    [self.contentView addSubview:_switchView];
-
-    // Set up the constraints assuming that the icon image is hidden..
-    _iconVisibleConstraint = [_textLabel.leadingAnchor
-        constraintEqualToAnchor:_iconImageView.trailingAnchor
-                       constant:kIconTrailingPadding];
-    _iconHiddenConstraint = [_textLabel.leadingAnchor
-        constraintEqualToAnchor:self.contentView.leadingAnchor
-                       constant:kHorizontalPadding];
-
-    [NSLayoutConstraint activateConstraints:@[
-      [_iconImageView.leadingAnchor
-          constraintEqualToAnchor:self.contentView.leadingAnchor
-                         constant:kHorizontalPadding],
-      [_iconImageView.widthAnchor constraintEqualToConstant:kIconImageSize],
-      [_iconImageView.heightAnchor constraintEqualToConstant:kIconImageSize],
-
-      [_switchView.trailingAnchor
-          constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kHorizontalPadding],
-      [_textLabel.trailingAnchor
-          constraintLessThanOrEqualToAnchor:_switchView.leadingAnchor
-                                   constant:-kHorizontalPadding],
-
-      [_iconImageView.centerYAnchor
-          constraintEqualToAnchor:self.contentView.centerYAnchor],
-      [_textLabel.centerYAnchor
-          constraintEqualToAnchor:self.contentView.centerYAnchor],
-      [_switchView.centerYAnchor
-          constraintEqualToAnchor:self.contentView.centerYAnchor],
-
-      _iconHiddenConstraint,
-    ]];
-
-    AddOptionalVerticalPadding(self.contentView, _textLabel, kVerticalPadding);
-  }
-  return self;
-}
-
-+ (UIColor*)defaultTextColorForState:(UIControlState)state {
-  return (state & UIControlStateDisabled)
-             ? UIColorFromRGB(kUIKitDetailTextColor)
-             : UIColorFromRGB(kUIKitMainTextColor);
-}
-
-- (void)setIconImage:(UIImage*)image {
-  BOOL hidden = (image == nil);
-  if (hidden == self.iconImageView.hidden) {
-    return;
-  }
-
-  self.iconImageView.image = image;
-  self.iconImageView.hidden = hidden;
-  if (hidden) {
-    self.iconVisibleConstraint.active = NO;
-    self.iconHiddenConstraint.active = YES;
-  } else {
-    self.iconHiddenConstraint.active = NO;
-    self.iconVisibleConstraint.active = YES;
-  }
-}
-
-// Implement -layoutSubviews as per instructions in documentation for
-// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
-- (void)layoutSubviews {
-  [super layoutSubviews];
-  // Adjust the text label preferredMaxLayoutWidth when the parent's width
-  // changes, for instance on screen rotation.
-  _textLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.contentView.frame) -
-                                       CGRectGetWidth(_switchView.frame) -
-                                       3 * kHorizontalPadding;
-
-  // Re-layout with the new preferred width to allow the label to adjust its
-  // height.
-  [super layoutSubviews];
-}
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-
-  [self setIconImage:nil];
-  [_switchView removeTarget:nil
-                     action:nil
-           forControlEvents:[_switchView allControlEvents]];
-}
-
-#pragma mark - UIAccessibility
-
-- (CGPoint)accessibilityActivationPoint {
-  // Center the activation point over the switch, so that double-tapping toggles
-  // the switch.
-  CGRect switchFrame =
-      UIAccessibilityConvertFrameToScreenCoordinates(_switchView.frame, self);
-  return CGPointMake(CGRectGetMidX(switchFrame), CGRectGetMidY(switchFrame));
-}
-
-- (NSString*)accessibilityHint {
-  if (_switchView.isEnabled) {
-    return _switchView.accessibilityHint;
-  } else {
-    return @"";
-  }
-}
-
-- (NSString*)accessibilityLabel {
-  return _textLabel.text;
-}
-
-- (NSString*)accessibilityValue {
-  if (_switchView.on) {
-    return l10n_util::GetNSString(IDS_IOS_SETTING_ON);
-  } else {
-    return l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/cells/password_details_item.h b/ios/chrome/browser/ui/settings/cells/password_details_item.h
deleted file mode 100644
index bc8707c..0000000
--- a/ios/chrome/browser/ui/settings/cells/password_details_item.h
+++ /dev/null
@@ -1,32 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_PASSWORD_DETAILS_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_PASSWORD_DETAILS_ITEM_H_
-
-#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-// Item to display an element of the Password Details settings.
-@interface PasswordDetailsItem : CollectionViewItem
-
-// The text (a username, a password).
-@property(nonatomic, copy) NSString* text;
-
-// Whether to reveal the text or ●●●●●●. There are as many dots as the text is
-// long.
-@property(nonatomic, assign, getter=isShowingText) BOOL showingText;
-
-@end
-
-// Cell class associated to PasswordDetailsItem. The text label can span
-// multiple lines.
-@interface PasswordDetailsCell : MDCCollectionViewCell
-
-// Label for the text.
-@property(nonatomic, readonly, strong) UILabel* textLabel;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_PASSWORD_DETAILS_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/password_details_item.mm b/ios/chrome/browser/ui/settings/cells/password_details_item.mm
deleted file mode 100644
index b2f0ef9..0000000
--- a/ios/chrome/browser/ui/settings/cells/password_details_item.mm
+++ /dev/null
@@ -1,111 +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 "ios/chrome/browser/ui/settings/cells/password_details_item.h"
-
-#import <CoreGraphics/CoreGraphics.h>
-#import <UIKit/UIKit.h>
-
-#include "ios/chrome/browser/ui/collection_view/cells/collection_view_cell_constants.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#include "ios/chrome/common/ui_util/constraints_ui_util.h"
-#include "ios/chrome/grit/ios_strings.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// Padding used on the leading and trailing edges of the cell.
-const CGFloat kHorizontalPadding = 16;
-
-// Padding used on the top and bottom edges of the cell.
-const CGFloat kVerticalPadding = 16;
-}  // namespace
-
-@implementation PasswordDetailsItem
-
-@synthesize text = _text;
-@synthesize showingText = _showingText;
-
-- (instancetype)initWithType:(NSInteger)type {
-  self = [super initWithType:type];
-  if (self) {
-    self.cellClass = [PasswordDetailsCell class];
-  }
-  return self;
-}
-
-- (void)configureCell:(PasswordDetailsCell*)cell {
-  [super configureCell:cell];
-  if (self.showingText) {
-    cell.textLabel.text = self.text;
-    cell.textLabel.accessibilityLabel = self.text;
-  } else {
-    NSString* obscuredText = [@"" stringByPaddingToLength:[self.text length]
-                                               withString:@"•"
-                                          startingAtIndex:0];
-    cell.textLabel.text = obscuredText;
-    cell.textLabel.accessibilityLabel =
-        l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_HIDDEN_LABEL);
-  }
-}
-
-@end
-
-@implementation PasswordDetailsCell
-
-@synthesize textLabel = _textLabel;
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    UIView* contentView = self.contentView;
-
-    _textLabel = [[UILabel alloc] init];
-    _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
-    _textLabel.font = [UIFont systemFontOfSize:kUIKitMainFontSize];
-    _textLabel.textColor = UIColorFromRGB(kUIKitMainTextColor);
-    _textLabel.numberOfLines = 0;
-    [contentView addSubview:_textLabel];
-
-    // Set up the constraints.
-    [NSLayoutConstraint activateConstraints:@[
-      [_textLabel.leadingAnchor
-          constraintEqualToAnchor:contentView.leadingAnchor
-                         constant:kHorizontalPadding],
-      [_textLabel.trailingAnchor
-          constraintEqualToAnchor:contentView.trailingAnchor
-                         constant:-kHorizontalPadding],
-    ]];
-    AddOptionalVerticalPadding(contentView, _textLabel, kVerticalPadding);
-  }
-  return self;
-}
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.textLabel.text = nil;
-  self.textLabel.accessibilityLabel = nil;
-}
-
-// Implements -layoutSubviews as per instructions in documentation for
-// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
-- (void)layoutSubviews {
-  [super layoutSubviews];
-
-  // Adjust the text label preferredMaxLayoutWidth when the parent's width
-  // changes, for instance on screen rotation.
-  CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
-  self.textLabel.preferredMaxLayoutWidth = parentWidth - 2 * kHorizontalPadding;
-
-  // Re-layout with the new preferred width to allow the label to adjust its
-  // height.
-  [super layoutSubviews];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/cells/password_details_item_unittest.mm b/ios/chrome/browser/ui/settings/cells/password_details_item_unittest.mm
deleted file mode 100644
index c70c6cc..0000000
--- a/ios/chrome/browser/ui/settings/cells/password_details_item_unittest.mm
+++ /dev/null
@@ -1,42 +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 "ios/chrome/browser/ui/settings/cells/password_details_item.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#import "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-using PasswordDetailsItemTest = PlatformTest;
-
-// Tests that the text label and showing status are set properly after a call to
-// |configureCell:|.
-TEST_F(PasswordDetailsItemTest, ConfigureCell) {
-  PasswordDetailsItem* item = [[PasswordDetailsItem alloc] initWithType:0];
-  PasswordDetailsCell* cell = [[[item cellClass] alloc] init];
-  EXPECT_TRUE([cell isMemberOfClass:[PasswordDetailsCell class]]);
-  EXPECT_NSEQ(nil, cell.textLabel.text);
-  NSString* text = @"Test text";
-  NSString* obscuredText = @"•••••••••";
-
-  item.text = text;
-  [item configureCell:cell];
-  EXPECT_NSEQ(obscuredText, cell.textLabel.text);
-
-  item.showingText = YES;
-  [item configureCell:cell];
-  EXPECT_NSEQ(text, cell.textLabel.text);
-
-  item.showingText = NO;
-  [item configureCell:cell];
-  EXPECT_NSEQ(obscuredText, cell.textLabel.text);
-}
-
-}  // namespace
diff --git a/ios/chrome/browser/ui/settings/cells/settings_text_item.h b/ios/chrome/browser/ui/settings/cells/settings_text_item.h
index a244cf3..230facb0 100644
--- a/ios/chrome/browser/ui/settings/cells/settings_text_item.h
+++ b/ios/chrome/browser/ui/settings/cells/settings_text_item.h
@@ -11,6 +11,7 @@
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
 // Collection view item to represent and configure a CollectionViewTextCell.
+// TODO(crbug.com/894800): Remove this.
 @interface SettingsTextItem : CollectionViewItem
 
 // The accessory type for the represented cell.
diff --git a/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h b/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h
index add53b2..d07227b1 100644
--- a/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h
+++ b/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.h
@@ -11,6 +11,7 @@
 
 // TableViewClearBrowsingDataItem contains the model data for a
 // TableViewTextCell in addition a BrowsingDataRemoveMask property.
+// TODO(crbug.com/894800): Remove this.
 @interface TableViewClearBrowsingDataItem : TableViewItem
 
 // Text of the TableViewTextCell
diff --git a/ios/chrome/browser/ui/settings/cells/text_and_error_item.h b/ios/chrome/browser/ui/settings/cells/text_and_error_item.h
index ffd540d..9b47db86 100644
--- a/ios/chrome/browser/ui/settings/cells/text_and_error_item.h
+++ b/ios/chrome/browser/ui/settings/cells/text_and_error_item.h
@@ -13,6 +13,7 @@
 // TextAndErrorItem: Displays a text label and might containg an accessory type.
 // It might also display an error icon at the right side of the cell if the
 // shouldDisplayError flag is set to true.
+// TODO(crbug.com/894800): Remove this.
 @interface TextAndErrorItem : CollectionViewItem
 
 // Item text.
diff --git a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller+protected.h b/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller+protected.h
deleted file mode 100644
index 16f162f..0000000
--- a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller+protected.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_PROTECTED_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_PROTECTED_H_
-
-#import "ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.h"
-
-// The collection view for an Autofill edit entry menu.
-@interface LegacyAutofillEditCollectionViewController (Protected)
-
-// Returns the indexPath for the currently focused text field when in edit mode.
-- (NSIndexPath*)indexPathForCurrentTextField;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_PROTECTED_H_
diff --git a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.h b/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.h
deleted file mode 100644
index ede0e52..0000000
--- a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_H_
-
-#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
-
-// The collection view for an Autofill edit entry menu.
-// TODO(crbug.com/894800): Remove this.
-@interface LegacyAutofillEditCollectionViewController
-    : SettingsRootCollectionViewController<UITextFieldDelegate>
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_LEGACY_AUTOFILL_EDIT_COLLECTION_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.mm b/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.mm
deleted file mode 100644
index a3a95b9..0000000
--- a/ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.mm
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller.h"
-
-#include "base/logging.h"
-#import "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/autofill/autofill_edit_accessory_view.h"
-#import "ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.h"
-#import "ios/chrome/browser/ui/settings/legacy_autofill_edit_collection_view_controller+protected.h"
-#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-LegacyAutofillEditCell* AutofillEditCellForTextField(UITextField* textField) {
-  LegacyAutofillEditCell* settingsCell = nil;
-  for (UIView* view = textField; view; view = [view superview]) {
-    LegacyAutofillEditCell* cell =
-        base::mac::ObjCCast<LegacyAutofillEditCell>(view);
-    if (cell) {
-      settingsCell = cell;
-      break;
-    }
-  }
-
-  // There should be a cell associated with this text field.
-  DCHECK(settingsCell);
-  return settingsCell;
-}
-
-}  // namespace
-
-@interface LegacyAutofillEditCollectionViewController ()<
-    AutofillEditAccessoryDelegate> {
-  LegacyAutofillEditCell* _currentEditingCell;
-  AutofillEditAccessoryView* _accessoryView;
-}
-@end
-
-@implementation LegacyAutofillEditCollectionViewController
-
-- (instancetype)initWithLayout:(UICollectionViewLayout*)layout
-                         style:(CollectionViewControllerStyle)style {
-  self = [super initWithLayout:layout style:style];
-  if (!self) {
-    return nil;
-  }
-
-  _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self];
-  [self setShouldHideDoneButton:YES];
-  [self updateEditButton];
-  return self;
-}
-
-- (void)viewDidAppear:(BOOL)animated {
-  [super viewDidAppear:animated];
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(keyboardDidShow)
-             name:UIKeyboardDidShowNotification
-           object:nil];
-}
-
-- (void)viewWillDisappear:(BOOL)animated {
-  [super viewWillDisappear:animated];
-  [[NSNotificationCenter defaultCenter]
-      removeObserver:self
-                name:UIKeyboardDidShowNotification
-              object:nil];
-}
-
-#pragma mark - SettingsRootCollectionViewController
-
-- (BOOL)shouldShowEditButton {
-  return YES;
-}
-
-- (BOOL)editButtonEnabled {
-  return YES;
-}
-
-- (NSIndexPath*)indexForCellPathWithOffset:(NSInteger)offset
-                                  fromPath:(NSIndexPath*)cellPath {
-  if (!cellPath || !offset)
-    return nil;
-
-  NSInteger cellSection = [cellPath section];
-  NSInteger nextCellRow = [cellPath row] + offset;
-
-  if (nextCellRow >= 0 &&
-      nextCellRow < [[self collectionView] numberOfItemsInSection:cellSection])
-    return [NSIndexPath indexPathForRow:nextCellRow inSection:cellSection];
-
-  NSInteger nextCellSection = cellSection + offset;
-  if (nextCellSection >= 0 &&
-      nextCellSection < [[self collectionView] numberOfSections])
-    return [NSIndexPath indexPathForRow:0 inSection:nextCellSection];
-
-  return nil;
-}
-
-#pragma mark - UITextFieldDelegate
-
-- (void)textFieldDidBeginEditing:(UITextField*)textField {
-  LegacyAutofillEditCell* cell = AutofillEditCellForTextField(textField);
-  _currentEditingCell = cell;
-  [textField setInputAccessoryView:_accessoryView];
-  [self updateAccessoryViewButtonState];
-}
-
-- (void)textFieldDidEndEditing:(UITextField*)textField {
-  LegacyAutofillEditCell* cell = AutofillEditCellForTextField(textField);
-  DCHECK(_currentEditingCell == cell);
-  [textField setInputAccessoryView:nil];
-  _currentEditingCell = nil;
-}
-
-- (BOOL)textFieldShouldReturn:(UITextField*)textField {
-  DCHECK([_currentEditingCell textField] == textField);
-  [self nextPressed];
-  return NO;
-}
-
-#pragma mark - AutofillEditAccessoryDelegate
-
-- (void)nextPressed {
-  [self moveToAnotherCellWithOffset:1];
-}
-
-- (void)previousPressed {
-  [self moveToAnotherCellWithOffset:-1];
-}
-
-- (void)closePressed {
-  [[_currentEditingCell textField] resignFirstResponder];
-}
-
-#pragma mark - Helper methods
-
-- (NSIndexPath*)indexPathForCurrentTextField {
-  DCHECK(_currentEditingCell);
-  return [[self collectionView] indexPathForCell:_currentEditingCell];
-}
-
-- (void)moveToAnotherCellWithOffset:(NSInteger)offset {
-  UICollectionView* collectionView = [self collectionView];
-  NSIndexPath* cellPath = [self indexPathForCurrentTextField];
-  DCHECK(cellPath);
-  NSIndexPath* nextCellPath =
-      [self indexForCellPathWithOffset:offset fromPath:cellPath];
-
-  if (!nextCellPath) {
-    [[_currentEditingCell textField] resignFirstResponder];
-  } else {
-    LegacyAutofillEditCell* nextCell =
-        base::mac::ObjCCastStrict<LegacyAutofillEditCell>(
-            [collectionView cellForItemAtIndexPath:nextCellPath]);
-    [nextCell.textField becomeFirstResponder];
-  }
-}
-
-- (void)updateAccessoryViewButtonState {
-  NSIndexPath* currentPath = [self indexPathForCurrentTextField];
-  NSIndexPath* nextPath =
-      [self indexForCellPathWithOffset:1 fromPath:currentPath];
-  NSIndexPath* previousPath =
-      [self indexForCellPathWithOffset:-1 fromPath:currentPath];
-
-  [[_accessoryView previousButton] setEnabled:previousPath != nil];
-  [[_accessoryView nextButton] setEnabled:nextPath != nil];
-}
-
-#pragma mark - Keyboard handling
-
-- (void)keyboardDidShow {
-  [self.collectionView scrollRectToVisible:[_currentEditingCell frame]
-                                  animated:YES];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm
index 19c413f..c8269be 100644
--- a/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.mm
@@ -33,13 +33,9 @@
 #import "ios/chrome/browser/ui/settings/cells/card_multiline_item.h"
 #import "ios/chrome/browser/ui/settings/cells/copied_to_chrome_item.h"
 #import "ios/chrome/browser/ui/settings/cells/legacy/legacy_account_signin_item.h"
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_autofill_data_item.h"
 #import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_detail_item.h"
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_image_detail_text_item.h"
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/legacy/legacy_sync_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/passphrase_error_item.h"
-#import "ios/chrome/browser/ui/settings/cells/password_details_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_multiline_detail_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_search_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_text_item.h"
@@ -60,7 +56,6 @@
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierTextCell = kSectionIdentifierEnumZero,
   SectionIdentifierDetailCell,
-  SectionIdentifierMultilineCell,
   SectionIdentifierSwitchCell,
   SectionIdentifierNativeAppCell,
   SectionIdentifierAutofill,
@@ -88,7 +83,6 @@
   ItemTypeDetailBothLong,
   ItemTypeMultilineBasic,
   ItemTypeImportDataMultiline,
-  ItemTypeSwitchBasic,
   ItemTypeSwitchDynamicHeight,
   ItemTypeSwitchSync,
   ItemTypeHeader,
@@ -101,9 +95,6 @@
   ItemTypePaymentsSingleLine,
   ItemTypePaymentsDynamicHeight,
   ItemTypeCopiedToChrome,
-  ItemTypePasswordDetailsShortHidden,
-  ItemTypePasswordDetailsShortVisible,
-  ItemTypePasswordDetailsLong,
   ItemTypeSettingsSearch,
   ItemTypeAutofillDynamicHeight,
   ItemTypeAutofillCVC,
@@ -112,7 +103,6 @@
   ItemTypeFooter,
   ItemTypeSyncPassphraseError,
   ItemTypeContentSuggestions,
-  ItemTypeImageDetailTextItem,
 };
 
 // Credit Card icon size.
@@ -229,45 +219,13 @@
   [model addItem:detailLongBoth
       toSectionWithIdentifier:SectionIdentifierDetailCell];
 
-  // Multiline cells.
-  [model addSectionWithIdentifier:SectionIdentifierMultilineCell];
-  [model addItem:[self settingsImageDetailTextItem]
-      toSectionWithIdentifier:SectionIdentifierMultilineCell];
-  LegacySettingsImageDetailTextItem* settingsImageDetailTextItem =
-      [self settingsImageDetailTextItem];
-  settingsImageDetailTextItem.text = @"Short title";
-  [model addItem:settingsImageDetailTextItem
-      toSectionWithIdentifier:SectionIdentifierMultilineCell];
-  settingsImageDetailTextItem = [self settingsImageDetailTextItem];
-  settingsImageDetailTextItem.detailText = @"Short detail text";
-  [model addItem:settingsImageDetailTextItem
-      toSectionWithIdentifier:SectionIdentifierMultilineCell];
-  settingsImageDetailTextItem = [self settingsImageDetailTextItem];
-  settingsImageDetailTextItem.detailText =
-      @"Text multiline that works nice with a very very very very very long "
-      @"text Text multiline that works nice with a very very very very very "
-      @"long text Text multiline that works nice with a very very very very "
-      @"very long text";
-  [model addItem:settingsImageDetailTextItem
-      toSectionWithIdentifier:SectionIdentifierMultilineCell];
-
   // Switch cells.
   [model addSectionWithIdentifier:SectionIdentifierSwitchCell];
-  [model addItem:[self basicSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitchCell];
-  [model addItem:[self longTextSwitchItem]
-      toSectionWithIdentifier:SectionIdentifierSwitchCell];
   [model addItem:[self syncSwitchItem]
       toSectionWithIdentifier:SectionIdentifierSwitchCell];
 
   // Autofill cells.
   [model addSectionWithIdentifier:SectionIdentifierAutofill];
-  [model addItem:[self autofillItemWithMainAndTrailingText]
-      toSectionWithIdentifier:SectionIdentifierAutofill];
-  [model addItem:[self autofillItemWithLeadingTextOnly]
-      toSectionWithIdentifier:SectionIdentifierAutofill];
-  [model addItem:[self autofillItemWithAllText]
-      toSectionWithIdentifier:SectionIdentifierAutofill];
   [model addItem:[self autofillEditItem]
       toSectionWithIdentifier:SectionIdentifierAutofill];
   [model addItem:[self autofillEditItemWithIcon]
@@ -350,15 +308,6 @@
   [model addItem:copiedToChromeItem
       toSectionWithIdentifier:SectionIdentifierCopiedToChrome];
 
-  // Password Details cells.
-  [model addSectionWithIdentifier:SectionIdentifierPasswordDetails];
-  [model addItem:[self passwordDetailsShortHiddenItem]
-      toSectionWithIdentifier:SectionIdentifierPasswordDetails];
-  [model addItem:[self passwordDetailsShortVisibleItem]
-      toSectionWithIdentifier:SectionIdentifierPasswordDetails];
-  [model addItem:[self passwordDetailsLongItem]
-      toSectionWithIdentifier:SectionIdentifierPasswordDetails];
-
   // Account cells.
   [model addSectionWithIdentifier:SectionIdentifierAccountCell];
   [model addItem:[self accountItemDetailWithError]
@@ -404,7 +353,6 @@
   CollectionViewItem* item =
       [self.collectionViewModel itemAtIndexPath:indexPath];
   switch (item.type) {
-    case ItemTypeImageDetailTextItem:
     case ItemTypeContentSuggestions:
     case ItemTypeFooter:
     case ItemTypeSwitchDynamicHeight:
@@ -416,9 +364,6 @@
     case ItemTypeTextError:
     case ItemTypeMultilineBasic:
     case ItemTypeImportDataMultiline:
-    case ItemTypePasswordDetailsShortHidden:
-    case ItemTypePasswordDetailsShortVisible:
-    case ItemTypePasswordDetailsLong:
     case ItemTypeAutofillCVC:
     case ItemTypeAutofillStatus:
     case ItemTypePaymentsDynamicHeight:
@@ -481,7 +426,6 @@
   switch (item.type) {
     case ItemTypeApp:
     case ItemTypeColdStateSigninPromo:
-    case ItemTypeSwitchBasic:
     case ItemTypeSwitchDynamicHeight:
     case ItemTypeSwitchSync:
     case ItemTypeWarmStateSigninPromo:
@@ -573,23 +517,6 @@
 
 #pragma mark Private
 
-- (CollectionViewItem*)basicSwitchItem {
-  LegacySettingsSwitchItem* item =
-      [[LegacySettingsSwitchItem alloc] initWithType:ItemTypeSwitchBasic];
-  item.text = @"Enable awesomeness.";
-  item.on = YES;
-  return item;
-}
-
-- (CollectionViewItem*)longTextSwitchItem {
-  LegacySettingsSwitchItem* item = [[LegacySettingsSwitchItem alloc]
-      initWithType:ItemTypeSwitchDynamicHeight];
-  item.text = @"Enable awesomeness. This is a very long text that is intended "
-              @"to overflow.";
-  item.on = YES;
-  return item;
-}
-
 - (CollectionViewItem*)syncSwitchItem {
   LegacySyncSwitchItem* item =
       [[LegacySyncSwitchItem alloc] initWithType:ItemTypeSwitchSync];
@@ -635,34 +562,6 @@
   return item;
 }
 
-- (CollectionViewItem*)autofillItemWithMainAndTrailingText {
-  LegacyAutofillDataItem* item = [[LegacyAutofillDataItem alloc]
-      initWithType:ItemTypeAutofillDynamicHeight];
-  item.text = @"Main Text";
-  item.trailingDetailText = @"Trailing Detail Text";
-  item.accessoryType = MDCCollectionViewCellAccessoryNone;
-  return item;
-}
-
-- (CollectionViewItem*)autofillItemWithLeadingTextOnly {
-  LegacyAutofillDataItem* item = [[LegacyAutofillDataItem alloc]
-      initWithType:ItemTypeAutofillDynamicHeight];
-  item.text = @"Main Text";
-  item.leadingDetailText = @"Leading Detail Text";
-  item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
-  return item;
-}
-
-- (CollectionViewItem*)autofillItemWithAllText {
-  LegacyAutofillDataItem* item = [[LegacyAutofillDataItem alloc]
-      initWithType:ItemTypeAutofillDynamicHeight];
-  item.text = @"Main Text";
-  item.leadingDetailText = @"Leading Detail Text";
-  item.trailingDetailText = @"Trailing Detail Text";
-  item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
-  return item;
-}
-
 - (CollectionViewItem*)autofillEditItem {
   LegacyAutofillEditItem* item = [[LegacyAutofillEditItem alloc]
       initWithType:ItemTypeAutofillDynamicHeight];
@@ -768,18 +667,6 @@
   return articleItem;
 }
 
-- (LegacySettingsImageDetailTextItem*)settingsImageDetailTextItem {
-  LegacySettingsImageDetailTextItem* settingsImageDetailTextItem =
-      [[LegacySettingsImageDetailTextItem alloc]
-          initWithType:ItemTypeImageDetailTextItem];
-  settingsImageDetailTextItem.image =
-      [UIImage imageNamed:@"ios_default_avatar"];
-  settingsImageDetailTextItem.text =
-      @"Text multiline that works nice with a very very very very very long "
-      @"text";
-  return settingsImageDetailTextItem;
-}
-
 - (ContentSuggestionsFooterItem*)contentSuggestionsFooterItem {
   ContentSuggestionsFooterItem* footerItem =
       [[ContentSuggestionsFooterItem alloc]
@@ -789,29 +676,4 @@
   return footerItem;
 }
 
-- (PasswordDetailsItem*)passwordDetailsShortHiddenItem {
-  PasswordDetailsItem* item = [[PasswordDetailsItem alloc]
-      initWithType:ItemTypePasswordDetailsShortHidden];
-  item.text = @"hunter2";
-  return item;
-}
-
-- (PasswordDetailsItem*)passwordDetailsShortVisibleItem {
-  PasswordDetailsItem* item = [[PasswordDetailsItem alloc]
-      initWithType:ItemTypePasswordDetailsShortVisible];
-  item.text = @"hunter2";
-  item.showingText = YES;
-  return item;
-}
-
-- (PasswordDetailsItem*)passwordDetailsLongItem {
-  PasswordDetailsItem* item =
-      [[PasswordDetailsItem alloc] initWithType:ItemTypePasswordDetailsLong];
-  item.text =
-      @"Lorem ipsum dolor sit amet, consectetur "
-      @"adipiscing elit, sed do eiusmod tempor "
-      @"incididunt ut labore et dolore magna aliqua.";
-  return item;
-}
-
 @end
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
index 070ffdf..ef39d77f 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
@@ -61,22 +61,6 @@
 @end
 
 @implementation GridCell
-// Public properties.
-@synthesize delegate = _delegate;
-@synthesize theme = _theme;
-@synthesize itemIdentifier = _itemIdentifier;
-@synthesize icon = _icon;
-@synthesize snapshot = _snapshot;
-@synthesize title = _title;
-@synthesize titleHidden = _titleHidden;
-// Private properties.
-@synthesize topBarHeight = _topBarHeight;
-@synthesize topBar = _topBar;
-@synthesize snapshotView = _snapshotView;
-@synthesize titleLabel = _titleLabel;
-@synthesize closeIconView = _closeIconView;
-@synthesize closeTapTargetButton = _closeTapTargetButton;
-@synthesize border = _border;
 
 // |-dequeueReusableCellWithReuseIdentifier:forIndexPath:| calls this method to
 // initialize a cell.
@@ -107,15 +91,11 @@
     _snapshotView = snapshotView;
     _closeTapTargetButton = closeTapTargetButton;
 
-    _topBarHeight =
-        [topBar.heightAnchor constraintEqualToConstant:kGridCellHeaderHeight];
-
     NSArray* constraints = @[
       [topBar.topAnchor constraintEqualToAnchor:contentView.topAnchor],
       [topBar.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor],
       [topBar.trailingAnchor
           constraintEqualToAnchor:contentView.trailingAnchor],
-      _topBarHeight,
       [snapshotView.topAnchor constraintEqualToAnchor:topBar.bottomAnchor],
       [snapshotView.leadingAnchor
           constraintEqualToAnchor:contentView.leadingAnchor],
@@ -274,6 +254,8 @@
   _closeIconView = closeIconView;
 
   _accessibilityConstraints = @[
+    [topBar.heightAnchor
+        constraintEqualToConstant:kGridCellHeaderAccessibilityHeight],
     [titleLabel.leadingAnchor
         constraintEqualToAnchor:topBar.leadingAnchor
                        constant:kGridCellHeaderLeadingInset],
@@ -282,6 +264,7 @@
   ];
 
   _nonAccessibilityConstraints = @[
+    [topBar.heightAnchor constraintEqualToConstant:kGridCellHeaderHeight],
     [iconView.leadingAnchor
         constraintEqualToAnchor:topBar.leadingAnchor
                        constant:kGridCellHeaderLeadingInset],
@@ -296,10 +279,11 @@
   NSArray* constraints = @[
     [titleLabel.centerYAnchor constraintEqualToAnchor:topBar.centerYAnchor],
     [titleLabel.trailingAnchor
-        constraintLessThanOrEqualToAnchor:closeIconView.leadingAnchor
-                                 constant:-kGridCellTitleLabelContentInset],
-    [closeIconView.topAnchor constraintEqualToAnchor:topBar.topAnchor],
-    [closeIconView.bottomAnchor constraintEqualToAnchor:topBar.bottomAnchor],
+        constraintEqualToAnchor:closeIconView.leadingAnchor
+                       constant:-kGridCellTitleLabelContentInset],
+    [closeIconView.topAnchor
+        constraintEqualToAnchor:topBar.topAnchor
+                       constant:kGridCellCloseButtonContentInset],
     [closeIconView.trailingAnchor
         constraintEqualToAnchor:topBar.trailingAnchor
                        constant:-kGridCellCloseButtonContentInset],
@@ -324,9 +308,11 @@
 - (void)updateTopBar {
   if (UIContentSizeCategoryIsAccessibilityCategory(
           self.traitCollection.preferredContentSizeCategory)) {
+    self.titleLabel.numberOfLines = 2;
     [NSLayoutConstraint deactivateConstraints:_nonAccessibilityConstraints];
     [NSLayoutConstraint activateConstraints:_accessibilityConstraints];
   } else {
+    self.titleLabel.numberOfLines = 1;
     [NSLayoutConstraint deactivateConstraints:_accessibilityConstraints];
     [NSLayoutConstraint activateConstraints:_nonAccessibilityConstraints];
   }
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
index 27bdb3d..9fe18a6bb 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
@@ -75,10 +75,12 @@
 extern const CGSize kGridCellSizeSmall;
 extern const CGSize kGridCellSizeMedium;
 extern const CGSize kGridCellSizeLarge;
+extern const CGSize kGridCellSizeAccessibility;
 extern const CGFloat kGridCellCornerRadius;
 extern const CGFloat kGridCellIconCornerRadius;
 // The cell header contains the icon, title, and close button.
 extern const CGFloat kGridCellHeaderHeight;
+extern const CGFloat kGridCellHeaderAccessibilityHeight;
 extern const CGFloat kGridCellHeaderLeadingInset;
 extern const CGFloat kGridCellCloseTapTargetWidthHeight;
 extern const CGFloat kGridCellCloseButtonContentInset;
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
index 5c6310f7..04fae2837 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
@@ -69,10 +69,12 @@
 const CGSize kGridCellSizeSmall = CGSize{144.0f, 168.0f};
 const CGSize kGridCellSizeMedium = CGSize{168.0f, 202.0f};
 const CGSize kGridCellSizeLarge = CGSize{228.0f, 256.0f};
+const CGSize kGridCellSizeAccessibility = CGSize{288.0f, 336.0f};
 const CGFloat kGridCellCornerRadius = 13.0f;
 const CGFloat kGridCellIconCornerRadius = 3.0f;
 // The cell header contains the icon, title, and close button.
 const CGFloat kGridCellHeaderHeight = 32.0f;
+const CGFloat kGridCellHeaderAccessibilityHeight = 108.0f;
 const CGFloat kGridCellHeaderLeadingInset = 9.0f;
 const CGFloat kGridCellCloseTapTargetWidthHeight = 44.0f;
 const CGFloat kGridCellCloseButtonContentInset = 8.5f;
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
index 16895e7..6ffd1a7 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
@@ -39,8 +39,13 @@
   UIUserInterfaceSizeClass verticalSizeClass =
       self.collectionView.traitCollection.verticalSizeClass;
   CGFloat width = CGRectGetWidth(self.collectionView.bounds);
-  if (horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
-      verticalSizeClass == UIUserInterfaceSizeClassCompact) {
+  if (UIContentSizeCategoryIsAccessibilityCategory(
+          UIApplication.sharedApplication.preferredContentSizeCategory)) {
+    self.itemSize = kGridCellSizeAccessibility;
+    self.sectionInset = kGridLayoutInsetsRegularCompact;
+    self.minimumLineSpacing = kGridLayoutLineSpacingRegularCompact;
+  } else if (horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
+             verticalSizeClass == UIUserInterfaceSizeClassCompact) {
     self.itemSize = kGridCellSizeSmall;
     if (width < kGridLayoutCompactCompactLimitedWidth) {
       self.sectionInset = kGridLayoutInsetsCompactCompactLimitedWidth;
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
index 44893f6..6a98c63b 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -100,6 +100,8 @@
   return self;
 }
 
+#pragma mark - UIViewController
+
 - (void)loadView {
   self.defaultLayout = [[GridLayout alloc] init];
   self.reorderingLayout = [[GridReorderingLayout alloc] init];
@@ -165,6 +167,13 @@
   [super viewWillDisappear:animated];
 }
 
+#pragma mark - UITraitEnvironment
+
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  [self.collectionView.collectionViewLayout invalidateLayout];
+}
+
 #pragma mark - Public
 
 - (UIScrollView*)gridView {
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
index 8eec742..2c1e3b2 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.mm
@@ -20,16 +20,10 @@
 
 - (void)hide {
   self.newTabButton.button.alpha = 0.0;
-  if ([self shouldUseCompactLayout]) {
-    self.backgroundColor = UIColor.blackColor;
-  }
 }
 
 - (void)show {
   self.newTabButton.button.alpha = 1.0;
-  if ([self shouldUseCompactLayout]) {
-    self.backgroundColor = UIColor.clearColor;
-  }
 }
 
 #pragma mark - UIView
@@ -62,8 +56,14 @@
   if (self.page == TabGridPageRemoteTabs) {
     if ([self shouldUseCompactLayout]) {
       [self setItems:@[ _spaceItem, self.trailingButton ]];
+      [self setBackgroundImage:_translucentBackground
+            forToolbarPosition:UIBarPositionAny
+                    barMetrics:UIBarMetricsDefault];
     } else {
       [self setItems:@[]];
+      [self setBackgroundImage:_transparentBackground
+            forToolbarPosition:UIToolbarPositionAny
+                    barMetrics:UIBarMetricsDefault];
     }
   } else {
     if ([self shouldUseCompactLayout]) {
@@ -72,14 +72,12 @@
         self.leadingButton, _spaceItem, _newTabButton, _spaceItem,
         self.trailingButton
       ]];
-      self.clipsToBounds = NO;
       [self setBackgroundImage:_translucentBackground
             forToolbarPosition:UIBarPositionAny
                     barMetrics:UIBarMetricsDefault];
     } else {
       self.newTabButton.sizeClass = TabGridNewTabButtonSizeClassLarge;
       [self setItems:@[ _spaceItem, _newTabButton ]];
-      self.clipsToBounds = YES;
       [self setBackgroundImage:_transparentBackground
             forToolbarPosition:UIToolbarPositionAny
                     barMetrics:UIBarMetricsDefault];
diff --git a/ios/chrome/browser/ui/translate/BUILD.gn b/ios/chrome/browser/ui/translate/BUILD.gn
index 3a626b1..8ff4681 100644
--- a/ios/chrome/browser/ui/translate/BUILD.gn
+++ b/ios/chrome/browser/ui/translate/BUILD.gn
@@ -9,18 +9,28 @@
     "language_selection_coordinator.mm",
     "language_selection_mediator.h",
     "language_selection_mediator.mm",
+    "translate_popup_menu_coordinator.h",
+    "translate_popup_menu_coordinator.mm",
+    "translate_popup_menu_mediator.h",
+    "translate_popup_menu_mediator.mm",
   ]
   deps = [
     ":translate_ui",
     "//base",
+    "//components/strings:components_strings_grit",
     "//components/translate/core/browser",
     "//ios/chrome/browser",
     "//ios/chrome/browser/translate",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/list_model",
+    "//ios/chrome/browser/ui/popup_menu/public:popup_menu_ui",
+    "//ios/chrome/browser/ui/popup_menu/public:ui_constants",
     "//ios/chrome/browser/ui/presenters",
     "//ios/chrome/browser/ui/translate/cells",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public",
+    "//ui/base",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.h b/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.h
new file mode 100644
index 0000000..c681d9d
--- /dev/null
+++ b/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+class WebStateList;
+
+// Coordinator for the translate infobar's language selection and translate
+// option popup menus.
+@interface TranslatePopupMenuCoordinator : ChromeCoordinator
+
+// Creates a coordinator that uses |viewController|, |browserState|, and
+// |webStateList|.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+                              webStateList:(WebStateList*)webStateList;
+
+// Unavailable, use -initWithBaseViewController:browserState:webStateList:.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+    NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.mm b/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.mm
new file mode 100644
index 0000000..c8cec8b
--- /dev/null
+++ b/ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.mm
@@ -0,0 +1,243 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/translate/translate_popup_menu_coordinator.h"
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/translate/language_selection_context.h"
+#import "ios/chrome/browser/translate/language_selection_delegate.h"
+#import "ios/chrome/browser/translate/language_selection_handler.h"
+#import "ios/chrome/browser/translate/translate_option_selection_delegate.h"
+#import "ios/chrome/browser/translate/translate_option_selection_handler.h"
+#import "ios/chrome/browser/ui/list_model/list_model.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter_delegate.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller_delegate.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_ui_constants.h"
+#import "ios/chrome/browser/ui/translate/cells/select_language_popup_menu_item.h"
+#import "ios/chrome/browser/ui/translate/translate_popup_menu_mediator.h"
+#import "ios/chrome/browser/ui/util/layout_guide_names.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface TranslatePopupMenuCoordinator () <
+    LanguageSelectionHandler,
+    PopupMenuPresenterDelegate,
+    PopupMenuTableViewControllerDelegate,
+    TranslateOptionSelectionHandler>
+
+// The WebStateList this coordinator observes.
+@property(nonatomic, assign) WebStateList* webStateList;
+// Presenter for the popup menu, managing the animations.
+@property(nonatomic, strong) PopupMenuPresenter* presenter;
+// Mediator for the popup menu.
+@property(nonatomic, strong) TranslatePopupMenuMediator* mediator;
+// ViewController for this coordinator.
+@property(nonatomic, strong) PopupMenuTableViewController* viewController;
+// Language selection delegate.
+@property(nonatomic, weak) id<LanguageSelectionDelegate>
+    languageSelectionDelegate;
+// Translate option selection delegate.
+@property(nonatomic, weak) id<TranslateOptionSelectionDelegate>
+    translateOptionSelectionDelegate;
+// YES if the coordinator has been started.
+@property(nonatomic) BOOL started;
+
+@end
+
+@implementation TranslatePopupMenuCoordinator
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                              browserState:
+                                  (ios::ChromeBrowserState*)browserState
+                              webStateList:(WebStateList*)webStateList {
+  DCHECK(webStateList);
+  self = [super initWithBaseViewController:viewController
+                              browserState:browserState];
+  if (self) {
+    _webStateList = webStateList;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  if (self.started)
+    return;
+
+  self.mediator =
+      [[TranslatePopupMenuMediator alloc] initWithSelectionHandler:self];
+  self.mediator.webStateList = self.webStateList;
+
+  self.started = YES;
+}
+
+- (void)stop {
+  if (!self.started)
+    return;
+
+  [self dismissPopupMenu];
+  [self.mediator disconnect];
+  self.mediator = nil;
+  self.webStateList = nullptr;
+  self.presenter = nil;
+  self.viewController = nil;
+  self.languageSelectionDelegate = nil;
+  self.translateOptionSelectionDelegate = nil;
+
+  self.started = NO;
+}
+
+#pragma mark - LanguageSelectionHandler
+
+- (void)showLanguageSelectorWithContext:(LanguageSelectionContext*)context
+                               delegate:
+                                   (id<LanguageSelectionDelegate>)delegate {
+  if (self.presenter)
+    return;
+
+  self.translateOptionSelectionDelegate = nil;
+  self.languageSelectionDelegate = delegate;
+
+  self.mediator.type = TranslatePopupMenuTypeLanguageSelection;
+  self.mediator.infobarDelegate = context.languageData;
+  self.mediator.unavailableLanguageIndex = context.unavailableLanguageIndex;
+
+  [self presentPopupMenu];
+}
+
+- (void)dismissLanguageSelector {
+  if (!self.presenter)
+    return;
+
+  [self dismissPopupMenu];
+}
+
+#pragma mark - TranslateOptionSelectionHandler
+
+- (void)
+    showTranslateOptionSelectorWithInfoBarDelegate:
+        (const translate::TranslateInfoBarDelegate*)infobarDelegate
+                                          delegate:
+                                              (id<TranslateOptionSelectionDelegate>)
+                                                  delegate {
+  if (self.presenter)
+    return;
+
+  self.translateOptionSelectionDelegate = delegate;
+  self.languageSelectionDelegate = nil;
+
+  self.mediator.type = TranslatePopupMenuTypeTranslateOptionSelection;
+  self.mediator.infobarDelegate = infobarDelegate;
+  self.mediator.unavailableLanguageIndex = -1;
+
+  [self presentPopupMenu];
+}
+
+- (void)dismissTranslateOptionSelector {
+  if (!self.presenter)
+    return;
+
+  [self dismissPopupMenu];
+}
+
+#pragma mark - PopupMenuTableViewControllerDelegate
+
+- (void)popupMenuTableViewController:(PopupMenuTableViewController*)sender
+                       didSelectItem:(TableViewItem<PopupMenuItem>*)item
+                              origin:(CGPoint)origin {
+  [self dismissPopupMenu];
+
+  switch (item.actionIdentifier) {
+    case PopupMenuActionSelectLanguage: {
+      SelectLanguagePopupMenuItem* languageItem =
+          base::mac::ObjCCastStrict<SelectLanguagePopupMenuItem>(item);
+      [self.languageSelectionDelegate
+          languageSelectorSelectedLanguage:base::SysNSStringToUTF8(
+                                               languageItem.languageCode)];
+      break;
+    }
+    case PopupMenuActionChangeTargetLanguage: {
+      [self.translateOptionSelectionDelegate
+          popupMenuTableViewControllerDidSelectTargetLanguageSelector:sender];
+      break;
+    }
+    case PopupMenuActionAlwaysTranslateSourceLanguage: {
+      [self.translateOptionSelectionDelegate
+          popupMenuTableViewControllerDidSelectAlwaysTranslateSourceLanguage:
+              sender];
+      break;
+    }
+    case PopupMenuActionNeverTranslateSourceLanguage: {
+      [self.translateOptionSelectionDelegate
+          popupMenuTableViewControllerDidSelectNeverTranslateSourceLanguage:
+              sender];
+      break;
+    }
+    case PopupMenuActionNeverTranslateSite: {
+      [self.translateOptionSelectionDelegate
+          popupMenuTableViewControllerDidSelectNeverTranslateSite:sender];
+      break;
+    }
+    case PopupMenuActionChangeSourceLanguage: {
+      [self.translateOptionSelectionDelegate
+          popupMenuTableViewControllerDidSelectSourceLanguageSelector:sender];
+      break;
+    }
+    default:
+      NOTREACHED() << "Unexpected identifier";
+      break;
+  }
+}
+
+#pragma mark - ContainedPresenterDelegate
+
+- (void)containedPresenterDidPresent:(id<ContainedPresenter>)presenter {
+  // noop.
+}
+
+- (void)containedPresenterDidDismiss:(id<ContainedPresenter>)presenter {
+  // noop.
+}
+
+#pragma mark - PopupMenuPresenterDelegate
+
+- (void)popupMenuPresenterWillDismiss:(PopupMenuPresenter*)presenter {
+  [self dismissPopupMenu];
+  [self.languageSelectionDelegate languageSelectorClosedWithoutSelection];
+  [self.translateOptionSelectionDelegate
+      popupMenuPresenterDidCloseWithoutSelection:presenter];
+}
+
+#pragma mark - Private
+
+// Presents a popup menu with animation.
+- (void)presentPopupMenu {
+  self.viewController = [[PopupMenuTableViewController alloc] init];
+  self.viewController.baseViewController = self.baseViewController;
+  self.viewController.delegate = self;
+
+  self.mediator.consumer = self.viewController;
+
+  self.presenter = [[PopupMenuPresenter alloc] init];
+  self.presenter.baseViewController = self.baseViewController;
+  self.presenter.presentedViewController = self.viewController;
+  self.presenter.guideName = kTranslateInfobarOptionsGuide;
+  self.presenter.delegate = self;
+  [self.presenter prepareForPresentation];
+  [self.presenter presentAnimated:YES];
+}
+
+- (void)dismissPopupMenu {
+  [self.presenter dismissAnimated:NO];
+  self.presenter = nil;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.h b/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.h
new file mode 100644
index 0000000..02e06e4
--- /dev/null
+++ b/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.h
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+namespace translate {
+class TranslateInfoBarDelegate;
+}
+
+@class LanguageSelectionContext;
+@protocol LanguageSelectionHandler;
+@protocol PopupMenuConsumer;
+@protocol TranslateOptionSelectionHandler;
+class WebStateList;
+
+// Translate popup menu types.
+typedef NS_ENUM(NSInteger, TranslatePopupMenuType) {
+  TranslatePopupMenuTypeLanguageSelection,
+  TranslatePopupMenuTypeTranslateOptionSelection,
+};
+
+// Mediator object to configure and provide data for the translate infobar's
+// language selection and translate option popup menus.
+@interface TranslatePopupMenuMediator : NSObject
+
+// |handler| presents and dismisses the language selection UI as well as the
+// translate option selection UI.
+- (instancetype)initWithSelectionHandler:
+    (id<LanguageSelectionHandler, TranslateOptionSelectionHandler>)handler
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Disconnects the mediator.
+- (void)disconnect;
+
+// Type of the translate popup menu.
+@property(nonatomic, assign) TranslatePopupMenuType type;
+
+// The WebStateList that this mediator observes.
+@property(nonatomic, assign) WebStateList* webStateList;
+
+// Provides a list of available languages as well as the current source and
+// target languages.
+@property(nonatomic, assign)
+    const translate::TranslateInfoBarDelegate* infobarDelegate;
+
+// Index of the language unavailable for selection depending on if the language
+// selection UI is being populated for the source or the target language.
+@property(nonatomic, assign) NSUInteger unavailableLanguageIndex;
+
+// The consumer to be configured with this mediator.
+@property(nonatomic, weak) id<PopupMenuConsumer> consumer;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_POPUP_MENU_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.mm b/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.mm
new file mode 100644
index 0000000..ccf45b8
--- /dev/null
+++ b/ios/chrome/browser/ui/translate/translate_popup_menu_mediator.mm
@@ -0,0 +1,244 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/translate/translate_popup_menu_mediator.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/scoped_observer.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/translate/core/browser/translate_infobar_delegate.h"
+#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "ios/chrome/browser/translate/language_selection_context.h"
+#import "ios/chrome/browser/translate/language_selection_handler.h"
+#import "ios/chrome/browser/translate/translate_option_selection_handler.h"
+#import "ios/chrome/browser/ui/list_model/list_model.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_consumer.h"
+#import "ios/chrome/browser/ui/translate/cells/select_language_popup_menu_item.h"
+#import "ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface TranslatePopupMenuMediator () <WebStateListObserving> {
+  // WebStateList observers.
+  std::unique_ptr<WebStateListObserverBridge> _webStateListObserverBridge;
+  std::unique_ptr<ScopedObserver<WebStateList, WebStateListObserver>>
+      _scopedWebStateListObserver;
+}
+
+// Presents and dismisses the language selection UI as well as the translate
+// option selection UI.
+@property(nonatomic, weak)
+    id<LanguageSelectionHandler, TranslateOptionSelectionHandler>
+        selectionHandler;
+
+@end
+
+@implementation TranslatePopupMenuMediator
+
+- (instancetype)initWithSelectionHandler:
+    (id<LanguageSelectionHandler, TranslateOptionSelectionHandler>)handler {
+  DCHECK(handler);
+  if ((self = [super init])) {
+    _selectionHandler = handler;
+  }
+  return self;
+}
+
+#pragma mark - Public methods
+
+- (void)disconnect {
+  self.webStateList = nil;
+}
+
+#pragma mark - Properties
+
+- (void)setWebStateList:(WebStateList*)webStateList {
+  if (_webStateList == webStateList)
+    return;
+
+  if (_webStateList) {
+    [self removeWebStateListObserver];
+
+    // Uninstall delegates for each WebState in WebStateList.
+    for (int i = 0; i < self.webStateList->count(); i++) {
+      [self uninstallDelegatesForWebState:self.webStateList->GetWebStateAt(i)];
+    }
+  }
+
+  _webStateList = webStateList;
+
+  if (_webStateList) {
+    // Install delegates for each WebState in WebStateList.
+    for (int i = 0; i < _webStateList->count(); i++) {
+      [self installDelegatesForWebState:_webStateList->GetWebStateAt(i)];
+    }
+
+    [self addWebStateListObserver];
+  }
+}
+
+- (void)setConsumer:(id<PopupMenuConsumer>)consumer {
+  _consumer = consumer;
+  [_consumer setPopupMenuItems:self.items];
+}
+
+#pragma mark - Private
+
+// Returns the menu items for either the language selection popup menu or the
+// translate option selection popup menu.
+- (NSArray<NSArray<TableViewItem<PopupMenuItem>*>*>*)items {
+  if (self.type == TranslatePopupMenuTypeLanguageSelection) {
+    return [self languageSelectionItems];
+  }
+  return [self translateOptionSelectionItems];
+}
+
+// Returns the menu items for the language selection popup menu.
+- (NSArray<NSArray<TableViewItem<PopupMenuItem>*>*>*)languageSelectionItems {
+  NSMutableArray* items = [NSMutableArray array];
+  for (size_t index = 0; index < self.infobarDelegate->num_languages();
+       index++) {
+    if (index == self.unavailableLanguageIndex)
+      continue;
+
+    SelectLanguagePopupMenuItem* item =
+        [[SelectLanguagePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+    item.actionIdentifier = PopupMenuActionSelectLanguage;
+    item.title =
+        base::SysUTF16ToNSString(self.infobarDelegate->language_name_at(index));
+    item.languageCode =
+        base::SysUTF8ToNSString(self.infobarDelegate->language_code_at(index));
+    [items addObject:item];
+  }
+
+  return @[ items ];
+}
+
+// Returns the menu items for the translate option selection popup menu.
+- (NSArray<NSArray<TableViewItem<PopupMenuItem>*>*>*)
+    translateOptionSelectionItems {
+  base::string16 originalLanguageName =
+      self.infobarDelegate->original_language_name();
+
+  TranslatePopupMenuItem* selectTargetLanguageItem =
+      [[TranslatePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+  selectTargetLanguageItem.actionIdentifier =
+      PopupMenuActionChangeTargetLanguage;
+  selectTargetLanguageItem.title = base::SysUTF16ToNSString(
+      l10n_util::GetStringUTF16(IDS_TRANSLATE_INFOBAR_OPTIONS_MORE_LANGUAGE));
+
+  TranslatePopupMenuItem* alwaysTranslateLanguageItem =
+      [[TranslatePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+  alwaysTranslateLanguageItem.actionIdentifier =
+      PopupMenuActionAlwaysTranslateSourceLanguage;
+  alwaysTranslateLanguageItem.title =
+      base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(
+          IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS, originalLanguageName));
+  if (self.infobarDelegate->ShouldAlwaysTranslate()) {
+    alwaysTranslateLanguageItem.accessoryType =
+        UITableViewCellAccessoryCheckmark;
+  }
+
+  TranslatePopupMenuItem* neverTranslateLanguageItem =
+      [[TranslatePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+  neverTranslateLanguageItem.actionIdentifier =
+      PopupMenuActionNeverTranslateSourceLanguage;
+  neverTranslateLanguageItem.title =
+      base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(
+          IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_LANG,
+          originalLanguageName));
+
+  TranslatePopupMenuItem* neverTranslateSiteItem =
+      [[TranslatePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+  neverTranslateSiteItem.actionIdentifier = PopupMenuActionNeverTranslateSite;
+  neverTranslateSiteItem.title =
+      base::SysUTF16ToNSString(l10n_util::GetStringUTF16(
+          IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_SITE));
+
+  TranslatePopupMenuItem* selectSourceLanguageItem =
+      [[TranslatePopupMenuItem alloc] initWithType:kItemTypeEnumZero];
+  selectSourceLanguageItem.actionIdentifier =
+      PopupMenuActionChangeSourceLanguage;
+  selectSourceLanguageItem.title =
+      base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(
+          IDS_TRANSLATE_INFOBAR_OPTIONS_NOT_SOURCE_LANGUAGE,
+          originalLanguageName));
+
+  return @[
+    @[ selectTargetLanguageItem ],
+    @[
+      alwaysTranslateLanguageItem, neverTranslateLanguageItem,
+      neverTranslateSiteItem, selectSourceLanguageItem
+    ]
+  ];
+}
+
+// Adds observer for WebStateList.
+- (void)addWebStateListObserver {
+  _webStateListObserverBridge =
+      std::make_unique<WebStateListObserverBridge>(self);
+  _scopedWebStateListObserver =
+      std::make_unique<ScopedObserver<WebStateList, WebStateListObserver>>(
+          _webStateListObserverBridge.get());
+  _scopedWebStateListObserver->Add(self.webStateList);
+}
+
+// Removes observer for WebStateList.
+- (void)removeWebStateListObserver {
+  _scopedWebStateListObserver.reset();
+  _webStateListObserverBridge.reset();
+}
+
+// Installs delegates for |webState|.
+- (void)installDelegatesForWebState:(web::WebState*)webState {
+  if (ChromeIOSTranslateClient::FromWebState(webState)) {
+    ChromeIOSTranslateClient::FromWebState(webState)
+        ->set_language_selection_handler(self.selectionHandler);
+    ChromeIOSTranslateClient::FromWebState(webState)
+        ->set_translate_option_selection_handler(self.selectionHandler);
+  }
+}
+
+// Uninstalls delegates for |webState|.
+- (void)uninstallDelegatesForWebState:(web::WebState*)webState {
+  if (ChromeIOSTranslateClient::FromWebState(webState)) {
+    ChromeIOSTranslateClient::FromWebState(webState)
+        ->set_language_selection_handler(nil);
+    ChromeIOSTranslateClient::FromWebState(webState)
+        ->set_translate_option_selection_handler(nil);
+  }
+}
+
+#pragma mark - WebStateListObserving
+
+- (void)webStateList:(WebStateList*)webStateList
+    didInsertWebState:(web::WebState*)webState
+              atIndex:(int)index
+           activating:(BOOL)activating {
+  [self installDelegatesForWebState:webState];
+}
+
+- (void)webStateList:(WebStateList*)webStateList
+    didReplaceWebState:(web::WebState*)oldWebState
+          withWebState:(web::WebState*)newWebState
+               atIndex:(int)index {
+  [self uninstallDelegatesForWebState:oldWebState];
+  [self installDelegatesForWebState:newWebState];
+}
+
+- (void)webStateList:(WebStateList*)webStateList
+    didDetachWebState:(web::WebState*)webState
+              atIndex:(int)index {
+  [self uninstallDelegatesForWebState:webState];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/util/layout_guide_names.h b/ios/chrome/browser/ui/util/layout_guide_names.h
index d6a291a..e4506b5f 100644
--- a/ios/chrome/browser/ui/util/layout_guide_names.h
+++ b/ios/chrome/browser/ui/util/layout_guide_names.h
@@ -36,6 +36,9 @@
 extern GuideName* const kTabStripTabSwitcherGuide;
 // A guide that is constrained to match the frame of the ToolsMenu button.
 extern GuideName* const kToolsMenuGuide;
+// A guide that is constrained to match the frame of the translate infobar
+// options button.
+extern GuideName* const kTranslateInfobarOptionsGuide;
 // A guide that is constrained to match the frame of the last-tapped voice
 // search button.
 extern GuideName* const kVoiceSearchButtonGuide;
diff --git a/ios/chrome/browser/ui/util/layout_guide_names.mm b/ios/chrome/browser/ui/util/layout_guide_names.mm
index 70154af..8c011ef1 100644
--- a/ios/chrome/browser/ui/util/layout_guide_names.mm
+++ b/ios/chrome/browser/ui/util/layout_guide_names.mm
@@ -19,4 +19,6 @@
 GuideName* const kTabSwitcherGuide = @"kTabSwitcherGuide";
 GuideName* const kTabStripTabSwitcherGuide = @"kTabStripTabSwitcherGuide";
 GuideName* const kToolsMenuGuide = @"kToolsMenuGuide";
+GuideName* const kTranslateInfobarOptionsGuide =
+    @"kTranslateInfobarOptionsGuide";
 GuideName* const kVoiceSearchButtonGuide = @"kVoiceSearchButtonGuide";
diff --git a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
index 666b347..d300c5b 100644
--- a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
+++ b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.h
@@ -6,13 +6,9 @@
 #define IOS_CHROME_BROWSER_UI_VOICE_TEXT_TO_SPEECH_PLAYBACK_CONTROLLER_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace ios {
 class ChromeBrowserState;
 }
@@ -32,8 +28,7 @@
   static TextToSpeechPlaybackControllerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      TextToSpeechPlaybackControllerFactory>;
+  friend class base::NoDestructor<TextToSpeechPlaybackControllerFactory>;
 
   TextToSpeechPlaybackControllerFactory();
 
diff --git a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.mm b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.mm
index d6e52e3..dea8de9 100644
--- a/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.mm
+++ b/ios/chrome/browser/ui/voice/text_to_speech_playback_controller_factory.mm
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -27,7 +27,8 @@
 // static
 TextToSpeechPlaybackControllerFactory*
 TextToSpeechPlaybackControllerFactory::GetInstance() {
-  return base::Singleton<TextToSpeechPlaybackControllerFactory>::get();
+  static base::NoDestructor<TextToSpeechPlaybackControllerFactory> instance;
+  return instance.get();
 }
 
 TextToSpeechPlaybackControllerFactory::TextToSpeechPlaybackControllerFactory()
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
index 66bd4a93..3df3168 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "ios/web/public/webui/web_ui_ios.h"
 #include "ios/web/public/webui/web_ui_ios_controller_factory.h"
 
@@ -28,7 +28,7 @@
   ~ChromeWebUIIOSControllerFactory() override;
 
  private:
-  friend struct base::DefaultSingletonTraits<ChromeWebUIIOSControllerFactory>;
+  friend class base::NoDestructor<ChromeWebUIIOSControllerFactory>;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeWebUIIOSControllerFactory);
 };
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
index 271977c..9deec54 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
@@ -118,7 +118,8 @@
 // static
 ChromeWebUIIOSControllerFactory*
 ChromeWebUIIOSControllerFactory::GetInstance() {
-  return base::Singleton<ChromeWebUIIOSControllerFactory>::get();
+  static base::NoDestructor<ChromeWebUIIOSControllerFactory> instance;
+  return instance.get();
 }
 
 ChromeWebUIIOSControllerFactory::ChromeWebUIIOSControllerFactory() {}
diff --git a/ios/chrome/browser/undo/bookmark_undo_service_factory.cc b/ios/chrome/browser/undo/bookmark_undo_service_factory.cc
index b500a4a..a1b2f1f 100644
--- a/ios/chrome/browser/undo/bookmark_undo_service_factory.cc
+++ b/ios/chrome/browser/undo/bookmark_undo_service_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/undo/bookmark_undo_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -28,7 +28,8 @@
 
 // static
 BookmarkUndoServiceFactory* BookmarkUndoServiceFactory::GetInstance() {
-  return base::Singleton<BookmarkUndoServiceFactory>::get();
+  static base::NoDestructor<BookmarkUndoServiceFactory> instance;
+  return instance.get();
 }
 
 BookmarkUndoServiceFactory::BookmarkUndoServiceFactory()
diff --git a/ios/chrome/browser/undo/bookmark_undo_service_factory.h b/ios/chrome/browser/undo/bookmark_undo_service_factory.h
index 0427de4..648c6bd 100644
--- a/ios/chrome/browser/undo/bookmark_undo_service_factory.h
+++ b/ios/chrome/browser/undo/bookmark_undo_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class BookmarkUndoService;
 
 namespace ios {
@@ -32,7 +28,7 @@
   static BookmarkUndoServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<BookmarkUndoServiceFactory>;
+  friend class base::NoDestructor<BookmarkUndoServiceFactory>;
 
   BookmarkUndoServiceFactory();
   ~BookmarkUndoServiceFactory() override;
diff --git a/ios/chrome/browser/url_loading/url_loading_util.h b/ios/chrome/browser/url_loading/url_loading_util.h
index a618cb76..57e4aed6 100644
--- a/ios/chrome/browser/url_loading/url_loading_util.h
+++ b/ios/chrome/browser/url_loading/url_loading_util.h
@@ -15,10 +15,11 @@
 namespace ios {
 class ChromeBrowserState;
 }
-@class TabModel;
+@protocol SessionWindowRestoring;
 namespace web {
 class WebState;
 }
+class WebStateList;
 
 // Possible results from calling LoadURL().
 enum class URLLoadResult {
@@ -60,9 +61,11 @@
 
 // Returns the result (as defined in the enum definition above) of initiating a
 // URL load as defined in |chrome_params|, using |browser_state| and the active
-// tab in |tab_model|.
+// webState in |web_state_list|. |restorer| is provied for dependencies which
+// may need to save the current session window.
 URLLoadResult LoadURL(const ChromeLoadParams& chrome_params,
                       ios::ChromeBrowserState* browser_state,
-                      TabModel* tab_model);
+                      WebStateList* web_state_list,
+                      id<SessionWindowRestoring> restorer);
 
 #endif  // IOS_CHROME_BROWSER_URL_LOADING_URL_LOADING_UTIL_H_
diff --git a/ios/chrome/browser/url_loading/url_loading_util.mm b/ios/chrome/browser/url_loading/url_loading_util.mm
index dbf9d04..3294759 100644
--- a/ios/chrome/browser/url_loading/url_loading_util.mm
+++ b/ios/chrome/browser/url_loading/url_loading_util.mm
@@ -16,7 +16,6 @@
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h"
 #import "ios/chrome/browser/web/load_timing_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -47,9 +46,9 @@
 
 void LoadJavaScriptURL(const GURL& url,
                        ios::ChromeBrowserState* browser_state,
-                       web::WebState* webState) {
+                       web::WebState* web_state) {
   DCHECK(url.SchemeIs(url::kJavaScriptScheme));
-  DCHECK(webState);
+  DCHECK(web_state);
   PrerenderService* prerenderService =
       PrerenderServiceFactory::GetForBrowserState(browser_state);
   if (prerenderService) {
@@ -57,8 +56,8 @@
   }
   NSString* jsToEval = [base::SysUTF8ToNSString(url.GetContent())
       stringByRemovingPercentEncoding];
-  if (webState)
-    webState->ExecuteUserJavaScript(jsToEval);
+  if (web_state)
+    web_state->ExecuteUserJavaScript(jsToEval);
 }
 
 void RestoreTab(const SessionID session_id,
@@ -75,7 +74,8 @@
 
 URLLoadResult LoadURL(const ChromeLoadParams& chrome_params,
                       ios::ChromeBrowserState* browser_state,
-                      TabModel* tab_model) {
+                      WebStateList* web_state_list,
+                      id<SessionWindowRestoring> restorer) {
   web::NavigationManager::WebLoadParams params = chrome_params.web_params;
   if (chrome_params.disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
     return URLLoadResult::SWITCH_TO_TAB;
@@ -86,8 +86,7 @@
                    transition:params.transition_type
                  browserState:browser_state];
 
-  WebStateList* webStateList = tab_model.webStateList;
-  web::WebState* current_web_state = webStateList->GetActiveWebState();
+  web::WebState* current_web_state = web_state_list->GetActiveWebState();
   DCHECK(current_web_state);
   if (params.transition_type & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) {
     bool isExpectingVoiceSearch =
@@ -112,8 +111,9 @@
   // so.
   PrerenderService* prerenderService =
       PrerenderServiceFactory::GetForBrowserState(browser_state);
-  if (prerenderService && prerenderService->MaybeLoadPrerenderedURL(
-                              params.url, params.transition_type, tab_model)) {
+  if (prerenderService &&
+      prerenderService->MaybeLoadPrerenderedURL(
+          params.url, params.transition_type, web_state_list, restorer)) {
     return URLLoadResult::LOADED_PRERENDER;
   }
 
@@ -145,7 +145,7 @@
   current_web_state->GetNavigationManager()->LoadURLWithParams(params);
 
   // Deactivate the NTP immediately on a load to hide the NTP quickly, but after
-  // calling -LoadURLWithParams.  Otherwise, if the webState has never been
+  // calling -LoadURLWithParams. Otherwise, if the webState has never been
   // visible (such as during startup with an NTP), it's possible the webView can
   // trigger a unnecessary load for chrome://newtab.
   if (params.url.GetOrigin() != kChromeUINewTabURL) {
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 2007648..806d84ab 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -27,6 +27,7 @@
 #import "ios/chrome/browser/ui/chrome_web_view_factory.h"
 #import "ios/chrome/browser/web/error_page_util.h"
 #include "ios/chrome/grit/ios_resources.h"
+#include "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/voice/audio_session_controller.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
@@ -174,6 +175,10 @@
 void ChromeWebClient::PostBrowserURLRewriterCreation(
     web::BrowserURLRewriter* rewriter) {
   rewriter->AddURLRewriter(&WillHandleWebBrowserAboutURL);
+  BrowserURLRewriterProvider* provider =
+      ios::GetChromeBrowserProvider()->GetBrowserURLRewriterProvider();
+  if (provider)
+    provider->AddProviderRewriters(rewriter);
 }
 
 NSString* ChromeWebClient::GetDocumentStartScriptForAllFrames(
diff --git a/ios/chrome/browser/web_data_service_factory.cc b/ios/chrome/browser/web_data_service_factory.cc
index a465b77..2132d4b 100644
--- a/ios/chrome/browser/web_data_service_factory.cc
+++ b/ios/chrome/browser/web_data_service_factory.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -86,7 +86,8 @@
 
 // static
 WebDataServiceFactory* WebDataServiceFactory::GetInstance() {
-  return base::Singleton<WebDataServiceFactory>::get();
+  static base::NoDestructor<WebDataServiceFactory> instance;
+  return instance.get();
 }
 
 WebDataServiceFactory::WebDataServiceFactory()
diff --git a/ios/chrome/browser/web_data_service_factory.h b/ios/chrome/browser/web_data_service_factory.h
index 0911605e..2621128 100644
--- a/ios/chrome/browser/web_data_service_factory.h
+++ b/ios/chrome/browser/web_data_service_factory.h
@@ -9,13 +9,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 class KeywordWebDataService;
 class TokenWebData;
 class WebDataServiceWrapper;
@@ -65,7 +61,7 @@
   static WebDataServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<WebDataServiceFactory>;
+  friend class base::NoDestructor<WebDataServiceFactory>;
 
   WebDataServiceFactory();
   ~WebDataServiceFactory() override;
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index d12889f..edfb0b2a 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -155,6 +155,7 @@
     "//ios/chrome/browser/history:unit_tests",
     "//ios/chrome/browser/itunes_urls:unit_tests",
     "//ios/chrome/browser/language:unit_tests",
+    "//ios/chrome/browser/main:unit_tests",
     "//ios/chrome/browser/metrics:unit_tests",
     "//ios/chrome/browser/metrics:unit_tests_internal",
     "//ios/chrome/browser/net:unit_tests",
diff --git a/ios/chrome/test/earl_grey/chrome_actions.h b/ios/chrome/test/earl_grey/chrome_actions.h
index d2c58e59..80d3283 100644
--- a/ios/chrome/test/earl_grey/chrome_actions.h
+++ b/ios/chrome/test/earl_grey/chrome_actions.h
@@ -23,11 +23,6 @@
     web::test::ElementSelector selector,
     bool triggers_context_menu);
 
-// Action to turn the switch of a LegacySettingsSwitchCell to the given |on|
-// state.
-// TODO(crbug.com/894800): Remove this.
-id<GREYAction> TurnLegacySettingsSwitchOn(BOOL on);
-
 // Action to turn the switch of a SettingsSwitchCell to the given |on| state.
 id<GREYAction> TurnSettingsSwitchOn(BOOL on);
 
diff --git a/ios/chrome/test/earl_grey/chrome_actions.mm b/ios/chrome/test/earl_grey/chrome_actions.mm
index c4f53a3..49ff7f62 100644
--- a/ios/chrome/test/earl_grey/chrome_actions.mm
+++ b/ios/chrome/test/earl_grey/chrome_actions.mm
@@ -6,7 +6,6 @@
 
 #import "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/legacy/legacy_sync_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
@@ -64,40 +63,6 @@
         }];
 }
 
-id<GREYAction> TurnLegacySettingsSwitchOn(BOOL on) {
-  id<GREYMatcher> constraints = grey_not(grey_systemAlertViewShown());
-  NSString* action_name =
-      [NSString stringWithFormat:@"Turn settings switch to %@ state",
-                                 on ? @"ON" : @"OFF"];
-  return [GREYActionBlock
-      actionWithName:action_name
-         constraints:constraints
-        performBlock:^BOOL(id collection_view_cell,
-                           __strong NSError** error_or_nil) {
-          LegacySettingsSwitchCell* switch_cell =
-              base::mac::ObjCCast<LegacySettingsSwitchCell>(
-                  collection_view_cell);
-          if (!switch_cell) {
-            *error_or_nil =
-                [NSError errorWithDomain:kChromeActionsErrorDomain
-                                    code:0
-                                userInfo:@{
-                                  NSLocalizedDescriptionKey :
-                                      @"The element isn't of the expected type "
-                                      @"(LegacySettingsSwitchCell)."
-                                }];
-            return NO;
-          }
-          UISwitch* switch_view = switch_cell.switchView;
-          if (switch_view.on != on) {
-            id<GREYAction> long_press_action = [GREYActions
-                actionForLongPressWithDuration:kGREYLongPressDefaultDuration];
-            return [long_press_action perform:switch_view error:error_or_nil];
-          }
-          return YES;
-        }];
-}
-
 id<GREYAction> TurnSyncSwitchOn(BOOL on) {
   id<GREYMatcher> constraints = grey_not(grey_systemAlertViewShown());
   NSString* actionName = [NSString
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 9625168..3f4d15d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -108,6 +108,10 @@
 + (void)loadURL:(const GURL&)URL {
   chrome_test_util::LoadUrl(URL);
   [ChromeEarlGrey waitForPageToFinishLoading];
+
+  web::WebState* webState = chrome_test_util::GetCurrentWebState();
+  if (webState->ContentIsHTML())
+    web::WaitUntilWindowIdInjected(webState);
 }
 
 + (void)reload {
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index c14f6e4d..4b85e1b 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -108,17 +108,6 @@
                                    BOOL is_toggled_on,
                                    BOOL is_enabled);
 
-// Matcher for LegacySettingsSwitchCell.
-// TODO(crbug.com/894800): Remove this.
-id<GREYMatcher> LegacySettingsSwitchCell(NSString* accessibility_identifier,
-                                         BOOL is_toggled_on);
-
-// Matcher for LegacySettingsSwitchCell.
-// TODO(crbug.com/894800): Remove this.
-id<GREYMatcher> LegacySettingsSwitchCell(NSString* accessibility_identifier,
-                                         BOOL is_toggled_on,
-                                         BOOL is_enabled);
-
 // Matcher for LegacySyncSwitchCell.
 id<GREYMatcher> LegacySyncSwitchCell(NSString* accessibilityLabel,
                                      BOOL isToggledOn);
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 860914b2..dca2ce6 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -24,7 +24,6 @@
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/settings/accounts_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/cells/clear_browsing_data_constants.h"
-#import "ios/chrome/browser/ui/settings/cells/legacy/legacy_settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_item.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
@@ -48,20 +47,11 @@
 
 namespace {
 
-// TODO(crbug.com/894800): Remove |use_new_cell|.
-id<GREYMatcher> SettingsSwitchIsToggledOn(BOOL is_toggled_on,
-                                          BOOL use_new_cell) {
+id<GREYMatcher> SettingsSwitchIsToggledOn(BOOL is_toggled_on) {
   MatchesBlock matches = ^BOOL(id element) {
-    UISwitch* switch_view;
-    if (use_new_cell) {
-      SettingsSwitchCell* switch_cell =
-          base::mac::ObjCCastStrict<SettingsSwitchCell>(element);
-      switch_view = switch_cell.switchView;
-    } else {
-      LegacySettingsSwitchCell* switch_cell =
-          base::mac::ObjCCastStrict<LegacySettingsSwitchCell>(element);
-      switch_view = switch_cell.switchView;
-    }
+    SettingsSwitchCell* switch_cell =
+        base::mac::ObjCCastStrict<SettingsSwitchCell>(element);
+    UISwitch* switch_view = switch_cell.switchView;
     return (switch_view.on && is_toggled_on) ||
            (!switch_view.on && !is_toggled_on);
   };
@@ -75,19 +65,11 @@
                                               descriptionBlock:describe];
 }
 
-// TODO(crbug.com/894800): Remove |use_new_cell|.
-id<GREYMatcher> SettingsSwitchIsEnabled(BOOL is_enabled, BOOL use_new_cell) {
+id<GREYMatcher> SettingsSwitchIsEnabled(BOOL is_enabled) {
   MatchesBlock matches = ^BOOL(id element) {
-    UISwitch* switch_view;
-    if (use_new_cell) {
-      SettingsSwitchCell* switch_cell =
-          base::mac::ObjCCastStrict<SettingsSwitchCell>(element);
-      switch_view = switch_cell.switchView;
-    } else {
-      LegacySettingsSwitchCell* switch_cell =
-          base::mac::ObjCCastStrict<LegacySettingsSwitchCell>(element);
-      switch_view = switch_cell.switchView;
-    }
+    SettingsSwitchCell* switch_cell =
+        base::mac::ObjCCastStrict<SettingsSwitchCell>(element);
+    UISwitch* switch_view = switch_cell.switchView;
     return (switch_view.enabled && is_enabled) ||
            (!switch_view.enabled && !is_enabled);
   };
@@ -318,22 +300,8 @@
                                    BOOL is_toggled_on,
                                    BOOL is_enabled) {
   return grey_allOf(grey_accessibilityID(accessibility_identifier),
-                    SettingsSwitchIsToggledOn(is_toggled_on, YES),
-                    SettingsSwitchIsEnabled(is_enabled, YES),
-                    grey_sufficientlyVisible(), nil);
-}
-
-id<GREYMatcher> LegacySettingsSwitchCell(NSString* accessibility_identifier,
-                                         BOOL is_toggled_on) {
-  return LegacySettingsSwitchCell(accessibility_identifier, is_toggled_on, YES);
-}
-
-id<GREYMatcher> LegacySettingsSwitchCell(NSString* accessibility_identifier,
-                                         BOOL is_toggled_on,
-                                         BOOL is_enabled) {
-  return grey_allOf(grey_accessibilityID(accessibility_identifier),
-                    SettingsSwitchIsToggledOn(is_toggled_on, NO),
-                    SettingsSwitchIsEnabled(is_enabled, NO),
+                    SettingsSwitchIsToggledOn(is_toggled_on),
+                    SettingsSwitchIsEnabled(is_enabled),
                     grey_sufficientlyVisible(), nil);
 }
 
diff --git a/ios/public/provider/chrome/browser/BUILD.gn b/ios/public/provider/chrome/browser/BUILD.gn
index 3c7729c..d2f177e2 100644
--- a/ios/public/provider/chrome/browser/BUILD.gn
+++ b/ios/public/provider/chrome/browser/BUILD.gn
@@ -7,6 +7,8 @@
 source_set("browser") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "browser_url_rewriter_provider.h",
+    "browser_url_rewriter_provider.mm",
     "chrome_browser_provider.h",
     "chrome_browser_provider.mm",
     "geolocation_updater_provider.h",
@@ -15,6 +17,7 @@
   deps = [
     "//base",
     "//components/metrics",
+    "//ios//web/public",
     "//ios/public/provider/chrome/browser/mailto",
   ]
   libs = [ "CoreLocation.framework" ]
diff --git a/ios/public/provider/chrome/browser/browser_url_rewriter_provider.h b/ios/public/provider/chrome/browser/browser_url_rewriter_provider.h
new file mode 100644
index 0000000..8db7c204
--- /dev/null
+++ b/ios/public/provider/chrome/browser/browser_url_rewriter_provider.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef IOS_PUBLIC_PROVIDER_CHROME_BROWSER_BROWSER_URL_REWRITER_PROVIDER_H_
+#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_BROWSER_URL_REWRITER_PROVIDER_H_
+
+#include "ios/web/public/browser_url_rewriter.h"
+
+#include "base/macros.h"
+
+// Provider class for custom BrowserURLRewriter.
+class BrowserURLRewriterProvider {
+ public:
+  BrowserURLRewriterProvider();
+  virtual ~BrowserURLRewriterProvider();
+  // Adds the provider rewriters into |rewriter|.
+  virtual void AddProviderRewriters(web::BrowserURLRewriter* rewriter);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserURLRewriterProvider);
+};
+
+#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_BROWSER_URL_REWRITER_PROVIDER_H_
diff --git a/ios/public/provider/chrome/browser/browser_url_rewriter_provider.mm b/ios/public/provider/chrome/browser/browser_url_rewriter_provider.mm
new file mode 100644
index 0000000..e0f6f93e
--- /dev/null
+++ b/ios/public/provider/chrome/browser/browser_url_rewriter_provider.mm
@@ -0,0 +1,16 @@
+// 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 "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+BrowserURLRewriterProvider::BrowserURLRewriterProvider() = default;
+
+BrowserURLRewriterProvider::~BrowserURLRewriterProvider() = default;
+
+void BrowserURLRewriterProvider::AddProviderRewriters(
+    web::BrowserURLRewriter* rewriter) {}
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index 798f03a3..2898032 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -18,6 +18,7 @@
 
 class AppDistributionProvider;
 class BrandedImageProvider;
+class BrowserURLRewriterProvider;
 class FullscreenProvider;
 class MailtoHandlerProvider;
 class OmahaServiceProvider;
@@ -157,6 +158,9 @@
   // Returns an instance of the fullscreen provider.
   virtual FullscreenProvider* GetFullscreenProvider() const;
 
+  // Returns an instance of the BrowserURLRewriter provider.
+  virtual BrowserURLRewriterProvider* GetBrowserURLRewriterProvider() const;
+
   // Adds and removes observers.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index 4078ae6..133acb19 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -110,6 +110,11 @@
   return nullptr;
 }
 
+BrowserURLRewriterProvider*
+ChromeBrowserProvider::GetBrowserURLRewriterProvider() const {
+  return nullptr;
+}
+
 MailtoHandlerProvider* ChromeBrowserProvider::GetMailtoHandlerProvider() const {
   return mailto_handler_provider_.get();
 }
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index bda3573..105329c6 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: 556fe19d5ac744bf6cddce01b96779b495a9b1fb
+Revision: d1654fd43dbe718ba59310a7aa3f80147f7cf8fd
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 34c8019..2d19ce1 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -452,6 +452,7 @@
     "web_state/js/context_menu_js_unittest.mm",
     "web_state/js/crw_js_injection_manager_unittest.mm",
     "web_state/js/crw_js_post_request_loader_unittest.mm",
+    "web_state/js/crw_js_window_id_manager_unittest.mm",
     "web_state/js/find_in_page_unittest.mm",
     "web_state/js/message_js_unittest.mm",
     "web_state/js/page_script_util_unittest.mm",
@@ -664,6 +665,7 @@
 
   sources = [
     "web_state/js/resources/post_request.js",
+    "web_state/js/resources/window_id.js",
   ]
 }
 
diff --git a/ios/web/browser_state.mm b/ios/web/browser_state.mm
index b95c1592..ae1b9e1b 100644
--- a/ios/web/browser_state.mm
+++ b/ios/web/browser_state.mm
@@ -9,9 +9,9 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/guid.h"
-#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/process/process_handle.h"
 #include "base/task/post_task.h"
 #include "base/token.h"
@@ -39,8 +39,11 @@
 namespace {
 
 // Maps service instance group IDs to associated BrowserState instances.
-base::LazyInstance<std::map<base::Token, BrowserState*>>::DestructorAtExit
-    g_instance_group_to_browser_state = LAZY_INSTANCE_INITIALIZER;
+std::map<base::Token, BrowserState*>& GetInstanceGroupToBrowserState() {
+  static base::NoDestructor<std::map<base::Token, BrowserState*>>
+      instance_group_to_browser_state;
+  return *instance_group_to_browser_state;
+}
 
 // Private key used for safe conversion of base::SupportsUserData to
 // web::BrowserState in web::BrowserState::FromSupportsUserData.
@@ -82,7 +85,7 @@
   ServiceInstanceGroupHolder* holder = static_cast<ServiceInstanceGroupHolder*>(
       browser_state->GetUserData(kServiceInstanceGroup));
   if (holder) {
-    g_instance_group_to_browser_state.Get().erase(holder->instance_group());
+    GetInstanceGroupToBrowserState().erase(holder->instance_group());
   }
 }
 
@@ -278,7 +281,7 @@
   // content::BrowserContext::Initialize). crbug.com/739450
 
   RemoveBrowserStateFromInstanceGroupMap(browser_state);
-  g_instance_group_to_browser_state.Get()[new_group] = browser_state;
+  GetInstanceGroupToBrowserState()[new_group] = browser_state;
   browser_state->SetUserData(
       kServiceInstanceGroup,
       std::make_unique<ServiceInstanceGroupHolder>(new_group));
diff --git a/ios/web/browser_url_rewriter_impl.h b/ios/web/browser_url_rewriter_impl.h
index cad43f2..faa956cf 100644
--- a/ios/web/browser_url_rewriter_impl.h
+++ b/ios/web/browser_url_rewriter_impl.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "ios/web/public/browser_url_rewriter.h"
 
 class GURL;
@@ -30,7 +30,7 @@
   // This object is a singleton:
   BrowserURLRewriterImpl();
   ~BrowserURLRewriterImpl() override;
-  friend struct base::DefaultSingletonTraits<BrowserURLRewriterImpl>;
+  friend class base::NoDestructor<BrowserURLRewriterImpl>;
 
   // The list of known URLRewriters.
   std::vector<URLRewriter> url_rewriters_;
diff --git a/ios/web/browser_url_rewriter_impl.mm b/ios/web/browser_url_rewriter_impl.mm
index 37f65c6..d63c7084 100644
--- a/ios/web/browser_url_rewriter_impl.mm
+++ b/ios/web/browser_url_rewriter_impl.mm
@@ -56,7 +56,8 @@
 
 // static
 BrowserURLRewriterImpl* BrowserURLRewriterImpl::GetInstance() {
-  return base::Singleton<BrowserURLRewriterImpl>::get();
+  static base::NoDestructor<BrowserURLRewriterImpl> instance;
+  return instance.get();
 }
 
 BrowserURLRewriterImpl::BrowserURLRewriterImpl() {
diff --git a/ios/web/download/download_controller_impl.h b/ios/web/download/download_controller_impl.h
index 9caddfe..966186b 100644
--- a/ios/web/download/download_controller_impl.h
+++ b/ios/web/download/download_controller_impl.h
@@ -42,6 +42,7 @@
   // DownloadTaskImpl::Delegate overrides:
   void OnTaskDestroyed(DownloadTaskImpl* task) override;
   NSURLSession* CreateSession(NSString* identifier,
+                              NSArray<NSHTTPCookie*>* cookies,
                               id<NSURLSessionDataDelegate> delegate,
                               NSOperationQueue* delegate_queue) override;
 
diff --git a/ios/web/download/download_controller_impl.mm b/ios/web/download/download_controller_impl.mm
index 871dc6e..fbdb86d 100644
--- a/ios/web/download/download_controller_impl.mm
+++ b/ios/web/download/download_controller_impl.mm
@@ -84,11 +84,18 @@
 
 NSURLSession* DownloadControllerImpl::CreateSession(
     NSString* identifier,
+    NSArray<NSHTTPCookie*>* cookies,
     id<NSURLSessionDataDelegate> delegate,
     NSOperationQueue* delegate_queue) {
   NSURLSessionConfiguration* configuration = [NSURLSessionConfiguration
       backgroundSessionConfigurationWithIdentifier:identifier];
-
+  // Cookies have to be set in session configuration before the session is
+  // created. Once the session is created, the configuration object can't be
+  // edited and configuration property will return a copy of the originally used
+  // configuration.
+  for (NSHTTPCookie* cookie in cookies) {
+    [configuration.HTTPCookieStorage setCookie:cookie];
+  }
   std::string user_agent = GetWebClient()->GetUserAgent(UserAgentType::MOBILE);
   configuration.HTTPAdditionalHeaders = @{
     base::SysUTF8ToNSString(net::HttpRequestHeaders::kUserAgent) :
diff --git a/ios/web/download/download_controller_impl_unittest.mm b/ios/web/download/download_controller_impl_unittest.mm
index 55d901e..5c1badf 100644
--- a/ios/web/download/download_controller_impl_unittest.mm
+++ b/ios/web/download/download_controller_impl_unittest.mm
@@ -31,29 +31,32 @@
 // Test fixture for testing DownloadControllerImpl class.
 class DownloadControllerImplTest : public WebTest {
  protected:
-  DownloadControllerImplTest() : delegate_(download_controller()) {
+  DownloadControllerImplTest()
+      : download_controller_(std::make_unique<DownloadControllerImpl>()),
+        delegate_(download_controller_.get()) {
     web_state_.SetBrowserState(GetBrowserState());
   }
 
-  DownloadController* download_controller() {
-    return DownloadController::FromBrowserState(GetBrowserState());
-  }
-
   TestWebState web_state_;
+  std::unique_ptr<DownloadControllerImpl> download_controller_;
   FakeDownloadControllerDelegate delegate_;
 };
 
 // Tests that DownloadController::GetDelegate returns delegate_.
 TEST_F(DownloadControllerImplTest, Delegate) {
-  ASSERT_EQ(&delegate_, download_controller()->GetDelegate());
+  ASSERT_EQ(&delegate_, download_controller_->GetDelegate());
 }
 
 // Tests that DownloadController::FromBrowserState returns the same object for
 // each call.
 TEST_F(DownloadControllerImplTest, FromBrowserState) {
-  DownloadController* controller = download_controller();
-  ASSERT_TRUE(controller);
-  ASSERT_EQ(controller, download_controller());
+  DownloadController* first_call_controller =
+      DownloadController::FromBrowserState(GetBrowserState());
+  ASSERT_TRUE(first_call_controller);
+  DownloadController* second_call_controller =
+      DownloadController::FromBrowserState(GetBrowserState());
+
+  ASSERT_EQ(first_call_controller, second_call_controller);
 }
 
 // Tests that DownloadController::CreateDownloadTask calls
@@ -61,7 +64,7 @@
 TEST_F(DownloadControllerImplTest, OnDownloadCreated) {
   NSString* identifier = [NSUUID UUID].UUIDString;
   GURL url("https://download.test");
-  download_controller()->CreateDownloadTask(
+  download_controller_->CreateDownloadTask(
       &web_state_, identifier, url, kContentDisposition,
       /*total_bytes=*/-1, kMimeType, ui::PageTransition::PAGE_TRANSITION_TYPED);
 
@@ -84,11 +87,33 @@
 // Tests that DownloadController::FromBrowserState does not crash if used
 // without delegate.
 TEST_F(DownloadControllerImplTest, NullDelegate) {
-  download_controller()->SetDelegate(nullptr);
+  download_controller_->SetDelegate(nullptr);
   GURL url("https://download.test");
-  download_controller()->CreateDownloadTask(
+  download_controller_->CreateDownloadTask(
       &web_state_, [NSUUID UUID].UUIDString, url, kContentDisposition,
       /*total_bytes=*/-1, kMimeType, ui::PageTransition::PAGE_TRANSITION_LINK);
 }
 
+// Tests that DownloadController::CreateSession sets cookies correctly into the
+// session's NSURLSessionConfiguration object.
+TEST_F(DownloadControllerImplTest, SessionCookies) {
+  // Pre iOS 11 cookies accept policy is different for sessions. So setting
+  // cookies will not work.
+  if (@available(iOS 11, *)) {
+    NSString* identifier = [NSUUID UUID].UUIDString;
+    NSURL* cookie_url = [NSURL URLWithString:@"https://download.test"];
+    NSHTTPCookie* cookie = [NSHTTPCookie cookieWithProperties:@{
+      NSHTTPCookieName : @"name",
+      NSHTTPCookieValue : @"value",
+      NSHTTPCookiePath : cookie_url.path,
+      NSHTTPCookieDomain : cookie_url.host,
+      NSHTTPCookieVersion : @1,
+    }];
+    NSURLSession* session = download_controller_->CreateSession(
+        identifier, @[ cookie ], /*delegate=*/nil, /*delegate_queue=*/nil);
+    NSArray* cookies = session.configuration.HTTPCookieStorage.cookies;
+    EXPECT_EQ(1U, cookies.count);
+    EXPECT_NSEQ(cookie, cookies.firstObject);
+  }
+}
 }  // namespace web
diff --git a/ios/web/download/download_task_impl.h b/ios/web/download/download_task_impl.h
index 2c40468..49fdfc4 100644
--- a/ios/web/download/download_task_impl.h
+++ b/ios/web/download/download_task_impl.h
@@ -33,9 +33,10 @@
     // remove all references to the given DownloadTask and stop using it.
     virtual void OnTaskDestroyed(DownloadTaskImpl* task) = 0;
 
-    // Creates background NSURLSession with given |identifier|, |delegate| and
-    // |delegate_queue|.
+    // Creates background NSURLSession with given |identifier|, |cookies|,
+    // |delegate| and |delegate_queue|.
     virtual NSURLSession* CreateSession(NSString* identifier,
+                                        NSArray<NSHTTPCookie*>* cookies,
                                         id<NSURLSessionDataDelegate> delegate,
                                         NSOperationQueue* delegate_queue) = 0;
     virtual ~Delegate() = default;
@@ -79,8 +80,9 @@
   ~DownloadTaskImpl() override;
 
  private:
-  // Creates background NSURLSession with given |identifier|.
-  NSURLSession* CreateSession(NSString* identifier);
+  // Creates background NSURLSession with given |identifier| and |cookies|.
+  NSURLSession* CreateSession(NSString* identifier,
+                              NSArray<NSHTTPCookie*>* cookies);
 
   // Asynchronously returns cookies for WebState associated with this task (on
   // iOS 10 and earlier, the array is always empty as it is not possible to
diff --git a/ios/web/download/download_task_impl.mm b/ios/web/download/download_task_impl.mm
index b62dee28..b82cd02cc 100644
--- a/ios/web/download/download_task_impl.mm
+++ b/ios/web/download/download_task_impl.mm
@@ -338,7 +338,8 @@
   observers_.RemoveObserver(observer);
 }
 
-NSURLSession* DownloadTaskImpl::CreateSession(NSString* identifier) {
+NSURLSession* DownloadTaskImpl::CreateSession(NSString* identifier,
+                                              NSArray<NSHTTPCookie*>* cookies) {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   DCHECK(identifier.length);
   base::WeakPtr<DownloadTaskImpl> weak_this = weak_factory_.GetWeakPtr();
@@ -393,7 +394,8 @@
         }
         completion_handler();
       }];
-  return delegate_->CreateSession(identifier, session_delegate, /*queue=*/nil);
+  return delegate_->CreateSession(identifier, cookies, session_delegate,
+                                  /*queue=*/nil);
 }
 
 void DownloadTaskImpl::GetCookies(
@@ -425,7 +427,7 @@
   DCHECK(writer_);
 
   if (!session_) {
-    session_ = CreateSession(identifier_);
+    session_ = CreateSession(identifier_, cookies);
     DCHECK(session_);
   }
 
@@ -435,8 +437,6 @@
 
   NSURL* url = net::NSURLWithGURL(GetOriginalUrl());
   session_task_ = [session_ dataTaskWithURL:url];
-  [session_.configuration.HTTPCookieStorage storeCookies:cookies
-                                                 forTask:session_task_];
   [session_task_ resume];
   OnDownloadUpdated();
 }
diff --git a/ios/web/download/download_task_impl_unittest.mm b/ios/web/download/download_task_impl_unittest.mm
index 450c074a..e20fa6a 100644
--- a/ios/web/download/download_task_impl_unittest.mm
+++ b/ios/web/download/download_task_impl_unittest.mm
@@ -91,11 +91,13 @@
 
   // Returns mock, which can be accessed via session() method.
   NSURLSession* CreateSession(NSString* identifier,
+                              NSArray<NSHTTPCookie*>* cookies,
                               id<NSURLSessionDataDelegate> delegate,
                               NSOperationQueue* delegate_queue) {
     // Make sure this method is called only once.
     EXPECT_FALSE(session_delegate_);
     session_delegate_ = delegate;
+    cookies_ = [cookies copy];
     return session_;
   }
 
@@ -104,9 +106,13 @@
   id session() { return session_; }
   id<NSURLSessionDataDelegate> session_delegate() { return session_delegate_; }
 
+  // Returns the cookies passed to Create session method.
+  NSArray<NSHTTPCookie*>* cookies() { return cookies_; }
+
  private:
   id<NSURLSessionDataDelegate> session_delegate_;
   id configuration_;
+  NSArray<NSHTTPCookie*>* cookies_ = nil;
   id session_;
 };
 
@@ -535,15 +541,10 @@
   EXPECT_CALL(task_delegate_, OnTaskDestroyed(task_.get()));
 }
 
-// Tests that NSURLSessionConfiguration contains up to date cookie from browser
-// state before the download started.
+// Tests that CreateSession is called with the correct cookies from the cookie
+// store.
 TEST_F(DownloadTaskImplTest, Cookie) {
   if (@available(iOS 11, *)) {
-    // Remove all cookies from the session configuration.
-    auto storage = task_delegate_.configuration().HTTPCookieStorage;
-    for (NSHTTPCookie* cookie in storage.cookies)
-      [storage deleteCookie:cookie];
-
     // Add a cookie to BrowserState.
     NSURL* cookie_url = [NSURL URLWithString:@(kUrl)];
     NSHTTPCookie* cookie = [NSHTTPCookie cookieWithProperties:@{
@@ -559,10 +560,9 @@
     // picked up.
     EXPECT_CALL(task_observer_, OnDownloadUpdated(task_.get()));
     ASSERT_TRUE(Start());
-    EXPECT_EQ(1U, storage.cookies.count);
-    EXPECT_NSEQ(cookie, storage.cookies.firstObject);
+    EXPECT_EQ(1U, task_delegate_.cookies().count);
+    EXPECT_NSEQ(cookie, task_delegate_.cookies().firstObject);
   }
-
   EXPECT_CALL(task_delegate_, OnTaskDestroyed(task_.get()));
 }
 
diff --git a/ios/web/features.mm b/ios/web/features.mm
index 0d8be623..8c9e0223 100644
--- a/ios/web/features.mm
+++ b/ios/web/features.mm
@@ -28,5 +28,7 @@
 const base::Feature kOutOfWebFullscreen{"OutOfWebFullscreen",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kHistoryClobberWorkaround{
+    "WKWebViewHistoryClobberWorkaround", base::FEATURE_ENABLED_BY_DEFAULT};
 }  // namespace features
 }  // namespace web
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 6a81acb..09125cb 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -146,6 +146,9 @@
   // new navigation.
   virtual void SetPendingItemIndex(int index) = 0;
 
+  // Applies the workaround for crbug.com/887497.
+  virtual void ApplyWKWebViewForwardHistoryClobberWorkaround();
+
   // Resets the transient url rewriter list.
   void RemoveTransientURLRewriters();
 
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index e6e2d44..1cab2002 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -125,6 +125,10 @@
 
 void NavigationManagerImpl::DetachFromWebView() {}
 
+void NavigationManagerImpl::ApplyWKWebViewForwardHistoryClobberWorkaround() {
+  NOTREACHED();
+}
+
 void NavigationManagerImpl::RemoveTransientURLRewriters() {
   transient_url_rewriters_.clear();
 }
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.h b/ios/web/navigation/wk_based_navigation_manager_impl.h
index 6224786f..e5cce840 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.h
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.h
@@ -113,6 +113,7 @@
                                    ui::PageTransition transition) override;
   bool IsRestoreSessionInProgress() const override;
   void SetPendingItemIndex(int index) override;
+  void ApplyWKWebViewForwardHistoryClobberWorkaround() override;
 
   // NavigationManager:
   BrowserState* GetBrowserState() const override;
@@ -216,6 +217,15 @@
   void FinishLoadURLWithParams() override;
   bool IsPlaceholderUrl(const GURL& url) const override;
 
+  // Restores the specified navigation session in the current web view. This
+  // differs from Restore() in that it doesn't reset the current navigation
+  // history to empty before restoring. It simply appends the restored session
+  // after the current item, effectively replacing only the forward history.
+  // |last_committed_item_index| is the 0-based index into |items| that the web
+  // view should be navigated to at the end of the restoration.
+  void UnsafeRestore(int last_committed_item_index,
+                     std::vector<std::unique_ptr<NavigationItem>> items);
+
   // The pending main frame navigation item. This is nullptr if there is no
   // pending item or if the pending item is a back-forward navigation, in which
   // case the NavigationItemImpl is stored on the WKBackForwardListItem.
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm
index c294102..fa129de 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.mm
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -444,6 +444,37 @@
   return true;
 }
 
+void WKBasedNavigationManagerImpl::
+    ApplyWKWebViewForwardHistoryClobberWorkaround() {
+  DCHECK(web_view_cache_.IsAttachedToWebView());
+
+  int current_item_index = web_view_cache_.GetCurrentItemIndex();
+  DCHECK_GE(current_item_index, 0);
+
+  int item_count = GetItemCount();
+  DCHECK_LT(current_item_index, item_count);
+
+  std::vector<std::unique_ptr<NavigationItem>> forward_items(
+      item_count - current_item_index);
+
+  for (size_t i = 0; i < forward_items.size(); i++) {
+    const NavigationItemImpl* item =
+        GetNavigationItemImplAtIndex(i + current_item_index);
+    forward_items[i] = std::make_unique<web::NavigationItemImpl>(*item);
+  }
+
+  DiscardNonCommittedItems();
+
+  // Replace forward history in WKWebView with |forward_items|.
+  // |last_committed_item_index| is set to 0 so that when this partial session
+  // restoration finishes, the current item is the first item in
+  // |forward_itmes|, which is also the current item before the session
+  // restoration, but because of crbug.com/887497 is expected to be clobbered
+  // with the wrong web content. The partial restore effectively forces a fresh
+  // load of this item while maintaining forward history.
+  UnsafeRestore(/*last_committed_item_index_=*/0, std::move(forward_items));
+}
+
 void WKBasedNavigationManagerImpl::Restore(
     int last_committed_item_index,
     std::vector<std::unique_ptr<NavigationItem>> items) {
@@ -463,10 +494,16 @@
     delegate_->RemoveWebView();
   }
   DCHECK_EQ(0, GetItemCount());
-  pending_item_index_ = -1;
+  DCHECK_EQ(-1, pending_item_index_);
   previous_item_index_ = -1;
   last_committed_item_index_ = -1;
 
+  UnsafeRestore(last_committed_item_index, std::move(items));
+}
+
+void WKBasedNavigationManagerImpl::UnsafeRestore(
+    int last_committed_item_index,
+    std::vector<std::unique_ptr<NavigationItem>> items) {
   // This function restores session history by loading a magic local file
   // (restore_session.html) into the web view. The session history is encoded
   // in the query parameter. When loaded, restore_session.html parses the
@@ -568,8 +605,11 @@
   }
 
   int index = GetLastCommittedItemIndexInCurrentOrRestoredSession();
-  return index == -1 ? nullptr
-                     : GetNavigationItemImplAtIndex(static_cast<size_t>(index));
+  if (index == -1) {
+    DCHECK_EQ(0, GetItemCount());
+    return nullptr;
+  }
+  return GetNavigationItemImplAtIndex(static_cast<size_t>(index));
 }
 
 int WKBasedNavigationManagerImpl::
diff --git a/ios/web/public/features.h b/ios/web/public/features.h
index 81179f9..f66145a 100644
--- a/ios/web/public/features.h
+++ b/ios/web/public/features.h
@@ -32,6 +32,10 @@
 // Used to use the fullscreen implementation out of web.
 extern const base::Feature kOutOfWebFullscreen;
 
+// Used to enable the workaround for WKWebView history clobber bug
+// (crbug.com/887497).
+extern const base::Feature kHistoryClobberWorkaround;
+
 }  // namespace features
 }  // namespace web
 
diff --git a/ios/web/public/test/earl_grey/js_test_util.h b/ios/web/public/test/earl_grey/js_test_util.h
index 401f5b1..1dfdf80 100644
--- a/ios/web/public/test/earl_grey/js_test_util.h
+++ b/ios/web/public/test/earl_grey/js_test_util.h
@@ -11,6 +11,11 @@
 
 namespace web {
 
+// Waits until the Window ID has been injected and the page is thus ready to
+// respond to JavaScript injection. Fails with a GREYAssert on timeout or if
+// unrecoverable error (such as no web view) occurs.
+void WaitUntilWindowIdInjected(WebState* web_state);
+
 // Executes |javascript| on the given |web_state|, and waits until execution is
 // completed. If |out_error| is not nil, it is set to the error resulting from
 // the execution, if one occurs. The return value is the result of the
diff --git a/ios/web/public/test/earl_grey/js_test_util.mm b/ios/web/public/test/earl_grey/js_test_util.mm
index cbdbd91..a0c2510 100644
--- a/ios/web/public/test/earl_grey/js_test_util.mm
+++ b/ios/web/public/test/earl_grey/js_test_util.mm
@@ -8,6 +8,7 @@
 #import <WebKit/WebKit.h>
 
 #import "base/test/ios/wait_util.h"
+#include "base/timer/elapsed_timer.h"
 #import "ios/web/interstitials/web_interstitial_impl.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 
@@ -28,6 +29,33 @@
   interstitial->ExecuteJavaScript(script, handler);
 }
 
+void WaitUntilWindowIdInjected(WebState* web_state) {
+  bool is_window_id_injected = false;
+  bool is_timeout = false;
+  bool is_unrecoverable_error = false;
+
+  base::ElapsedTimer timer;
+  base::TimeDelta timeout =
+      base::TimeDelta::FromSeconds(kWaitForJSCompletionTimeout);
+
+  // Keep polling until either the JavaScript execution returns with expected
+  // value (indicating that Window ID is set), the timeout occurs, or an
+  // unrecoverable error occurs.
+  while (!is_window_id_injected && !is_timeout && !is_unrecoverable_error) {
+    NSError* error = nil;
+    id result = ExecuteJavaScript(web_state, @"0", &error);
+    if (error) {
+      is_unrecoverable_error = ![error.domain isEqual:WKErrorDomain] ||
+                               error.code != WKErrorJavaScriptExceptionOccurred;
+    } else {
+      is_window_id_injected = [result isEqual:@0];
+    }
+    is_timeout = timeout < timer.Elapsed();
+  }
+  GREYAssertFalse(is_timeout, @"windowID injection timed out");
+  GREYAssertFalse(is_unrecoverable_error, @"script execution error");
+}
+
 id ExecuteJavaScript(WebState* web_state,
                      NSString* javascript,
                      NSError* __autoreleasing* out_error) {
diff --git a/ios/web/service_manager_connection_impl.cc b/ios/web/service_manager_connection_impl.cc
index aa29b35..68988dc 100644
--- a/ios/web/service_manager_connection_impl.cc
+++ b/ios/web/service_manager_connection_impl.cc
@@ -10,9 +10,9 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop_current.h"
+#include "base/no_destructor.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ios/web/public/web_thread.h"
@@ -26,8 +26,11 @@
 namespace web {
 namespace {
 
-base::LazyInstance<std::unique_ptr<ServiceManagerConnection>>::Leaky
-    g_connection_for_process = LAZY_INSTANCE_INITIALIZER;
+std::unique_ptr<ServiceManagerConnection>& GetConnectionForProcess() {
+  static base::NoDestructor<std::unique_ptr<ServiceManagerConnection>>
+      connection_for_process;
+  return *connection_for_process;
+}
 
 }  // namespace
 
@@ -222,8 +225,8 @@
 void ServiceManagerConnection::Set(
     std::unique_ptr<ServiceManagerConnection> connection) {
   DCHECK_CURRENTLY_ON(WebThread::UI);
-  DCHECK(!g_connection_for_process.Get());
-  g_connection_for_process.Get() = std::move(connection);
+  DCHECK(!GetConnectionForProcess());
+  GetConnectionForProcess() = std::move(connection);
 }
 
 // static
@@ -233,7 +236,7 @@
   // otherwise the DCHECK in the above method would fire).
   DCHECK(!web::WebThread::IsThreadInitialized(web::WebThread::UI) ||
          web::WebThread::CurrentlyOn(web::WebThread::UI));
-  return g_connection_for_process.Get().get();
+  return GetConnectionForProcess().get();
 }
 
 // static
@@ -241,7 +244,7 @@
   DCHECK_CURRENTLY_ON(WebThread::UI);
 
   // This joins the service manager controller thread.
-  g_connection_for_process.Get().reset();
+  GetConnectionForProcess().reset();
 }
 
 // static
diff --git a/ios/web/service_manager_context.mm b/ios/web/service_manager_context.mm
index df31e85..bc4a51c6 100644
--- a/ios/web/service_manager_context.mm
+++ b/ios/web/service_manager_context.mm
@@ -13,7 +13,6 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
-#include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.mm b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
index 6a9ee37..1643625 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.mm
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
@@ -32,6 +32,10 @@
       [condition waitWithTimeout:base::test::ios::kWaitForPageLoadTimeout],
       @"Page did not complete loading.");
 
+  web::WebState* webState = web::shell_test_util::GetCurrentWebState();
+  if (webState->ContentIsHTML())
+    web::WaitUntilWindowIdInjected(webState);
+
   // Ensure any UI elements handled by EarlGrey become idle for any subsequent
   // EarlGrey steps.
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
diff --git a/ios/web/web_state/global_web_state_event_tracker.h b/ios/web/web_state/global_web_state_event_tracker.h
index a58da4fe..1cff14c 100644
--- a/ios/web/web_state/global_web_state_event_tracker.h
+++ b/ios/web/web_state/global_web_state_event_tracker.h
@@ -8,16 +8,12 @@
 #include <stddef.h>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
 #include "ios/web/public/web_state/global_web_state_observer.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace web {
 
 // This singleton serves as the mechanism via which GlobalWebStateObservers get
@@ -32,7 +28,7 @@
   void RemoveObserver(GlobalWebStateObserver* observer);
 
  private:
-  friend struct base::DefaultSingletonTraits<GlobalWebStateEventTracker>;
+  friend class base::NoDestructor<GlobalWebStateEventTracker>;
   friend class WebStateEventForwarder;
   friend class WebStateImpl;
 
diff --git a/ios/web/web_state/global_web_state_event_tracker.mm b/ios/web/web_state/global_web_state_event_tracker.mm
index 75b73f5..64f9c4f 100644
--- a/ios/web/web_state/global_web_state_event_tracker.mm
+++ b/ios/web/web_state/global_web_state_event_tracker.mm
@@ -7,7 +7,7 @@
 #include <stddef.h>
 
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #import "ios/web/public/web_state/web_state_user_data.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -17,7 +17,8 @@
 namespace web {
 
 GlobalWebStateEventTracker* GlobalWebStateEventTracker::GetInstance() {
-  return base::Singleton<GlobalWebStateEventTracker>::get();
+  static base::NoDestructor<GlobalWebStateEventTracker> instance;
+  return instance.get();
 }
 
 GlobalWebStateEventTracker::GlobalWebStateEventTracker()
diff --git a/ios/web/web_state/js/BUILD.gn b/ios/web/web_state/js/BUILD.gn
index 9140bf4..de76bc5b 100644
--- a/ios/web/web_state/js/BUILD.gn
+++ b/ios/web/web_state/js/BUILD.gn
@@ -17,6 +17,8 @@
     "crw_js_injection_receiver.mm",
     "crw_js_post_request_loader.h",
     "crw_js_post_request_loader.mm",
+    "crw_js_window_id_manager.h",
+    "crw_js_window_id_manager.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/web_state/js/crw_js_window_id_manager.h b/ios/web/web_state/js/crw_js_window_id_manager.h
new file mode 100644
index 0000000..f334b6a
--- /dev/null
+++ b/ios/web/web_state/js/crw_js_window_id_manager.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
+#define IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+#import <WebKit/WebKit.h>
+
+// Injects the JavaScript file window_id.js which sets __gCrWeb.windowId and
+// manages the windowId for Page->Native->Page messages.
+@interface CRWJSWindowIDManager : NSObject
+
+// A unique window ID is assigned when the script is injected. Can not be null.
+@property(nonatomic, copy, readonly) NSString* windowID;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Initializes CRWJSWindowIDManager. |webView| will be used for script
+// evaluation to inject window ID and can not be null.
+- (instancetype)initWithWebView:(WKWebView*)webView NS_DESIGNATED_INITIALIZER;
+
+// Injects windowId to a web page.
+- (void)inject;
+
+@end
+
+#endif  // IOS_WEB_WEB_STATE_JS_CRW_JS_WINDOW_ID_MANAGER_H_
diff --git a/ios/web/web_state/js/crw_js_window_id_manager.mm b/ios/web/web_state/js/crw_js_window_id_manager.mm
new file mode 100644
index 0000000..f0362b7
--- /dev/null
+++ b/ios/web/web_state/js/crw_js_window_id_manager.mm
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/web_state/js/crw_js_window_id_manager.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "crypto/random.h"
+#import "ios/web/web_state/js/page_script_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Number of random bytes in unique key for window ID. The length of the
+// window ID will be twice this number, as it is hexadecimal encoded.
+const size_t kUniqueKeyLength = 16;
+}  // namespace
+
+@interface CRWJSWindowIDManager () {
+  // Web view used for script evaluation to inject window ID.
+  WKWebView* _webView;
+  // Backs up property with the same name.
+  NSString* _windowID;
+}
+
+// Returns a string of randomized ASCII characters.
++ (NSString*)newUniqueKey;
+
+@end
+
+@implementation CRWJSWindowIDManager
+
+- (NSString*)windowID {
+  return _windowID;
+}
+
+- (instancetype)initWithWebView:(WKWebView*)webView {
+  if ((self = [super init])) {
+    _webView = webView;
+    _windowID = [[self class] newUniqueKey];
+  }
+  return self;
+}
+
+- (void)inject {
+  _windowID = [[self class] newUniqueKey];
+  NSString* script = [web::GetPageScript(@"window_id")
+      stringByReplacingOccurrencesOfString:@"$(WINDOW_ID)"
+                                withString:_windowID];
+  // WKUserScript may not be injected yet. Make windowID script return boolean
+  // indicating whether the injection was successful.
+  NSString* scriptWithResult = [NSString
+      stringWithFormat:@"if (!window.__gCrWeb) {false; } else { %@; true; }",
+                       script];
+
+  __weak CRWJSWindowIDManager* weakSelf = self;
+  [_webView evaluateJavaScript:scriptWithResult
+             completionHandler:^(id result, NSError* error) {
+               if (error) {
+                 DCHECK(error.code == WKErrorWebViewInvalidated ||
+                        error.code == WKErrorWebContentProcessTerminated);
+                 return;
+               }
+
+               DCHECK_EQ(CFBooleanGetTypeID(),
+                         CFGetTypeID((__bridge CFTypeRef)result));
+               if (![result boolValue]) {
+                 // WKUserScript has not been injected yet. Retry window id
+                 // injection, because it is critical for the system to
+                 // function.
+                 [weakSelf inject];
+               }
+             }];
+}
+
+#pragma mark - Private
+
++ (NSString*)newUniqueKey {
+  char randomBytes[kUniqueKeyLength];
+  crypto::RandBytes(randomBytes, kUniqueKeyLength);
+  std::string result = base::HexEncode(randomBytes, kUniqueKeyLength);
+  return base::SysUTF8ToNSString(result);
+}
+
+@end
diff --git a/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm b/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
new file mode 100644
index 0000000..2b96e44
--- /dev/null
+++ b/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
@@ -0,0 +1,95 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/web_state/js/crw_js_window_id_manager.h"
+
+#import <WebKit/WebKit.h>
+
+#include "ios/web/public/test/fakes/test_browser_state.h"
+#import "ios/web/public/test/js_test_util.h"
+#import "ios/web/web_state/js/page_script_util.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+// Test fixture for testing CRWJSWindowIDManager class.
+class JSWindowIDManagerTest : public PlatformTest {
+ protected:
+  TestBrowserState browser_state_;
+};
+
+// Tests that window ID injection by a second manager results in a different
+// window ID.
+TEST_F(JSWindowIDManagerTest, WindowIDDifferentManager) {
+  // Inject the first manager.
+  WKWebView* web_view = [[WKWebView alloc] init];
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
+
+  CRWJSWindowIDManager* manager =
+      [[CRWJSWindowIDManager alloc] initWithWebView:web_view];
+  [manager inject];
+  EXPECT_NSEQ([manager windowID],
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+
+  // Inject the second manager.
+  WKWebView* web_view2 = [[WKWebView alloc] init];
+  test::ExecuteJavaScript(web_view2,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
+
+  CRWJSWindowIDManager* manager2 =
+      [[CRWJSWindowIDManager alloc] initWithWebView:web_view2];
+  [manager2 inject];
+  EXPECT_NSEQ([manager2 windowID],
+              test::ExecuteJavaScript(web_view2, @"window.__gCrWeb.windowId"));
+
+  // Window IDs must be different.
+  EXPECT_NSNE([manager windowID], [manager2 windowID]);
+}
+
+// Tests that injecting multiple times creates a new window ID.
+TEST_F(JSWindowIDManagerTest, MultipleInjections) {
+  WKWebView* web_view = [[WKWebView alloc] init];
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
+
+  // First injection.
+  CRWJSWindowIDManager* manager =
+      [[CRWJSWindowIDManager alloc] initWithWebView:web_view];
+  [manager inject];
+  NSString* windowID = [manager windowID];
+  EXPECT_NSEQ(windowID,
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+
+  // Second injection.
+  [manager inject];
+  EXPECT_NSEQ([manager windowID],
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+
+  EXPECT_NSNE(windowID, [manager windowID]);
+}
+
+// Tests that injection will retry if |window.__gCrWeb| is not present.
+TEST_F(JSWindowIDManagerTest, InjectionRetry) {
+  WKWebView* web_view = [[WKWebView alloc] init];
+
+  CRWJSWindowIDManager* manager =
+      [[CRWJSWindowIDManager alloc] initWithWebView:web_view];
+  [manager inject];
+  EXPECT_TRUE([manager windowID]);
+  EXPECT_FALSE(test::ExecuteJavaScript(web_view, @"window.__gCrWeb"));
+
+  // Now inject window.__gCrWeb and check if window ID injection retried.
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
+  EXPECT_NSEQ([manager windowID],
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/js/page_script_util.mm b/ios/web/web_state/js/page_script_util.mm
index 0bfa89c..b6aa8f0e 100644
--- a/ios/web/web_state/js/page_script_util.mm
+++ b/ios/web/web_state/js/page_script_util.mm
@@ -20,7 +20,9 @@
 // Make sure that script is injected only once. For example, content of
 // WKUserScript can be injected into the same page multiple times
 // without notifying WKNavigationDelegate (e.g. after window.document.write
-// JavaScript call). Wrapping injected script into "if (!injected)" check
+// JavaScript call). Injecting the script multiple times invalidates the
+// __gCrWeb.windowId variable and will break the ability to send messages from
+// JS to the native code. Wrapping injected script into "if (!injected)" check
 // prevents multiple injections into the same page. |script_identifier| should
 // identify the script being injected in order to enforce the injection of
 // |script| to only once.
diff --git a/ios/web/web_state/js/resources/message.js b/ios/web/web_state/js/resources/message.js
index 4449df4c..c643291 100644
--- a/ios/web/web_state/js/resources/message.js
+++ b/ios/web/web_state/js/resources/message.js
@@ -104,6 +104,21 @@
     return;
   }
 
+  var windowId = null;
+  try {
+    windowId = window.top.__gCrWeb['windowId'];
+    // Do nothing if windowId has not been set.
+    if (typeof windowId != 'string') {
+      return;
+    }
+  } catch (e) {
+    // A SecurityError will be thrown if this is a cross origin iframe. Allow
+    // sending the message in this case and it will be filtered by frameID.
+    if (e.name !== 'SecurityError') {
+      throw e;
+    }
+  }
+
   // Some pages/plugins implement Object.prototype.toJSON, which can result
   // in serializing messageQueue_ to an invalid format.
   var originalObjectToJSON = Object.prototype.toJSON;
@@ -114,6 +129,9 @@
       'crwCommand': command,
       'crwFrameId': __gCrWeb.message['getFrameId']()
     };
+    if (windowId) {
+      message['crwWindowId'] = windowId;
+    }
     __gCrWeb.common.sendWebKitMessage(queueObject.scheme, message);
   });
   queueObject.reset();
diff --git a/ios/web/web_state/js/resources/window_id.js b/ios/web/web_state/js/resources/window_id.js
new file mode 100644
index 0000000..563a48a
--- /dev/null
+++ b/ios/web/web_state/js/resources/window_id.js
@@ -0,0 +1,16 @@
+// Copyright 2014 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 file adheres to closure-compiler conventions in order to enable
+// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy
+
+// Script to set windowId.
+(function() {
+// CRWJSWindowIDManager replaces $(WINDOW_ID) with appropriate string upon
+// injection.
+__gCrWeb['windowId'] = '$(WINDOW_ID)';
+
+// Send messages queued since message.js injection.
+__gCrWeb.message.invokeQueues();
+}());
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 6854001d..e9fdac62 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -36,6 +36,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "base/values.h"
 #include "crypto/symmetric_key.h"
 #import "ios/net/http_response_headers_util.h"
@@ -86,6 +87,7 @@
 #include "ios/web/public/webui/web_ui_ios.h"
 #import "ios/web/web_state/error_translation_util.h"
 #import "ios/web/web_state/js/crw_js_post_request_loader.h"
+#import "ios/web/web_state/js/crw_js_window_id_manager.h"
 #import "ios/web/web_state/navigation_context_impl.h"
 #import "ios/web/web_state/page_viewport_state.h"
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
@@ -144,16 +146,6 @@
   CFAbsoluteTime time;
 };
 
-// Struct to store a queued script and corresponding completion block.
-struct QueuedScript {
-  QueuedScript(NSString* script, web::JavaScriptResultBlock block)
-      : script_string(script), completion_block(block) {}
-  // The script to execute.
-  NSString* script_string;
-  // The completion block to run with the result of the script execution.
-  web::JavaScriptResultBlock completion_block;
-};
-
 // Keys for JavaScript command handlers context.
 NSString* const kUserIsInteractingKey = @"userIsInteracting";
 NSString* const kOriginURLKey = @"originURL";
@@ -335,6 +327,9 @@
   // idempotent.
   NSMutableSet* _injectedScriptManagers;
 
+  // Script manager for setting the windowID.
+  CRWJSWindowIDManager* _windowIDJSManager;
+
   // The receiver of JavaScripts.
   CRWJSInjectionReceiver* _jsInjectionReceiver;
 
@@ -359,6 +354,12 @@
   // helps with diagnosing a navigation related crash (crbug.com/565457).
   __weak WKNavigation* _stoppedWKNavigation;
 
+  // Used to poll for a SafeBrowsing warning being displayed. This is created in
+  // |decidePolicyForNavigationAction| and destroyed once any of the following
+  // happens: 1) a SafeBrowsing warning is detected; 2) any WKNavigationDelegate
+  // method is called; 3) |abortLoad| is called.
+  base::RepeatingTimer _safeBrowsingWarningDetectionTimer;
+
   // CRWWebUIManager object for loading WebUI pages.
   CRWWebUIManager* _webUIManager;
 
@@ -376,9 +377,6 @@
   // cert status from |didReceiveAuthenticationChallenge:| to
   // |didFailProvisionalNavigation:| delegate method.
   std::unique_ptr<CertVerificationErrorsCacheType> _certVerificationErrors;
-
-  // Scripts queued while waiting for the main WebFrame to become available.
-  std::vector<QueuedScript> _queuedScripts;
 }
 
 // If |contentView_| contains a web view, this is the web view it contains.
@@ -591,9 +589,9 @@
              senderFrame:(web::WebFrame*)senderFrame;
 // Called when web controller receives a new message from the web page.
 - (void)didReceiveScriptMessage:(WKScriptMessage*)message;
-// Returns a new script which wraps |script| with frameID check so |script| is
-// not evaluated on frameID mismatch.
-- (NSString*)scriptByAddingFrameIDCheckForScript:(NSString*)script;
+// Returns a new script which wraps |script| with windowID check so |script| is
+// not evaluated on windowID mismatch.
+- (NSString*)scriptByAddingWindowIDCheckForScript:(NSString*)script;
 // Attempts to handle a script message. Returns YES on success, NO otherwise.
 - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage;
 // Handles frame became available message.
@@ -884,7 +882,9 @@
 
 // This method should be called on receiving WKNavigationDelegate callbacks. It
 // will log a metric if the callback occurs after the reciever has already been
-// closed.
+// closed. It also stops the SafeBrowsing warning detection timer, since after
+// this point it's too late for a SafeBrowsing warning to be displayed for the
+// navigation for which the timer was started.
 - (void)didReceiveWebViewNavigationDelegateCallback;
 
 // Sets up WebUI for URL.
@@ -892,11 +892,6 @@
 // Clears WebUI, if one exists.
 - (void)clearWebUI;
 
-// Executes the supplied JavaScript in the WebView by wrapping it with a
-// frameId check to ensure it is executed only in the expected page.
-- (void)safelyExecuteJavaScript:(NSString*)script
-              completionHandler:(web::JavaScriptResultBlock)completionHandler;
-
 @end
 
 namespace {
@@ -1258,6 +1253,7 @@
   _webStateImpl->OnNavigationStarted(context.get());
   self.navigationManagerImpl->AddPushStateItemIfNecessary(pageURL, stateObject,
                                                           transition);
+  context->SetHasCommitted(true);
   _webStateImpl->OnNavigationFinished(context.get());
   self.userInteractionRegistered = NO;
 }
@@ -1274,6 +1270,7 @@
   _webStateImpl->OnNavigationStarted(context.get());
   self.navigationManagerImpl->UpdateCurrentItemForReplaceState(pageURL,
                                                                stateObject);
+  context->SetHasCommitted(true);
   _webStateImpl->OnNavigationFinished(context.get());
 }
 
@@ -2117,6 +2114,7 @@
   [_pendingNavigationInfo setCancelled:YES];
   _certVerificationErrors->Clear();
   [self loadCancelled];
+  _safeBrowsingWarningDetectionTimer.Stop();
 }
 
 - (void)loadCancelled {
@@ -2382,17 +2380,7 @@
 
 - (void)executeJavaScript:(NSString*)script
         completionHandler:(web::JavaScriptResultBlock)completionHandler {
-  if (!web::WebFramesManager::FromWebState([self webState])
-           ->GetMainWebFrame()) {
-    _queuedScripts.push_back(QueuedScript(script, completionHandler));
-  } else {
-    [self safelyExecuteJavaScript:script completionHandler:completionHandler];
-  }
-}
-
-- (void)safelyExecuteJavaScript:(NSString*)script
-              completionHandler:(web::JavaScriptResultBlock)completionHandler {
-  NSString* safeScript = [self scriptByAddingFrameIDCheckForScript:script];
+  NSString* safeScript = [self scriptByAddingWindowIDCheckForScript:script];
   web::ExecuteJavaScript(_webView, safeScript, completionHandler);
 }
 
@@ -2511,12 +2499,10 @@
   }
 }
 
-- (NSString*)scriptByAddingFrameIDCheckForScript:(NSString*)script {
-  NSString* kTemplate = @"if (__gCrWeb.message.getFrameId() === '%@') { %@; }";
-  web::WebFrame* mainWebFrame =
-      web::WebFramesManager::FromWebState([self webState])->GetMainWebFrame();
-  NSString* frameID = base::SysUTF8ToNSString(mainWebFrame->GetFrameId());
-  return [NSString stringWithFormat:kTemplate, frameID, script];
+- (NSString*)scriptByAddingWindowIDCheckForScript:(NSString*)script {
+  NSString* kTemplate = @"if (__gCrWeb['windowId'] === '%@') { %@; }";
+  return [NSString
+      stringWithFormat:kTemplate, [_windowIDJSManager windowID], script];
 }
 
 - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage {
@@ -2554,6 +2540,18 @@
       // different origins.
       return NO;
     }
+
+    std::string windowID;
+    // If windowID exists, it must match the ID from the main frame.
+    if (message->GetString("crwWindowId", &windowID)) {
+      if (base::SysNSStringToUTF8([_windowIDJSManager windowID]) != windowID) {
+        DLOG(WARNING)
+            << "Message from JS ignored due to non-matching windowID: "
+            << base::SysNSStringToUTF8([_windowIDJSManager windowID])
+            << " != " << windowID;
+        return NO;
+      }
+    }
   }
 
   base::DictionaryValue* command = nullptr;
@@ -2615,14 +2613,6 @@
     framesManager->AddFrame(std::move(newFrame));
     _webStateImpl->OnWebFrameAvailable(framesManager->GetFrameWithId(frameID));
   }
-
-  if (!_queuedScripts.empty() && message.frameInfo.mainFrame) {
-    std::vector<QueuedScript> queue = std::move(_queuedScripts);
-    for (const QueuedScript& queuedScript : queue) {
-      [self safelyExecuteJavaScript:queuedScript.script_string
-                  completionHandler:queuedScript.completion_block];
-    }
-  }
 }
 
 - (void)frameBecameUnavailableWithMessage:(WKScriptMessage*)message {
@@ -2956,6 +2946,12 @@
   // item will be committed when the native content or webUI is displayed.
   if (!context->IsPlaceholderNavigation()) {
     self.navigationManagerImpl->CommitPendingItem();
+    // If a SafeBrowsing warning is currently displayed, the user has tapped
+    // the button on the warning page to proceed to the site, the site has
+    // started loading, and the warning is about to be removed. In this case,
+    // the transient item for the warning needs to be removed too.
+    if ([self isSafeBrowsingWarningDisplayedInWebView])
+      self.navigationManagerImpl->DiscardNonCommittedItems();
   }
 }
 
@@ -3222,6 +3218,7 @@
   if (_isBeingDestroyed) {
     UMA_HISTOGRAM_BOOLEAN("Renderer.WKWebViewCallbackAfterDestroy", true);
   }
+  _safeBrowsingWarningDetectionTimer.Stop();
 }
 
 - (BOOL)shouldRenderResponse:(WKNavigationResponse*)WKResponse {
@@ -3417,7 +3414,7 @@
       return;
     const NavigationManagerImpl* navigationManager = self.navigationManagerImpl;
     GURL mainDocumentURL =
-        navigationManager->GetItemCount()
+        navigationManager->GetLastCommittedItem()
             ? navigationManager->GetLastCommittedItem()->GetURL()
             : [self currentURL];
     _lastUserInteraction =
@@ -4075,6 +4072,10 @@
     }
                                       name:kFrameBecameUnavailableMessageName
                                    webView:webView];
+
+    _windowIDJSManager = [[CRWJSWindowIDManager alloc] initWithWebView:webView];
+  } else {
+    _windowIDJSManager = nil;
   }
   [_webView setNavigationDelegate:self];
   [_webView setUIDelegate:self];
@@ -4369,6 +4370,44 @@
 
   GURL requestURL = net::GURLWithNSURL(action.request.URL);
 
+  // Workaround for a WKWebView bug where the web content loaded using
+  // |-loadHTMLString:baseURL| clobbers the next WKBackForwardListItem. It works
+  // by detecting back/forward navigation to a clobbered item and replacing the
+  // clobberred item and its forward history using a partial session restore in
+  // the current web view. There is an unfortunate caveat: if the workaround is
+  // triggered in a back navigation to a clobbered item, the restored forward
+  // session is inserted after the current item before the back navigation, so
+  // it doesn't fully replaces the "bad" history, even though user will be
+  // navigated to the expected URL and may not notice the issue until they
+  // review the back history by long pressing on "Back" button.
+  //
+  // TODO(crbug.com/887497): remove this workaround once iOS ships the fix.
+  if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+      action.targetFrame.mainFrame) {
+    GURL webViewURL = net::GURLWithNSURL(webView.URL);
+    GURL currentWKItemURL =
+        net::GURLWithNSURL(webView.backForwardList.currentItem.URL);
+    GURL backItemURL = net::GURLWithNSURL(webView.backForwardList.backItem.URL);
+    web::NavigationContextImpl* context =
+        [self contextForPendingMainFrameNavigationWithURL:webViewURL];
+    bool willClobberHistory =
+        action.navigationType == WKNavigationTypeBackForward &&
+        requestURL == backItemURL && webView.backForwardList.currentItem &&
+        requestURL != currentWKItemURL && currentWKItemURL == webViewURL &&
+        context &&
+        (context->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK);
+
+    UMA_HISTOGRAM_BOOLEAN("IOS.WKWebViewClobberedHistory", willClobberHistory);
+
+    if (willClobberHistory && base::FeatureList::IsEnabled(
+                                  web::features::kHistoryClobberWorkaround)) {
+      decisionHandler(WKNavigationActionPolicyCancel);
+      self.navigationManagerImpl
+          ->ApplyWKWebViewForwardHistoryClobberWorkaround();
+      return;
+    }
+  }
+
   // The page will not be changed until this navigation is committed, so the
   // retrieved state will be pending until |didCommitNavigation| callback.
   [self updatePendingNavigationInfoFromNavigationAction:action];
@@ -4535,6 +4574,56 @@
     }
   }
 
+  // Only try to detect a SafeBrowsing warning if one isn't already displayed,
+  // since the detection logic won't be able to distinguish between the current
+  // warning and a warning for the page that's about to be loaded. Also, since
+  // the purpose of running this logic is to ensure that the right URL is
+  // displayed in the omnibox, don't try to detect a SafeBrowsing warning for
+  // iframe navigations, because the omnibox already shows the correct main
+  // frame URL in that case.
+  if (allowLoad && isMainFrameNavigationAction &&
+      ![self isSafeBrowsingWarningDisplayedInWebView]) {
+    __weak CRWWebController* weakSelf = self;
+    const base::TimeDelta kDelayUntilSafeBrowsingWarningCheck =
+        base::TimeDelta::FromMilliseconds(20);
+    _safeBrowsingWarningDetectionTimer.Start(
+        FROM_HERE, kDelayUntilSafeBrowsingWarningCheck, base::BindRepeating(^{
+          __strong __typeof(weakSelf) strongSelf = weakSelf;
+          if ([strongSelf isSafeBrowsingWarningDisplayedInWebView]) {
+            // Extract state from an existing navigation context if one exists.
+            // Create a new context rather than just re-using the existing one,
+            // since the existing context will continue to be used if the user
+            // decides to proceed to the unsafe page. In that case, WebKit
+            // continues the navigation with the same WKNavigation* that's
+            // associated with the existing context.
+            web::NavigationContextImpl* existingContext = [strongSelf
+                contextForPendingMainFrameNavigationWithURL:requestURL];
+            bool hasUserGesture =
+                existingContext ? existingContext->HasUserGesture() : false;
+            bool isRendererInitiated =
+                existingContext ? existingContext->IsRendererInitiated() : true;
+            std::unique_ptr<web::NavigationContextImpl> context =
+                web::NavigationContextImpl::CreateNavigationContext(
+                    strongSelf->_webStateImpl, requestURL, hasUserGesture,
+                    transition, isRendererInitiated);
+            strongSelf.navigationManagerImpl->AddTransientItem(requestURL);
+            strongSelf.webStateImpl->OnNavigationStarted(context.get());
+            strongSelf.webStateImpl->OnNavigationFinished(context.get());
+            strongSelf->_safeBrowsingWarningDetectionTimer.Stop();
+            if (!existingContext) {
+              // If there's an existing context, observers will already be aware
+              // of a load in progress. Otherwise, observers need to be notified
+              // here, so that if the user decides to go back to the previous
+              // page (stopping the load), observers will be aware of a possible
+              // URL change and the URL displayed in the omnibox will get
+              // updated.
+              DCHECK(strongSelf->_webView.loading);
+              strongSelf->_webStateImpl->SetIsLoading(true);
+            }
+          }
+        }));
+  }
+
   decisionHandler(allowLoad ? WKNavigationActionPolicyAllow
                             : WKNavigationActionPolicyCancel);
 }
@@ -4867,17 +4956,13 @@
       !IsPlaceholderUrl(webViewURL)) {
     _injectedScriptManagers = [[NSMutableSet alloc] init];
     if ([self contentIsHTML] || [self contentIsImage] ||
-        // In unit tests MIME type will be empty, because loadHTML:forURL: does
-        // not notify web view delegate about received response, so web
-        // controller does not get a chance to properly update MIME type.
         self.webState->GetContentsMimeType().empty()) {
-      // This JavaScript call bypasses adding the frameId check because no main
-      // frame may be registered yet until after the response triggered by this
-      // call. For example, when a page is loaded from WebKit's PageCache.
-      // TODO(crbug.com/872134): Remove this call once JavaScript pageshow and
-      // pagehide APIs are reliable.
-      NSString* registerFramesScript = @"__gCrWeb.message.getExistingFrames();";
-      web::ExecuteJavaScript(_webView, registerFramesScript, nil);
+      // In unit tests MIME type will be empty, because loadHTML:forURL: does
+      // not notify web view delegate about received response, so web controller
+      // does not get a chance to properly update MIME type.
+      [_windowIDJSManager inject];
+      web::WebFramesManagerImpl::FromWebState(self.webState)
+          ->RegisterExistingFrames();
     }
   }
 
@@ -4971,6 +5056,7 @@
   _webStateImpl->OnNavigationStarted(context.get());
   [self updateHTML5HistoryState];
   [self setDocumentURL:URL context:context.get()];
+  context->SetHasCommitted(true);
   _webStateImpl->OnNavigationFinished(context.get());
   [self didFinishWithURL:URL loadSuccess:YES context:context.get()];
 }
@@ -5665,6 +5751,7 @@
     _webStateImpl->OnNavigationStarted(navigationContext);
     [self didStartLoading];
     self.navigationManagerImpl->CommitPendingItem();
+    navigationContext->SetHasCommitted(true);
     _webStateImpl->OnNavigationFinished(navigationContext);
 
     [self updateSSLStatusForCurrentNavigationItem];
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index a7684d8f..7b61532 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -481,6 +481,21 @@
   EXPECT_NSEQ(@NO, ExecuteJavaScript(@"false"));
 }
 
+// Tests that a script is not executed on windowID mismatch.
+TEST_P(CRWWebControllerJSExecutionTest, WindowIdMissmatch) {
+  LoadHtml(@"<p></p>");
+  // Script is evaluated since windowID is matched.
+  ExecuteJavaScript(@"window.test1 = '1';");
+  EXPECT_NSEQ(@"1", ExecuteJavaScript(@"window.test1"));
+
+  // Change windowID.
+  ExecuteJavaScript(@"__gCrWeb['windowId'] = '';");
+
+  // Script is not evaluated because of windowID mismatch.
+  ExecuteJavaScript(@"window.test2 = '2';");
+  EXPECT_FALSE(ExecuteJavaScript(@"window.test2"));
+}
+
 INSTANTIATE_TEST_CASES(CRWWebControllerJSExecutionTest);
 
 // Test fixture to test decidePolicyForNavigationResponse:decisionHandler:
diff --git a/ios/web/web_state/web_frames_manager_impl.h b/ios/web/web_state/web_frames_manager_impl.h
index 7b0abb0..dba457a7 100644
--- a/ios/web/web_state/web_frames_manager_impl.h
+++ b/ios/web/web_state/web_frames_manager_impl.h
@@ -31,6 +31,9 @@
   void RemoveFrameWithId(const std::string& frame_id);
   // Removes all web frames from the list of associated web frames.
   void RemoveAllWebFrames();
+  // Broadcasts a (not encrypted) JavaScript message to get the identifiers
+  // and keys of existing frames.
+  void RegisterExistingFrames();
 
   // WebFramesManager overrides
   std::set<WebFrame*> GetAllWebFrames() override;
diff --git a/ios/web/web_state/web_frames_manager_impl.mm b/ios/web/web_state/web_frames_manager_impl.mm
index e1ce5087..e20075f 100644
--- a/ios/web/web_state/web_frames_manager_impl.mm
+++ b/ios/web/web_state/web_frames_manager_impl.mm
@@ -100,4 +100,9 @@
   return main_web_frame_;
 }
 
+void WebFramesManagerImpl::RegisterExistingFrames() {
+  web_state_->ExecuteJavaScript(
+      base::UTF8ToUTF16("__gCrWeb.message.getExistingFrames();"));
+}
+
 }  // namespace
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 732d38b..f7bca8639 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -403,7 +403,7 @@
   EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs(
       page_transition, (*context)->GetPageTransition()));
   EXPECT_TRUE((*context)->IsSameDocument());
-  EXPECT_FALSE((*context)->HasCommitted());
+  EXPECT_TRUE((*context)->HasCommitted());
   EXPECT_FALSE((*context)->IsDownload());
   EXPECT_FALSE((*context)->IsPost());
   EXPECT_FALSE((*context)->GetError());
@@ -715,6 +715,7 @@
 
 }  // namespace
 
+using net::test_server::EmbeddedTestServer;
 using testing::Return;
 using testing::StrictMock;
 using testing::_;
@@ -751,7 +752,7 @@
     WebStateImpl* web_state_impl = reinterpret_cast<WebStateImpl*>(web_state());
     web_state_impl->GetWebController().nativeProvider = provider_;
 
-    test_server_ = std::make_unique<net::test_server::EmbeddedTestServer>();
+    test_server_ = std::make_unique<EmbeddedTestServer>();
     test_server_->RegisterRequestHandler(
         base::BindRepeating(&net::test_server::HandlePrefixedRequest, "/form",
                             base::BindRepeating(&testing::HandleForm)));
@@ -774,7 +775,7 @@
   TestNativeContent* content_;
   std::unique_ptr<StrictMock<PolicyDeciderMock>> decider_;
   StrictMock<WebStateObserverMock> observer_;
-  std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_;
+  std::unique_ptr<EmbeddedTestServer> test_server_;
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -1905,6 +1906,34 @@
   ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state()));
 }
 
+// Tests navigation to a page with self signed SSL cert.
+TEST_P(WebStateObserverTest, FailedSslConnection) {
+  EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
+  ASSERT_TRUE(https_server.Start());
+
+  const GURL url = https_server.GetURL("/");
+  NavigationContext* context = nullptr;
+  int32_t nav_id = 0;
+  EXPECT_CALL(observer_, DidStartLoading(web_state()));
+  WebStatePolicyDecider::RequestInfo request_info(
+      ui::PageTransition::PAGE_TRANSITION_TYPED,
+      /*target_main_frame=*/true, /*has_user_gesture=*/false);
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, RequestInfoMatch(request_info)))
+      .WillOnce(Return(true));
+  EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
+      .WillOnce(VerifyPageStartedContext(
+          web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context,
+          &nav_id));
+  // TODO(crbug.com/921916): DidFinishNavigation is not called for SSL errors.
+  EXPECT_CALL(observer_, DidStopLoading(web_state()));
+
+  test::LoadUrl(web_state(), url);
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return !web_state()->IsLoading();
+  }));
+}
+
 // Tests rejecting the navigation from ShouldAllowRequest. The load should stop,
 // but no other callbacks are called.
 TEST_P(WebStateObserverTest, DisallowRequest) {
diff --git a/ios/web/webui/url_data_manager_ios.cc b/ios/web/webui/url_data_manager_ios.cc
index 746cfe5..599336e 100644
--- a/ios/web/webui/url_data_manager_ios.cc
+++ b/ios/web/webui/url_data_manager_ios.cc
@@ -10,8 +10,8 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/lazy_instance.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/no_destructor.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/synchronization/lock.h"
@@ -29,7 +29,10 @@
 
 const char kURLDataManagerIOSKeyName[] = "url_data_manager";
 
-base::LazyInstance<base::Lock>::Leaky g_delete_lock = LAZY_INSTANCE_INITIALIZER;
+base::Lock& GetDeleteLock() {
+  static base::NoDestructor<base::Lock> delete_lock;
+  return *delete_lock;
+}
 
 URLDataManagerIOS* GetFromBrowserState(BrowserState* browser_state) {
   if (!browser_state->GetUserData(kURLDataManagerIOSKeyName)) {
@@ -75,7 +78,7 @@
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   URLDataSources sources;
   {
-    base::AutoLock lock(g_delete_lock.Get());
+    base::AutoLock lock(GetDeleteLock());
     if (!data_sources_)
       return;
     data_sources_->swap(sources);
@@ -98,7 +101,7 @@
   // to delete.
   bool schedule_delete = false;
   {
-    base::AutoLock lock(g_delete_lock.Get());
+    base::AutoLock lock(GetDeleteLock());
     if (!data_sources_)
       data_sources_ = new URLDataSources();
     schedule_delete = data_sources_->empty();
@@ -128,7 +131,7 @@
 // static
 bool URLDataManagerIOS::IsScheduledForDeletion(
     const URLDataSourceIOSImpl* data_source) {
-  base::AutoLock lock(g_delete_lock.Get());
+  base::AutoLock lock(GetDeleteLock());
   if (!data_sources_)
     return false;
   return base::ContainsValue(*data_sources_, data_source);
diff --git a/ios/web/webui/url_data_manager_ios_backend.mm b/ios/web/webui/url_data_manager_ios_backend.mm
index e6aeae8..c45e514 100644
--- a/ios/web/webui/url_data_manager_ios_backend.mm
+++ b/ios/web/webui/url_data_manager_ios_backend.mm
@@ -10,7 +10,6 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
-#include "base/lazy_instance.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
diff --git a/ios/web/webui/web_ui_ios_controller_factory_registry.cc b/ios/web/webui/web_ui_ios_controller_factory_registry.cc
index d48a8ecd..c06a51d 100644
--- a/ios/web/webui/web_ui_ios_controller_factory_registry.cc
+++ b/ios/web/webui/web_ui_ios_controller_factory_registry.cc
@@ -7,32 +7,36 @@
 #include <stddef.h>
 #include <memory>
 
-#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
 namespace web {
-
-base::LazyInstance<std::vector<WebUIIOSControllerFactory*>>::DestructorAtExit
-    g_factories = LAZY_INSTANCE_INITIALIZER;
+namespace {
+// Returns the global list of registered factories.
+std::vector<WebUIIOSControllerFactory*>& GetGlobalFactories() {
+  static base::NoDestructor<std::vector<WebUIIOSControllerFactory*>> factories;
+  return *factories;
+}
+}  // namespace
 
 void WebUIIOSControllerFactory::RegisterFactory(
     WebUIIOSControllerFactory* factory) {
-  g_factories.Pointer()->push_back(factory);
+  GetGlobalFactories().push_back(factory);
 }
 
 WebUIIOSControllerFactoryRegistry*
 WebUIIOSControllerFactoryRegistry::GetInstance() {
-  return base::Singleton<WebUIIOSControllerFactoryRegistry>::get();
+  static base::NoDestructor<WebUIIOSControllerFactoryRegistry> instance;
+  return instance.get();
 }
 
 std::unique_ptr<WebUIIOSController>
 WebUIIOSControllerFactoryRegistry::CreateWebUIIOSControllerForURL(
     WebUIIOS* web_ui,
     const GURL& url) const {
-  std::vector<WebUIIOSControllerFactory*>* factories = g_factories.Pointer();
-  for (WebUIIOSControllerFactory* factory : *factories) {
+  for (WebUIIOSControllerFactory* factory : GetGlobalFactories()) {
     auto controller = factory->CreateWebUIIOSControllerForURL(web_ui, url);
     if (controller)
       return controller;
diff --git a/ios/web/webui/web_ui_ios_controller_factory_registry.h b/ios/web/webui/web_ui_ios_controller_factory_registry.h
index cc837ba9..eba13ec 100644
--- a/ios/web/webui/web_ui_ios_controller_factory_registry.h
+++ b/ios/web/webui/web_ui_ios_controller_factory_registry.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "ios/web/public/webui/web_ui_ios_controller_factory.h"
 
 namespace web {
@@ -26,7 +26,7 @@
       const GURL& url) const override;
 
  private:
-  friend struct base::DefaultSingletonTraits<WebUIIOSControllerFactoryRegistry>;
+  friend class base::NoDestructor<WebUIIOSControllerFactoryRegistry>;
 
   WebUIIOSControllerFactoryRegistry();
   ~WebUIIOSControllerFactoryRegistry() override;
diff --git a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h
index 61d0889..b530b53 100644
--- a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h
+++ b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace autofill {
 class AutocompleteHistoryManager;
 }
@@ -32,8 +28,7 @@
   static WebViewAutocompleteHistoryManagerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      WebViewAutocompleteHistoryManagerFactory>;
+  friend class base::NoDestructor<WebViewAutocompleteHistoryManagerFactory>;
 
   WebViewAutocompleteHistoryManagerFactory();
   ~WebViewAutocompleteHistoryManagerFactory() override;
diff --git a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
index 6a94bfa..c89b351 100644
--- a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -33,7 +33,8 @@
 // static
 WebViewAutocompleteHistoryManagerFactory*
 WebViewAutocompleteHistoryManagerFactory::GetInstance() {
-  return base::Singleton<WebViewAutocompleteHistoryManagerFactory>::get();
+  static base::NoDestructor<WebViewAutocompleteHistoryManagerFactory> instance;
+  return instance.get();
 }
 
 WebViewAutocompleteHistoryManagerFactory::
diff --git a/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.h b/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.h
index 206ac15..8f0914b 100644
--- a/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.h
+++ b/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace autofill {
 class LegacyStrikeDatabase;
 }
@@ -32,8 +28,7 @@
   static WebViewLegacyStrikeDatabaseFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      WebViewLegacyStrikeDatabaseFactory>;
+  friend class base::NoDestructor<WebViewLegacyStrikeDatabaseFactory>;
 
   WebViewLegacyStrikeDatabaseFactory();
   ~WebViewLegacyStrikeDatabaseFactory() override;
diff --git a/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.mm b/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.mm
index 5ca31549..573a83a 100644
--- a/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_legacy_strike_database_factory.mm
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/legacy_strike_database.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/web_view/internal/app/application_context.h"
@@ -29,7 +29,8 @@
 // static
 WebViewLegacyStrikeDatabaseFactory*
 WebViewLegacyStrikeDatabaseFactory::GetInstance() {
-  return base::Singleton<WebViewLegacyStrikeDatabaseFactory>::get();
+  static base::NoDestructor<WebViewLegacyStrikeDatabaseFactory> instance;
+  return instance.get();
 }
 
 WebViewLegacyStrikeDatabaseFactory::WebViewLegacyStrikeDatabaseFactory()
diff --git a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h
index 12c78025..cfe5584 100644
--- a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h
+++ b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace autofill {
 class PersonalDataManager;
 }
@@ -32,7 +28,7 @@
   static WebViewPersonalDataManagerFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<WebViewPersonalDataManagerFactory>;
+  friend class base::NoDestructor<WebViewPersonalDataManagerFactory>;
 
   WebViewPersonalDataManagerFactory();
   ~WebViewPersonalDataManagerFactory() override;
diff --git a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
index 4914d9f87..4ae0ca5f 100644
--- a/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_personal_data_manager_factory.mm
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -33,7 +33,8 @@
 // static
 WebViewPersonalDataManagerFactory*
 WebViewPersonalDataManagerFactory::GetInstance() {
-  return base::Singleton<WebViewPersonalDataManagerFactory>::get();
+  static base::NoDestructor<WebViewPersonalDataManagerFactory> instance;
+  return instance.get();
 }
 
 WebViewPersonalDataManagerFactory::WebViewPersonalDataManagerFactory()
diff --git a/ios/web_view/internal/autofill/web_view_strike_database_factory.h b/ios/web_view/internal/autofill/web_view_strike_database_factory.h
index 93b48395..f58d2fd 100644
--- a/ios/web_view/internal/autofill/web_view_strike_database_factory.h
+++ b/ios/web_view/internal/autofill/web_view_strike_database_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace autofill {
 class StrikeDatabase;
 }
@@ -31,7 +27,7 @@
   static WebViewStrikeDatabaseFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<WebViewStrikeDatabaseFactory>;
+  friend class base::NoDestructor<WebViewStrikeDatabaseFactory>;
 
   WebViewStrikeDatabaseFactory();
   ~WebViewStrikeDatabaseFactory() override;
diff --git a/ios/web_view/internal/autofill/web_view_strike_database_factory.mm b/ios/web_view/internal/autofill/web_view_strike_database_factory.mm
index 288f470..b6162fa 100644
--- a/ios/web_view/internal/autofill/web_view_strike_database_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_strike_database_factory.mm
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/autofill/core/browser/strike_database.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/web_view/internal/app/application_context.h"
@@ -27,7 +27,8 @@
 
 // static
 WebViewStrikeDatabaseFactory* WebViewStrikeDatabaseFactory::GetInstance() {
-  return base::Singleton<WebViewStrikeDatabaseFactory>::get();
+  static base::NoDestructor<WebViewStrikeDatabaseFactory> instance;
+  return instance.get();
 }
 
 WebViewStrikeDatabaseFactory::WebViewStrikeDatabaseFactory()
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 0fe5a34..7fa0d42 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -18,10 +18,10 @@
 #import "ios/web_view/internal/cwv_web_view_internal.h"
 #include "ios/web_view/internal/signin/ios_web_view_signin_client.h"
 #include "ios/web_view/internal/signin/web_view_account_tracker_service_factory.h"
+#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
 #include "ios/web_view/internal/signin/web_view_oauth2_token_service_factory.h"
 #include "ios/web_view/internal/signin/web_view_signin_client_factory.h"
 #include "ios/web_view/internal/signin/web_view_signin_error_controller_factory.h"
-#include "ios/web_view/internal/signin/web_view_signin_manager_factory.h"
 #import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
 #import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
@@ -161,8 +161,8 @@
     AccountTrackerService* accountTrackerService =
         ios_web_view::WebViewAccountTrackerServiceFactory::GetForBrowserState(
             self.browserState);
-    SigninManager* signinManager =
-        ios_web_view::WebViewSigninManagerFactory::GetForBrowserState(
+    identity::IdentityManager* identityManager =
+        ios_web_view::WebViewIdentityManagerFactory::GetForBrowserState(
             self.browserState);
     ProfileOAuth2TokenService* tokenService =
         ios_web_view::WebViewOAuth2TokenServiceFactory::GetForBrowserState(
@@ -174,7 +174,7 @@
     _syncController = [[CWVSyncController alloc]
         initWithProfileSyncService:profileSyncService
              accountTrackerService:accountTrackerService
-                     signinManager:signinManager
+                   identityManager:identityManager
                       tokenService:tokenService
              signinErrorController:signinErrorController];
 
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.h b/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.h
index b1fb6ac..d37e2eea 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.h
+++ b/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.h
@@ -6,13 +6,9 @@
 #define IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_PASSWORD_MANAGER_INTERNALS_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace password_manager {
 class PasswordManagerInternalsService;
 }
@@ -31,7 +27,7 @@
   static WebViewPasswordManagerInternalsServiceFactory* GetInstance();
 
  private:
-  friend struct base::DefaultSingletonTraits<
+  friend class base::NoDestructor<
       WebViewPasswordManagerInternalsServiceFactory>;
 
   WebViewPasswordManagerInternalsServiceFactory();
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.mm b/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.mm
index 502e707..8aac761 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.mm
+++ b/ios/web_view/internal/passwords/web_view_password_manager_internals_service_factory.mm
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/password_manager/core/browser/password_manager_internals_service.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
@@ -29,7 +29,9 @@
 // static
 WebViewPasswordManagerInternalsServiceFactory*
 WebViewPasswordManagerInternalsServiceFactory::GetInstance() {
-  return base::Singleton<WebViewPasswordManagerInternalsServiceFactory>::get();
+  static base::NoDestructor<WebViewPasswordManagerInternalsServiceFactory>
+      instance;
+  return instance.get();
 }
 
 WebViewPasswordManagerInternalsServiceFactory::
diff --git a/ios/web_view/internal/passwords/web_view_password_store_factory.h b/ios/web_view/internal/passwords/web_view_password_store_factory.h
index 6ff8b8a..985baf1 100644
--- a/ios/web_view/internal/passwords/web_view_password_store_factory.h
+++ b/ios/web_view/internal/passwords/web_view_password_store_factory.h
@@ -7,15 +7,11 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/refcounted_browser_state_keyed_service_factory.h"
 
 enum class ServiceAccessType;
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace password_manager {
 class PasswordStore;
 }
@@ -41,7 +37,7 @@
       WebViewBrowserState* browser_state);
 
  private:
-  friend struct base::DefaultSingletonTraits<WebViewPasswordStoreFactory>;
+  friend class base::NoDestructor<WebViewPasswordStoreFactory>;
 
   WebViewPasswordStoreFactory();
   ~WebViewPasswordStoreFactory() override;
diff --git a/ios/web_view/internal/passwords/web_view_password_store_factory.mm b/ios/web_view/internal/passwords/web_view_password_store_factory.mm
index 277d4a3..5039dbf 100644
--- a/ios/web_view/internal/passwords/web_view_password_store_factory.mm
+++ b/ios/web_view/internal/passwords/web_view_password_store_factory.mm
@@ -9,7 +9,7 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -51,7 +51,8 @@
 
 // static
 WebViewPasswordStoreFactory* WebViewPasswordStoreFactory::GetInstance() {
-  return base::Singleton<WebViewPasswordStoreFactory>::get();
+  static base::NoDestructor<WebViewPasswordStoreFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
index 96491dba..a288602 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -13,12 +13,13 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_error_controller.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
 #include "ios/web/public/web_thread.h"
 #import "ios/web_view/public/cwv_identity.h"
 #import "ios/web_view/public/cwv_sync_controller_data_source.h"
 #import "ios/web_view/public/cwv_sync_controller_delegate.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_mutator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -108,7 +109,7 @@
 @implementation CWVSyncController {
   browser_sync::ProfileSyncService* _profileSyncService;
   AccountTrackerService* _accountTrackerService;
-  SigninManager* _signinManager;
+  identity::IdentityManager* _identityManager;
   ProfileOAuth2TokenService* _tokenService;
   SigninErrorController* _signinErrorController;
   std::unique_ptr<ios_web_view::WebViewSyncControllerObserverBridge> _observer;
@@ -123,14 +124,14 @@
     initWithProfileSyncService:
         (browser_sync::ProfileSyncService*)profileSyncService
          accountTrackerService:(AccountTrackerService*)accountTrackerService
-                 signinManager:(SigninManager*)signinManager
+               identityManager:(identity::IdentityManager*)identityManager
                   tokenService:(ProfileOAuth2TokenService*)tokenService
          signinErrorController:(SigninErrorController*)signinErrorController {
   self = [super init];
   if (self) {
     _profileSyncService = profileSyncService;
     _accountTrackerService = accountTrackerService;
-    _signinManager = signinManager;
+    _identityManager = identityManager;
     _tokenService = tokenService;
     _signinErrorController = signinErrorController;
     _observer =
@@ -157,7 +158,7 @@
 #pragma mark - Public Methods
 
 - (CWVIdentity*)currentIdentity {
-  std::string authenticatedID = _signinManager->GetAuthenticatedAccountId();
+  std::string authenticatedID = _identityManager->GetPrimaryAccountId();
   if (authenticatedID.empty()) {
     return nil;
   }
@@ -187,13 +188,16 @@
   info.full_name = base::SysNSStringToUTF8(identity.fullName);
   std::string newAuthenticatedAccountID =
       _accountTrackerService->SeedAccountInfo(info);
-  _signinManager->OnExternalSigninCompleted(info.email);
+  auto* primaryAccountMutator = _identityManager->GetPrimaryAccountMutator();
+  primaryAccountMutator->SetPrimaryAccount(newAuthenticatedAccountID);
 
   [self reloadCredentials];
 }
 
 - (void)stopSyncAndClearIdentity {
-  _signinManager->SignOut(
+  auto* primaryAccountMutator = _identityManager->GetPrimaryAccountMutator();
+  primaryAccountMutator->ClearPrimaryAccount(
+      identity::PrimaryAccountMutator::ClearAccountsAction::kDefault,
       signin_metrics::ProfileSignout::USER_CLICKED_SIGNOUT_SETTINGS,
       signin_metrics::SignoutDelete::IGNORE_METRIC);
   _dataSource = nil;
@@ -218,7 +222,7 @@
 }
 
 - (void)reloadCredentials {
-  std::string authenticatedID = _signinManager->GetAuthenticatedAccountId();
+  std::string authenticatedID = _identityManager->GetPrimaryAccountId();
   if (!authenticatedID.empty()) {
     ProfileOAuth2TokenServiceIOSDelegate* tokenDelegate =
         static_cast<ProfileOAuth2TokenServiceIOSDelegate*>(
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_internal.h b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
index 50448c9..78757c3 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_internal.h
+++ b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
@@ -18,8 +18,11 @@
 class ProfileSyncService;
 }  // namespace browser_sync
 
+namespace identity {
+class IdentityManager;
+}
+
 class AccountTrackerService;
-class SigninManager;
 class ProfileOAuth2TokenService;
 class SigninErrorController;
 
@@ -30,7 +33,7 @@
     initWithProfileSyncService:
         (browser_sync::ProfileSyncService*)profileSyncService
          accountTrackerService:(AccountTrackerService*)accountTrackerService
-                 signinManager:(SigninManager*)signinManager
+               identityManager:(identity::IdentityManager*)identityManager
                   tokenService:(ProfileOAuth2TokenService*)tokenService
          signinErrorController:(SigninErrorController*)SigninErrorController
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
index 40168f0..daf93cc7 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -26,6 +26,7 @@
 #include "google_apis/gaia/google_service_auth_error.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#include "ios/web_view/internal/app/application_context.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
 #import "ios/web_view/public/cwv_identity.h"
 #import "ios/web_view/public/cwv_sync_controller_data_source.h"
@@ -88,6 +89,8 @@
 
     account_tracker_service_.Initialize(browser_state_.GetPrefs(),
                                         base::FilePath());
+    signin_manager_.Initialize(
+        ApplicationContext::GetInstance()->GetLocalState());
 
     EXPECT_CALL(*profile_sync_service_, AddObserver(_))
         .WillOnce(Invoke(this, &CWVSyncControllerTest::AddObserver));
@@ -95,7 +98,7 @@
     sync_controller_ = [[CWVSyncController alloc]
         initWithProfileSyncService:profile_sync_service_.get()
              accountTrackerService:&account_tracker_service_
-                     signinManager:&signin_manager_
+                   identityManager:identity_test_env_.identity_manager()
                       tokenService:&token_service_
              signinErrorController:&signin_error_controller_];
   };
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index 71460309..85e8d26 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -146,6 +146,14 @@
                    base::Unretained(this)));
     connector_->set_enforce_errors_from_incoming_receiver(false);
     connector_->SetWatcherHeapProfilerTag("IPC Channel");
+
+    // Don't let the Connector do any sort of queuing on our behalf. Individual
+    // messages bound for the IPC::ChannelProxy thread (i.e. that vast majority
+    // of messages received by this Connector) are already individually
+    // scheduled for dispatch by ChannelProxy, so Connector's normal mode of
+    // operation would only introduce a redundant scheduling step for most
+    // messages.
+    connector_->set_force_immediate_dispatch(true);
   }
 
   void Pause() {
diff --git a/jingle/glue/network_service_async_socket.cc b/jingle/glue/network_service_async_socket.cc
index 89c411f0..122b3d93f 100644
--- a/jingle/glue/network_service_async_socket.cc
+++ b/jingle/glue/network_service_async_socket.cc
@@ -16,7 +16,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
-#include "third_party/webrtc/rtc_base/socket_address.h"
 
 namespace jingle_glue {
 
@@ -138,13 +137,13 @@
 
 // STATE_CLOSED -> STATE_CONNECTING
 
-bool NetworkServiceAsyncSocket::Connect(const rtc::SocketAddress& address) {
+bool NetworkServiceAsyncSocket::Connect(const net::HostPortPair& address) {
   if (state_ != STATE_CLOSED) {
     LOG(DFATAL) << "Connect() called on non-closed socket";
     DoNonNetError(ERROR_WRONGSTATE);
     return false;
   }
-  if (address.hostname().empty() || address.port() == 0) {
+  if (address.host().empty() || address.port() == 0) {
     DoNonNetError(ERROR_DNS);
     return false;
   }
@@ -155,8 +154,6 @@
 
   state_ = STATE_CONNECTING;
 
-  net::HostPortPair dest_host_port_pair(address.hostname(), address.port());
-
   get_socket_factory_callback_.Run(mojo::MakeRequest(&socket_factory_));
 
   network::mojom::SocketObserverPtr socket_observer;
@@ -167,7 +164,7 @@
   options->use_tls = false;
   options->fake_tls_handshake = use_fake_tls_handshake_;
   socket_factory_->CreateProxyResolvingSocket(
-      GURL("https://" + dest_host_port_pair.ToString()), std::move(options),
+      GURL("https://" + address.ToString()), std::move(options),
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation_),
       mojo::MakeRequest(&socket_), std::move(socket_observer),
       base::BindOnce(&NetworkServiceAsyncSocket::ProcessConnectDone,
diff --git a/jingle/glue/network_service_async_socket.h b/jingle/glue/network_service_async_socket.h
index 7dbe4a8c..57e845b2 100644
--- a/jingle/glue/network_service_async_socket.h
+++ b/jingle/glue/network_service_async_socket.h
@@ -21,6 +21,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/base/host_port_pair.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
 #include "third_party/libjingle_xmpp/xmpp/asyncsocket.h"
@@ -68,7 +69,7 @@
   // Otherwise, starts the connection process and returns true.
   // SignalConnected will be raised when the connection is successful;
   // otherwise, SignalClosed will be raised with a net error set.
-  bool Connect(const rtc::SocketAddress& address) override;
+  bool Connect(const net::HostPortPair& address) override;
 
   // Tries to read at most |len| bytes into |data|.
   //
diff --git a/jingle/glue/network_service_async_socket_unittest.cc b/jingle/glue/network_service_async_socket_unittest.cc
index 84a94c0..28bc2a73 100644
--- a/jingle/glue/network_service_async_socket_unittest.cc
+++ b/jingle/glue/network_service_async_socket_unittest.cc
@@ -22,6 +22,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/address_list.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
 #include "net/cert/mock_cert_verifier.h"
@@ -35,8 +36,6 @@
 #include "services/network/proxy_resolving_socket_factory_mojo.h"
 #include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/webrtc/rtc_base/ip_address.h"
-#include "third_party/webrtc/rtc_base/socket_address.h"
 #include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
 
 namespace jingle_glue {
@@ -274,7 +273,7 @@
       : ssl_socket_data_provider_(net::ASYNC, net::OK),
         use_mojo_level_mock_(use_mojo_level_mock),
         mock_proxy_resolving_socket_factory_(nullptr),
-        addr_("localhost", 35) {
+        addr_({"localhost", 35}) {
     // GTest death tests by default execute in a fork()ed but not exec()ed
     // process. On macOS, a CoreFoundation-backed MessageLoop will exit with a
     // __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__
@@ -570,7 +569,7 @@
 
   std::unique_ptr<NetworkServiceAsyncSocket> ns_async_socket_;
   base::circular_deque<SignalSocketState> signal_socket_states_;
-  const rtc::SocketAddress addr_;
+  const net::HostPortPair addr_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(NetworkServiceAsyncSocketTest);
@@ -610,20 +609,8 @@
   ExpectClosed();
 }
 
-TEST_F(NetworkServiceAsyncSocketTest, NoHostnameConnect) {
-  rtc::IPAddress ip_address;
-  EXPECT_TRUE(rtc::IPFromString("127.0.0.1", &ip_address));
-  const rtc::SocketAddress no_hostname_addr(ip_address, addr_.port());
-  EXPECT_FALSE(ns_async_socket_->Connect(no_hostname_addr));
-  ExpectErrorState(NetworkServiceAsyncSocket::STATE_CLOSED,
-                   NetworkServiceAsyncSocket::ERROR_DNS);
-
-  EXPECT_TRUE(ns_async_socket_->Close());
-  ExpectClosed();
-}
-
 TEST_F(NetworkServiceAsyncSocketTest, ZeroPortConnect) {
-  const rtc::SocketAddress zero_port_addr(addr_.hostname(), 0);
+  const net::HostPortPair zero_port_addr({addr_.host(), 0});
   EXPECT_FALSE(ns_async_socket_->Connect(zero_port_addr));
   ExpectErrorState(NetworkServiceAsyncSocket::STATE_CLOSED,
                    NetworkServiceAsyncSocket::ERROR_DNS);
diff --git a/jingle/glue/task_pump.cc b/jingle/glue/task_pump.cc
index cb82875..9ee5aee 100644
--- a/jingle/glue/task_pump.cc
+++ b/jingle/glue/task_pump.cc
@@ -31,13 +31,6 @@
   }
 }
 
-int64_t TaskPump::CurrentTime() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Only timeout tasks rely on this function.  Since we're not using
-  // libjingle tasks for timeout, it's safe to return 0 here.
-  return 0;
-}
-
 void TaskPump::Stop() {
   stopped_ = true;
 }
diff --git a/jingle/glue/task_pump.h b/jingle/glue/task_pump.h
index 64325d0..fb531b1 100644
--- a/jingle/glue/task_pump.h
+++ b/jingle/glue/task_pump.h
@@ -24,7 +24,6 @@
 
   // rtc::TaskRunner implementation.
   void WakeTasks() override;
-  int64_t CurrentTime() override;
 
   // No tasks will be processed after this is called, even if
   // WakeTasks() is called.
diff --git a/jingle/notifier/base/fake_base_task.cc b/jingle/notifier/base/fake_base_task.cc
index 7087ad0..57ec042 100644
--- a/jingle/notifier/base/fake_base_task.cc
+++ b/jingle/notifier/base/fake_base_task.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 
 #include "base/compiler_specific.h"
+#include "net/base/host_port_pair.h"
 #include "jingle/notifier/base/fake_base_task.h"
 #include "jingle/notifier/base/weak_xmpp_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -22,7 +23,7 @@
   MOCK_METHOD0(state, State());
   MOCK_METHOD0(error, Error());
   MOCK_METHOD0(GetError, int());
-  MOCK_METHOD1(Connect, bool(const rtc::SocketAddress&));
+  MOCK_METHOD1(Connect, bool(const net::HostPortPair&));
   MOCK_METHOD3(Read, bool(char*, size_t, size_t*));
   MOCK_METHOD2(Write, bool(const char*, size_t));
   MOCK_METHOD0(Close, bool());
diff --git a/jingle/notifier/communicator/connection_settings.cc b/jingle/notifier/communicator/connection_settings.cc
index f057de4f..e7ff880 100644
--- a/jingle/notifier/communicator/connection_settings.cc
+++ b/jingle/notifier/communicator/connection_settings.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "net/base/host_port_pair.h"
 
 #include "third_party/libjingle_xmpp/xmpp/xmppclientsettings.h"
 
@@ -14,10 +15,9 @@
 
 const uint16_t kSslTcpPort = 443;
 
-ConnectionSettings::ConnectionSettings(
-    const rtc::SocketAddress& server,
-    SslTcpMode ssltcp_mode,
-    SslTcpSupport ssltcp_support)
+ConnectionSettings::ConnectionSettings(const net::HostPortPair& server,
+                                       SslTcpMode ssltcp_mode,
+                                       SslTcpSupport ssltcp_support)
     : server(server),
       ssltcp_mode(ssltcp_mode),
       ssltcp_support(ssltcp_support) {}
@@ -71,13 +71,13 @@
   for (ServerList::const_iterator it = servers.begin();
        it != servers.end(); ++it) {
     const ConnectionSettings settings(
-        rtc::SocketAddress(it->server.host(), it->server.port()),
+        net::HostPortPair({it->server.host(), it->server.port()}),
         DO_NOT_USE_SSLTCP, it->ssltcp_support);
 
     if (it->ssltcp_support == SUPPORTS_SSLTCP) {
       const ConnectionSettings settings_with_ssltcp(
-        rtc::SocketAddress(it->server.host(), kSslTcpPort),
-        USE_SSLTCP, it->ssltcp_support);
+          net::HostPortPair({it->server.host(), kSslTcpPort}), USE_SSLTCP,
+          it->ssltcp_support);
 
       if (try_ssltcp_first) {
         settings_list.push_back(settings_with_ssltcp);
diff --git a/jingle/notifier/communicator/connection_settings.h b/jingle/notifier/communicator/connection_settings.h
index 3103a59..7ec14ed 100644
--- a/jingle/notifier/communicator/connection_settings.h
+++ b/jingle/notifier/communicator/connection_settings.h
@@ -10,8 +10,8 @@
 #include <string>
 #include <vector>
 
+#include "net/base/host_port_pair.h"
 #include "jingle/notifier/base/server_information.h"
-#include "third_party/webrtc/rtc_base/socket_address.h"
 
 namespace buzz {
 class XmppClientSettings;
@@ -26,7 +26,7 @@
 
 struct ConnectionSettings {
  public:
-  ConnectionSettings(const rtc::SocketAddress& server,
+  ConnectionSettings(const net::HostPortPair& server,
                      SslTcpMode ssltcp_mode,
                      SslTcpSupport ssltcp_support);
   ConnectionSettings();
@@ -39,7 +39,7 @@
   // Fill in the connection-related fields of |client_settings|.
   void FillXmppClientSettings(buzz::XmppClientSettings* client_settings) const;
 
-  rtc::SocketAddress server;
+  net::HostPortPair server;
   SslTcpMode ssltcp_mode;
   SslTcpSupport ssltcp_support;
 };
diff --git a/jingle/notifier/communicator/connection_settings_unittest.cc b/jingle/notifier/communicator/connection_settings_unittest.cc
index 622d5cf..a997cce 100644
--- a/jingle/notifier/communicator/connection_settings_unittest.cc
+++ b/jingle/notifier/communicator/connection_settings_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "net/base/host_port_pair.h"
 #include "jingle/notifier/base/server_information.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,20 +46,14 @@
 
   ConnectionSettingsList expected_settings_list;
   expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("supports_ssltcp.com", 100),
-          DO_NOT_USE_SSLTCP,
-          SUPPORTS_SSLTCP));
+      ConnectionSettings(net::HostPortPair({"supports_ssltcp.com", 100}),
+                         DO_NOT_USE_SSLTCP, SUPPORTS_SSLTCP));
   expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("supports_ssltcp.com", 443),
-          USE_SSLTCP,
-          SUPPORTS_SSLTCP));
-  expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("does_not_support_ssltcp.com", 200),
-          DO_NOT_USE_SSLTCP,
-          DOES_NOT_SUPPORT_SSLTCP));
+      ConnectionSettings(net::HostPortPair({"supports_ssltcp.com", 443}),
+                         USE_SSLTCP, SUPPORTS_SSLTCP));
+  expected_settings_list.push_back(ConnectionSettings(
+      net::HostPortPair({"does_not_support_ssltcp.com", 200}),
+      DO_NOT_USE_SSLTCP, DOES_NOT_SUPPORT_SSLTCP));
 
   ASSERT_EQ(expected_settings_list.size(), settings_list.size());
   for (size_t i = 0; i < settings_list.size(); ++i) {
@@ -75,20 +70,14 @@
 
   ConnectionSettingsList expected_settings_list;
   expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("supports_ssltcp.com", 443),
-          USE_SSLTCP,
-          SUPPORTS_SSLTCP));
+      ConnectionSettings(net::HostPortPair({"supports_ssltcp.com", 443}),
+                         USE_SSLTCP, SUPPORTS_SSLTCP));
   expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("supports_ssltcp.com", 100),
-          DO_NOT_USE_SSLTCP,
-          SUPPORTS_SSLTCP));
-  expected_settings_list.push_back(
-      ConnectionSettings(
-          rtc::SocketAddress("does_not_support_ssltcp.com", 200),
-          DO_NOT_USE_SSLTCP,
-          DOES_NOT_SUPPORT_SSLTCP));
+      ConnectionSettings(net::HostPortPair({"supports_ssltcp.com", 100}),
+                         DO_NOT_USE_SSLTCP, SUPPORTS_SSLTCP));
+  expected_settings_list.push_back(ConnectionSettings(
+      net::HostPortPair({"does_not_support_ssltcp.com", 200}),
+      DO_NOT_USE_SSLTCP, DOES_NOT_SUPPORT_SSLTCP));
 
   ASSERT_EQ(expected_settings_list.size(), settings_list.size());
   for (size_t i = 0; i < settings_list.size(); ++i) {
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 30c4bc1c..30a708b 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -254,16 +254,8 @@
     "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enable MojoVideoDecoder, replacing GpuVideoDecoder.
-const base::Feature kMojoVideoDecoder {
-  "MojoVideoDecoder",
-#if defined(OS_CHROMEOS)
-      // TODO(posciak): Re-enable once the feature is verified on CrOS.
-      // https://crbug.com/902968.
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_ENABLED_BY_DEFAULT
-#endif
-};
+const base::Feature kMojoVideoDecoder{"MojoVideoDecoder",
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable The D3D11 Video decoder. Must also enable MojoVideoDecoder for
 // this to have any effect.
@@ -299,16 +291,8 @@
     "NewRemotePlaybackPipeline", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Use the new RTC hardware decode path via RTCVideoDecoderAdapter.
-const base::Feature kRTCVideoDecoderAdapter {
-  "RTCVideoDecoderAdapter",
-#if defined(OS_CHROMEOS)
-      // TODO(posciak): Enable once the feature is verified on CrOS.
-      // https://crbug.com/902968.
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_ENABLED_BY_DEFAULT
-#endif
-};
+const base::Feature kRTCVideoDecoderAdapter{"RTCVideoDecoderAdapter",
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
 // CanPlayThrough issued according to standard.
 const base::Feature kSpecCompliantCanPlayThrough{
diff --git a/media/base/video_frame_layout.cc b/media/base/video_frame_layout.cc
index 1e5f83e9..10a84d7 100644
--- a/media/base/video_frame_layout.cc
+++ b/media/base/video_frame_layout.cc
@@ -160,7 +160,8 @@
 
 bool VideoFrameLayout::operator==(const VideoFrameLayout& rhs) const {
   return format_ == rhs.format_ && coded_size_ == rhs.coded_size_ &&
-         planes_ == rhs.planes_ && buffer_sizes_ == rhs.buffer_sizes_;
+         planes_ == rhs.planes_ && buffer_sizes_ == rhs.buffer_sizes_ &&
+         buffer_addr_align_ == rhs.buffer_addr_align_;
 }
 
 bool VideoFrameLayout::operator!=(const VideoFrameLayout& rhs) const {
diff --git a/media/base/video_frame_layout_unittest.cc b/media/base/video_frame_layout_unittest.cc
index d675e938..3c9309a 100644
--- a/media/base/video_frame_layout_unittest.cc
+++ b/media/base/video_frame_layout_unittest.cc
@@ -286,21 +286,30 @@
   std::vector<int32_t> strides = {384, 192, 192};
   std::vector<size_t> offsets = {0, 100, 200};
   std::vector<size_t> buffer_sizes = {73728, 18432, 18432};
+  const size_t align = VideoFrameLayout::kBufferAddressAlignment;
+
   auto layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      buffer_sizes);
+      buffer_sizes, align);
   ASSERT_TRUE(layout.has_value());
 
   auto same_layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      buffer_sizes);
+      buffer_sizes, align);
   ASSERT_TRUE(same_layout.has_value());
   EXPECT_EQ(*layout, *same_layout);
 
   std::vector<size_t> another_buffer_sizes = {73728};
   auto different_layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      another_buffer_sizes);
+      another_buffer_sizes, align);
+  ASSERT_TRUE(different_layout.has_value());
+  EXPECT_NE(*layout, *different_layout);
+
+  const size_t another_align = 0x1000;
+  different_layout = VideoFrameLayout::CreateWithPlanes(
+      PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
+      buffer_sizes, another_align);
   ASSERT_TRUE(different_layout.has_value());
   EXPECT_NE(*layout, *different_layout);
 }
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 72c1910..5ca0364 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -354,7 +354,6 @@
   WebMediaPlayerImplTest()
       : media_thread_("MediaThreadForTest"),
         web_view_(blink::WebView::Create(/*client=*/nullptr,
-                                         /*widget_client=*/nullptr,
                                          /*is_hidden=*/false,
                                          /*compositing_enabled=*/false,
                                          nullptr)),
diff --git a/media/capabilities/in_memory_video_decode_stats_db_impl.h b/media/capabilities/in_memory_video_decode_stats_db_impl.h
index 746b78a4..6cf0525 100644
--- a/media/capabilities/in_memory_video_decode_stats_db_impl.h
+++ b/media/capabilities/in_memory_video_decode_stats_db_impl.h
@@ -10,7 +10,7 @@
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "media/base/media_export.h"
 #include "media/base/video_codecs.h"
 #include "media/capabilities/video_decode_stats_db.h"
diff --git a/media/capabilities/video_decode_stats_db_impl.cc b/media/capabilities/video_decode_stats_db_impl.cc
index e5640e3..6739870 100644
--- a/media/capabilities/video_decode_stats_db_impl.cc
+++ b/media/capabilities/video_decode_stats_db_impl.cc
@@ -15,7 +15,7 @@
 #include "base/sequence_checker.h"
 #include "base/task/post_task.h"
 #include "base/time/default_clock.h"
-#include "components/leveldb_proto/proto_database_impl.h"
+#include "components/leveldb_proto/public/proto_database_provider.h"
 #include "media/base/media_switches.h"
 #include "media/capabilities/video_decode_stats.pb.h"
 
@@ -61,7 +61,7 @@
   DVLOG(2) << __func__ << " db_dir:" << db_dir;
 
   auto proto_db =
-      std::make_unique<leveldb_proto::ProtoDatabaseImpl<DecodeStatsProto>>(
+      leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<DecodeStatsProto>(
           base::CreateSequencedTaskRunnerWithTraits(
               {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
diff --git a/media/capabilities/video_decode_stats_db_impl.h b/media/capabilities/video_decode_stats_db_impl.h
index bec7fec..1dd9740 100644
--- a/media/capabilities/video_decode_stats_db_impl.h
+++ b/media/capabilities/video_decode_stats_db_impl.h
@@ -9,7 +9,7 @@
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/public/proto_database.h"
 #include "media/base/media_export.h"
 #include "media/base/video_codecs.h"
 #include "media/capabilities/video_decode_stats_db.h"
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index 6b9e7ac6..d558a12d 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -886,6 +886,12 @@
     private static final long kNanosecondsPer100Microsecond = 100000;
     private static final String TAG = "VideoCapture";
 
+    private static final String[] AE_TARGET_FPS_RANGE_BUGGY_DEVICE_LIST = {
+            // See https://crbug.com/913203 for more info.
+            "Pixel 3",
+            "Pixel 3 XL",
+    };
+
     // Map of the equivalent color temperature in Kelvin for the White Balance setting. The
     // values are a mixture of educated guesses and data from Android's Camera2 API. The
     // temperatures must be ordered increasingly.
@@ -1085,7 +1091,9 @@
         } else {
             requestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
             requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
-            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mAeFpsRange);
+            if (!shouldSkipSettingAeTargetFpsRange()) {
+                requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mAeFpsRange);
+            }
         }
 
         if (mTorch) {
@@ -1165,6 +1173,15 @@
         }
     }
 
+    private static boolean shouldSkipSettingAeTargetFpsRange() {
+        for (String buggyDevice : AE_TARGET_FPS_RANGE_BUGGY_DEVICE_LIST) {
+            if (buggyDevice.contentEquals(android.os.Build.MODEL)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // Finds the closest Size to (|width|x|height|) in |sizes|, and returns it or null.
     // Ignores |width| or |height| if either is zero (== don't care).
     private static Size findClosestSizeInArray(Size[] sizes, int width, int height) {
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc
index 5fd8aaa1..f67ce80 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.cc
+++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -728,12 +728,13 @@
   CaptureResult& pending_result = pending_results_[frame_number];
   if (!stream_context_[stream_type]->capture_results_with_buffer.count(
           frame_number) ||
-      pending_result.partial_metadata_received.size() < partial_result_count_ ||
+      *pending_result.partial_metadata_received.rbegin() <
+          partial_result_count_ ||
       pending_result.reference_time == base::TimeTicks()) {
     // We can only submit the result buffer of |frame_number| for |stream_type|
     // when:
     //   1. The result buffer for |stream_type| is received, and
-    //   2. All the result metadata are received, and
+    //   2. Received partial result id equals to partial result count, and
     //   3. The shutter time is received.
     return;
   }
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc
index 6788e97..678c291c 100644
--- a/media/capture/video/fake_video_capture_device.cc
+++ b/media/capture/video/fake_video_capture_device.cc
@@ -22,6 +22,7 @@
 #include "media/capture/mojom/image_capture_types.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "ui/gfx/codec/jpeg_codec.h"
@@ -326,6 +327,8 @@
   bitmap.setPixels(target_buffer);
   SkPaint paint;
   paint.setStyle(SkPaint::kFill_Style);
+  SkFont font;
+  font.setEdging(SkFont::Edging::kAlias);
   SkCanvas canvas(bitmap);
 
   const SkScalar unscaled_zoom = fake_device_state_->zoom / 100.f;
@@ -362,7 +365,8 @@
       base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes, seconds,
                          milliseconds, frame_count);
   canvas.scale(3, 3);
-  canvas.drawText(time_string.data(), time_string.length(), 30, 20, paint);
+  canvas.drawSimpleText(time_string.data(), time_string.length(),
+                        kUTF8_SkTextEncoding, 30, 20, font, paint);
 
   if (pixel_format_ == Format::Y16) {
     // Use 8 bit bitmap rendered to first half of the buffer as high byte values
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 9c124e2..64c02a9 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -438,7 +438,7 @@
   // Convert the packet if there is a bitstream filter.
   if (bitstream_converter_ &&
       !bitstream_converter_->ConvertPacket(packet.get())) {
-    MEDIA_LOG(ERROR, media_log_) << "Format conversion failed.";
+    DVLOG(1) << "Format conversion failed.";
   }
 #endif
 
diff --git a/media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.cc b/media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.cc
index a8d56d2..64f8c53 100644
--- a/media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.cc
+++ b/media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.cc
@@ -25,20 +25,25 @@
 bool FFmpegH264ToAnnexBBitstreamConverter::ConvertPacket(AVPacket* packet) {
   std::unique_ptr<mp4::AVCDecoderConfigurationRecord> avc_config;
 
-  if (packet == NULL || !packet->data)
+  if (packet == NULL || !packet->data) {
+    DVLOG(2) << __func__ << ": Null or empty packet";
     return false;
+  }
 
   // Calculate the needed output buffer size.
   if (!configuration_processed_) {
     if (!stream_codec_parameters_->extradata ||
-        stream_codec_parameters_->extradata_size <= 0)
+        stream_codec_parameters_->extradata_size <= 0) {
+      DVLOG(2) << __func__ << ": Empty extra data";
       return false;
+    }
 
     avc_config.reset(new mp4::AVCDecoderConfigurationRecord());
 
     if (!converter_.ParseConfiguration(stream_codec_parameters_->extradata,
                                        stream_codec_parameters_->extradata_size,
                                        avc_config.get())) {
+      DVLOG(2) << __func__ << ": ParseConfiguration() failure";
       return false;
     }
   }
@@ -46,13 +51,17 @@
   uint32_t output_packet_size = converter_.CalculateNeededOutputBufferSize(
       packet->data, packet->size, avc_config.get());
 
-  if (output_packet_size == 0)
+  if (output_packet_size == 0) {
+    DVLOG(2) << __func__ << ": zero |output_packet_size|";
     return false;  // Invalid input packet.
+  }
 
   // Allocate new packet for the output.
   AVPacket dest_packet;
-  if (av_new_packet(&dest_packet, output_packet_size) != 0)
-    return false;  // Memory allocation failure.
+  if (av_new_packet(&dest_packet, output_packet_size) != 0) {
+    DVLOG(2) << __func__ << ": Memory allocation failure";
+    return false;
+  }
 
   // This is a bit tricky: since the interface does not allow us to replace
   // the pointer of the old packet with a new one, we will initially copy the
@@ -66,6 +75,7 @@
           packet->data, packet->size,
           avc_config.get(),
           dest_packet.data, &io_size)) {
+    DVLOG(2) << __func__ << ": ConvertNalUnitStreamToByteStream() failure";
     return false;
   }
 
diff --git a/media/formats/webm/webm_content_encodings_client.cc b/media/formats/webm/webm_content_encodings_client.cc
index ad992a26..6e58adf 100644
--- a/media/formats/webm/webm_content_encodings_client.cc
+++ b/media/formats/webm/webm_content_encodings_client.cc
@@ -249,7 +249,6 @@
                                           int size) {
   DCHECK(cur_content_encoding_.get());
   DCHECK(data);
-  DCHECK_GT(size, 0);
 
   if (id != kWebMIdContentEncKeyID) {
     MEDIA_LOG(ERROR, media_log_) << "Unsupported element " << id;
@@ -261,6 +260,11 @@
     return false;
   }
 
+  if (size <= 0) {
+    MEDIA_LOG(ERROR, media_log_) << "Invalid ContentEncKeyID size: " << size;
+    return false;
+  }
+
   cur_content_encoding_->SetEncryptionKeyId(data, size);
   return true;
 }
diff --git a/media/gpu/OWNERS b/media/gpu/OWNERS
index a81c64a..1f006b6 100644
--- a/media/gpu/OWNERS
+++ b/media/gpu/OWNERS
@@ -1,6 +1,7 @@
 dalecurtis@chromium.org
 dcastagna@chromium.org
 hiroh@chromium.org
+acourbot@chromium.org
 jcliang@chromium.org
 kcwu@chromium.org
 posciak@chromium.org
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index ce918a1..f0f94af 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -133,7 +133,6 @@
     ]
     deps = [
       ":decode_helpers",
-      ":frame_validator",
       "//media/gpu",
     ]
     if (use_ozone) {
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index 91535035..6aa6930 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -22,6 +22,19 @@
 
 namespace test {
 
+// The video frame processor defines an abstract interface for classes that are
+// interested in processing video frames (e.g. FrameValidator,...).
+class VideoFrameProcessor {
+ public:
+  virtual ~VideoFrameProcessor() = default;
+
+  // Process the specified |video_frame|. This can e.g. validate the frame,
+  // calculate the frame's checksum, write the frame to file,... The
+  // |frame_index| is the index of the video frame in display order.
+  virtual void ProcessVideoFrame(scoped_refptr<VideoFrame> video_frame,
+                                 size_t frame_index) = 0;
+};
+
 // Create a video frame with specified |pixel_format| and |size|.
 scoped_refptr<VideoFrame> CreateVideoFrame(
     VideoPixelFormat pixel_format,
diff --git a/media/gpu/test/video_frame_mapper_factory.h b/media/gpu/test/video_frame_mapper_factory.h
index 36d5ecaa..8dd0d4e 100644
--- a/media/gpu/test/video_frame_mapper_factory.h
+++ b/media/gpu/test/video_frame_mapper_factory.h
@@ -28,4 +28,4 @@
 }  // namespace test
 }  // namespace media
 
-#endif  // MEDIA_GPU_TEST_GENERIC_VIDEO_FRAME_MAPPER_FACTORY_H_
+#endif  // MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_FACTORY_H_
diff --git a/media/gpu/test/video_frame_validator.cc b/media/gpu/test/video_frame_validator.cc
index bec41095c..10e3c2eb 100644
--- a/media/gpu/test/video_frame_validator.cc
+++ b/media/gpu/test/video_frame_validator.cc
@@ -21,7 +21,8 @@
 namespace test {
 
 // static
-std::unique_ptr<VideoFrameValidator> VideoFrameValidator::Create() {
+std::unique_ptr<VideoFrameValidator> VideoFrameValidator::Create(
+    const std::vector<std::string>& frame_checksums) {
   auto video_frame_mapper = VideoFrameMapperFactory::CreateMapper();
   if (!video_frame_mapper) {
     LOG(ERROR) << "Failed to create VideoFrameMapper.";
@@ -29,7 +30,7 @@
   }
 
   return base::WrapUnique(new VideoFrameValidator(
-      VideoFrameValidator::CHECK, base::FilePath(), std::vector<std::string>(),
+      VideoFrameValidator::CHECK, base::FilePath(), frame_checksums,
       base::File(), std::move(video_frame_mapper)));
 }
 
@@ -95,7 +96,18 @@
 
 VideoFrameValidator::~VideoFrameValidator() {}
 
-void VideoFrameValidator::EvaluateVideoFrame(
+std::vector<VideoFrameValidator::MismatchedFrameInfo>
+VideoFrameValidator::GetMismatchedFramesInfo() const {
+  base::AutoLock auto_lock(mismatched_frames_lock_);
+  return mismatched_frames_;
+}
+
+size_t VideoFrameValidator::GetMismatchedFramesCount() const {
+  base::AutoLock auto_lock(mismatched_frames_lock_);
+  return mismatched_frames_.size();
+}
+
+void VideoFrameValidator::ProcessVideoFrame(
     scoped_refptr<VideoFrame> video_frame,
     size_t frame_index) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -127,28 +139,6 @@
   }
 }
 
-std::vector<VideoFrameValidator::MismatchedFrameInfo>
-VideoFrameValidator::GetMismatchedFramesInfo() const {
-  base::AutoLock auto_lock(mismatched_frames_lock_);
-  return mismatched_frames_;
-}
-
-size_t VideoFrameValidator::GetMismatchedFramesCount() const {
-  base::AutoLock auto_lock(mismatched_frames_lock_);
-  return mismatched_frames_.size();
-}
-
-void VideoFrameValidator::SetFrameChecksums(
-    const std::vector<std::string>& frame_checksums) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  md5_of_frames_ = frame_checksums;
-  // TODO(dstaessens@) Setting a different stream happens at the start of a
-  // test, and we don't want test results to influence each-other. This should
-  // be removed when 'SetFrameChecksums' is moved to the constructor.
-  base::AutoLock auto_lock(mismatched_frames_lock_);
-  mismatched_frames_.clear();
-}
-
 scoped_refptr<VideoFrame> VideoFrameValidator::CreateStandardizedFrame(
     scoped_refptr<VideoFrame> video_frame) const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/media/gpu/test/video_frame_validator.h b/media/gpu/test/video_frame_validator.h
index abd398eb..3b6e08d 100644
--- a/media/gpu/test/video_frame_validator.h
+++ b/media/gpu/test/video_frame_validator.h
@@ -16,6 +16,7 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "media/base/video_types.h"
+#include "media/gpu/test/video_frame_helpers.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace media {
@@ -35,7 +36,7 @@
 // identical on all platforms.
 // Mapping and verification of a frame is a costly operation and will influence
 // performance measurements.
-class VideoFrameValidator {
+class VideoFrameValidator : public VideoFrameProcessor {
  public:
   enum Flags : uint32_t {
     // Checks soundness of video frames.
@@ -52,9 +53,11 @@
     std::string expected_md5;
   };
 
-  // Creates an instance of the video frame validator in 'CHECK' mode. The md5
-  // frame checksums need to be provided by calling SetFrameChecksums().
-  static std::unique_ptr<VideoFrameValidator> Create();
+  // Creates an instance of the video frame validator in 'CHECK' mode.
+  // |frame_checksums| should contain the ordered list of md5 frame checksums to
+  // be used by the validator
+  static std::unique_ptr<VideoFrameValidator> Create(
+      const std::vector<std::string>& frame_checksums);
 
   // |flags| decides the behavior of created video frame validator. See the
   // detail in Flags.
@@ -72,14 +75,7 @@
       const base::FilePath& md5_file_path,
       bool linear);
 
-  ~VideoFrameValidator();
-
-  // This checks if |video_frame|'s pixel content is as expected.
-  // A client of VideoFrameValidator would call this function on each frame in
-  // PictureReady().
-  // |frame_index| is the index of video frame in display order.
-  void EvaluateVideoFrame(scoped_refptr<VideoFrame> video_frame,
-                          size_t frame_index);
+  ~VideoFrameValidator() override;
 
   // Returns information of frames that don't match golden md5 values.
   // If there is no mismatched frame, returns an empty vector. This function is
@@ -90,10 +86,10 @@
   // function is thread-safe.
   size_t GetMismatchedFramesCount() const;
 
-  // Set the ordered list of md5 frame checksums to be used by the validator.
-  // TODO(dstaessens) Remove this function and provide checksums on
-  // construction of VideoFrameValidator.
-  void SetFrameChecksums(const std::vector<std::string>& frame_checksums);
+  // Interface VideoFrameProcessor
+  // Validate the |video_frame|'s pixel content.
+  void ProcessVideoFrame(scoped_refptr<VideoFrame> video_frame,
+                         size_t frame_index) override;
 
  private:
   VideoFrameValidator(uint32_t flags,
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 42e37b9..628eda9 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -14,7 +14,6 @@
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
 #include "media/gpu/test/video_frame_helpers.h"
-#include "media/gpu/test/video_frame_validator.h"
 #include "media/gpu/test/video_player/frame_renderer.h"
 
 #define DVLOGF(level) DVLOG(level) << __func__ << "(): "
@@ -26,10 +25,10 @@
 VideoDecoderClient::VideoDecoderClient(
     const VideoPlayer::EventCallback& event_cb,
     FrameRenderer* renderer,
-    VideoFrameValidator* frame_validator)
+    const std::vector<VideoFrameProcessor*>& frame_processors)
     : event_cb_(event_cb),
       frame_renderer_(renderer),
-      frame_validator_(frame_validator),
+      frame_processors_(frame_processors),
       decoder_client_thread_("VDAClientDecoderThread"),
       decoder_client_state_(VideoDecoderClientState::kUninitialized),
       weak_this_factory_(this) {
@@ -46,9 +45,9 @@
 std::unique_ptr<VideoDecoderClient> VideoDecoderClient::Create(
     const VideoPlayer::EventCallback& event_cb,
     FrameRenderer* frame_renderer,
-    VideoFrameValidator* frame_validator) {
+    const std::vector<VideoFrameProcessor*>& frame_processors) {
   auto decoder_client = base::WrapUnique(
-      new VideoDecoderClient(event_cb, frame_renderer, frame_validator));
+      new VideoDecoderClient(event_cb, frame_renderer, frame_processors));
   if (!decoder_client->Initialize()) {
     return nullptr;
   }
@@ -84,15 +83,13 @@
 
 void VideoDecoderClient::CreateDecoder(
     const VideoDecodeAccelerator::Config& config,
-    const std::vector<uint8_t>& stream,
-    const std::vector<std::string>& frame_checksums) {
+    const std::vector<uint8_t>& stream) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
 
   base::WaitableEvent done;
   decoder_client_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VideoDecoderClient::CreateDecoderTask, weak_this_, config,
-                     &stream, &frame_checksums, &done));
+      FROM_HERE, base::BindOnce(&VideoDecoderClient::CreateDecoderTask,
+                                weak_this_, config, &stream, &done));
   done.Wait();
 }
 
@@ -190,7 +187,9 @@
 
   frame_renderer_->RenderFrame(wrapped_video_frame);
 
-  frame_validator_->EvaluateVideoFrame(video_frame, current_frame_index_++);
+  for (VideoFrameProcessor* frame_processor : frame_processors_)
+    frame_processor->ProcessVideoFrame(video_frame, current_frame_index_);
+  current_frame_index_++;
 }
 
 void VideoDecoderClient::NotifyEndOfBitstreamBuffer(
@@ -278,7 +277,6 @@
 void VideoDecoderClient::CreateDecoderTask(
     VideoDecodeAccelerator::Config config,
     const std::vector<uint8_t>* stream,
-    const std::vector<std::string>* frame_checksums,
     base::WaitableEvent* done) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_client_sequence_checker_);
   LOG_ASSERT(decoder_factory_) << "Decoder factory not created yet";
@@ -287,7 +285,6 @@
 
   encoded_data_helper_ =
       std::make_unique<EncodedDataHelper>(*stream, config.profile);
-  frame_validator_->SetFrameChecksums(*frame_checksums);
 
   gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds;
   gpu::GpuPreferences gpu_preferences;
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index b9452ca..6ec7d8e 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 #include <map>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -26,7 +25,7 @@
 
 class EncodedDataHelper;
 class FrameRenderer;
-class VideoFrameValidator;
+class VideoFrameProcessor;
 
 // The video decoder client is responsible for the communication between the
 // video player and the video decoder. It also communicates with the frame
@@ -43,21 +42,20 @@
   ~VideoDecoderClient() override;
 
   // Return an instance of the VideoDecoderClient. The |frame_renderer| and
-  // |frame_validator| will not be owned by the decoder client, the caller
+  // |frame_processors| will not be owned by the decoder client, the caller
   // should guarantee they outlive the decoder client. The |event_cb| will be
   // called whenever an event occurs (e.g. frame decoded) and should be
   // thread-safe.
   static std::unique_ptr<VideoDecoderClient> Create(
       const VideoPlayer::EventCallback& event_cb,
       FrameRenderer* frame_renderer,
-      VideoFrameValidator* frame_validator);
+      const std::vector<VideoFrameProcessor*>& frame_processors);
 
-  // Create a decoder with specified |config|, video |stream| and video
-  // |stream_size|. The video stream will not be owned by the decoder client,
-  // the caller should guarantee it exists until DestroyDecoder() is called.
+  // Create a decoder with specified |config| and video |stream|. The video
+  // will not be owned by the decoder client, the caller should guarantee it
+  // exists until DestroyDecoder() is called.
   void CreateDecoder(const VideoDecodeAccelerator::Config& config,
-                     const std::vector<uint8_t>& stream,
-                     const std::vector<std::string>& frame_checksums);
+                     const std::vector<uint8_t>& stream);
   // Destroy the currently active decoder.
   void DestroyDecoder();
 
@@ -83,7 +81,7 @@
 
   VideoDecoderClient(const VideoPlayer::EventCallback& event_cb,
                      FrameRenderer* renderer,
-                     VideoFrameValidator* frame_validator);
+                     const std::vector<VideoFrameProcessor*>& frame_processors);
 
   bool Initialize();
   void Destroy();
@@ -104,7 +102,6 @@
   void CreateDecoderFactoryTask(base::WaitableEvent* done);
   void CreateDecoderTask(VideoDecodeAccelerator::Config config,
                          const std::vector<uint8_t>* stream,
-                         const std::vector<std::string>* frame_checksums,
                          base::WaitableEvent* done);
   void DestroyDecoderTask(base::WaitableEvent* done);
 
@@ -127,7 +124,7 @@
 
   VideoPlayer::EventCallback event_cb_;
   FrameRenderer* const frame_renderer_;
-  VideoFrameValidator* const frame_validator_;
+  std::vector<VideoFrameProcessor*> const frame_processors_;
 
   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> decoder_factory_;
   std::unique_ptr<VideoDecodeAccelerator> decoder_;
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index 431e4ec..bfbd514 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -30,27 +30,39 @@
 
 // static
 std::unique_ptr<VideoPlayer> VideoPlayer::Create(
+    const Video* video,
     FrameRenderer* frame_renderer,
-    VideoFrameValidator* frame_validator) {
+    const std::vector<VideoFrameProcessor*>& frame_processors) {
   auto video_player = base::WrapUnique(new VideoPlayer());
-  video_player->Initialize(frame_renderer, frame_validator);
+  video_player->Initialize(video, frame_renderer, frame_processors);
   return video_player;
 }
 
-void VideoPlayer::Initialize(FrameRenderer* frame_renderer,
-                             VideoFrameValidator* frame_validator) {
+void VideoPlayer::Initialize(
+    const Video* video,
+    FrameRenderer* frame_renderer,
+    const std::vector<VideoFrameProcessor*>& frame_processors) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(video_player_state_, VideoPlayerState::kUninitialized);
-  DCHECK(frame_renderer);
+  DCHECK(frame_renderer && video);
   DVLOGF(4);
 
   EventCallback event_cb =
       base::BindRepeating(&VideoPlayer::NotifyEvent, base::Unretained(this));
 
   decoder_client_ =
-      VideoDecoderClient::Create(event_cb, frame_renderer, frame_validator);
+      VideoDecoderClient::Create(event_cb, frame_renderer, frame_processors);
   CHECK(decoder_client_) << "Failed to create decoder client";
 
+  // Create a decoder for the specified video. We'll always use import mode as
+  // this is the only mode supported by the media::VideoDecoder interface, which
+  // the video decoders are being migrated to.
+  VideoDecodeAccelerator::Config decoder_config(video->Profile());
+  decoder_config.output_mode =
+      VideoDecodeAccelerator::Config::OutputMode::IMPORT;
+  decoder_client_->CreateDecoder(decoder_config, video->Data());
+
+  video_ = video;
   video_player_state_ = VideoPlayerState::kIdle;
 }
 
@@ -63,28 +75,6 @@
   video_player_state_ = VideoPlayerState::kDestroyed;
 }
 
-void VideoPlayer::SetStream(const Video* const video) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(video);
-  DVLOGF(4);
-
-  // Destroy the currently active decoder.
-  if (video_) {
-    decoder_client_->DestroyDecoder();
-  }
-
-  // Create a decoder for the specified video. We'll always use import mode as
-  // this is the only mode supported by the media::VideoDecoder interface, which
-  // the video decoders are being migrated to.
-  VideoDecodeAccelerator::Config decoder_config(video->Profile());
-  decoder_config.output_mode =
-      VideoDecodeAccelerator::Config::OutputMode::IMPORT;
-  decoder_client_->CreateDecoder(decoder_config, video->Data(),
-                                 video->FrameChecksums());
-
-  video_ = video;
-}
-
 void VideoPlayer::Play() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(video_player_state_, VideoPlayerState::kIdle);
@@ -131,13 +121,6 @@
   return video_player_state_;
 }
 
-size_t VideoPlayer::GetEventCount(VideoPlayerEvent event) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  base::AutoLock auto_lock(event_lock_);
-  return video_player_event_counts_[static_cast<size_t>(event)];
-}
-
 bool VideoPlayer::WaitForEvent(VideoPlayerEvent event,
                                size_t times,
                                base::TimeDelta max_wait) {
@@ -171,6 +154,37 @@
   }
 }
 
+bool VideoPlayer::WaitForFlushDone() {
+  return WaitForEvent(VideoPlayerEvent::kFlushDone);
+}
+
+bool VideoPlayer::WaitForResetDone() {
+  return WaitForEvent(VideoPlayerEvent::kResetDone);
+}
+
+bool VideoPlayer::WaitForFrameDecoded(size_t times) {
+  return WaitForEvent(VideoPlayerEvent::kFrameDecoded, times);
+}
+
+size_t VideoPlayer::GetEventCount(VideoPlayerEvent event) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::AutoLock auto_lock(event_lock_);
+  return video_player_event_counts_[static_cast<size_t>(event)];
+}
+
+size_t VideoPlayer::GetFlushDoneCount() const {
+  return GetEventCount(VideoPlayerEvent::kFlushDone);
+}
+
+size_t VideoPlayer::GetResetDoneCount() const {
+  return GetEventCount(VideoPlayerEvent::kResetDone);
+}
+
+size_t VideoPlayer::GetFrameDecodedCount() const {
+  return GetEventCount(VideoPlayerEvent::kFrameDecoded);
+}
+
 void VideoPlayer::NotifyEvent(VideoPlayerEvent event) {
   base::AutoLock auto_lock(event_lock_);
   if (event == VideoPlayerEvent::kFlushDone ||
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index 58040e8..c6cc240 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -21,7 +21,7 @@
 class FrameRenderer;
 class Video;
 class VideoDecoderClient;
-class VideoFrameValidator;
+class VideoFrameProcessor;
 
 // Default timeout used when waiting for events.
 constexpr base::TimeDelta kDefaultTimeout = base::TimeDelta::FromSeconds(10);
@@ -51,16 +51,13 @@
 
   ~VideoPlayer();
 
-  // Create an instance of the video player. The |frame_renderer| and
-  // |frame_validator| will not be owned by the video player. The caller should
-  // guarantee they exists for the entire lifetime of the video player.
+  // Create an instance of the video player. The |video|, |frame_renderer| and
+  // |frame_processors| will not be owned by the video player. The caller should
+  // guarantee they outlive the video player.
   static std::unique_ptr<VideoPlayer> Create(
+      const Video* video,
       FrameRenderer* frame_renderer,
-      VideoFrameValidator* frame_validator);
-
-  // Set the video stream to be played. The |video| will not be owned by the
-  // video player. A decoder will be set up for the specified video stream.
-  void SetStream(const Video* video);
+      const std::vector<VideoFrameProcessor*>& frame_processors);
 
   void Play();
   void Stop();
@@ -81,15 +78,28 @@
   bool WaitForEvent(VideoPlayerEvent event,
                     size_t times = 1,
                     base::TimeDelta max_wait = kDefaultTimeout);
+  // Helper function to wait for a FlushDone event.
+  bool WaitForFlushDone();
+  // Helper function to wait for a ResetDone event.
+  bool WaitForResetDone();
+  // Helper function to wait for the specified number of FrameDecoded events.
+  bool WaitForFrameDecoded(size_t times);
 
   // Get the number of times the specified event occurred.
   size_t GetEventCount(VideoPlayerEvent event) const;
+  // Helper function to get the number of ResetDone events thrown.
+  size_t GetResetDoneCount() const;
+  // Helper function to get the number of FlushDone events thrown.
+  size_t GetFlushDoneCount() const;
+  // Helper function to get the number of FrameDecoded events thrown.
+  size_t GetFrameDecodedCount() const;
 
  private:
   VideoPlayer();
 
-  void Initialize(FrameRenderer* frame_renderer,
-                  VideoFrameValidator* frame_validator);
+  void Initialize(const Video* video,
+                  FrameRenderer* frame_renderer,
+                  const std::vector<VideoFrameProcessor*>& frame_processors);
   void Destroy();
 
   // Notify the client an event has occurred (e.g. frame decoded).
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 66f6ee76..faa1af6 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -40,6 +40,7 @@
   ~V4L2Buffer();
 
   void* GetPlaneMapping(const size_t plane);
+  size_t GetMemoryUsage() const;
   const struct v4l2_buffer* v4l2_buffer() const { return &v4l2_buffer_; }
 
  private:
@@ -145,6 +146,14 @@
   return p;
 }
 
+size_t V4L2Buffer::GetMemoryUsage() const {
+  size_t usage = 0;
+  for (size_t i = 0; i < v4l2_buffer_.length; i++) {
+    usage += v4l2_buffer_.m.planes[i].length;
+  }
+  return usage;
+}
+
 // Module-private class that let users query/write V4L2 buffer information.
 // It also makes some private V4L2Queue methods available to this module only.
 class V4L2BufferQueueProxy {
@@ -622,6 +631,19 @@
   return true;
 }
 
+size_t V4L2Queue::GetMemoryUsage() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  size_t usage = 0;
+  for (const auto& buf : buffers_) {
+    usage += buf->GetMemoryUsage();
+  }
+  return usage;
+}
+
+v4l2_memory V4L2Queue::GetMemoryType() const {
+  return memory_;
+}
+
 V4L2WritableBufferRef V4L2Queue::GetFreeBuffer() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto iter = free_buffers_.begin();
@@ -1177,6 +1199,22 @@
 }
 
 // static
+std::string V4L2Device::V4L2MemoryToString(const v4l2_memory memory) {
+  switch (memory) {
+    case V4L2_MEMORY_MMAP:
+      return "V4L2_MEMORY_MMAP";
+    case V4L2_MEMORY_USERPTR:
+      return "V4L2_MEMORY_USERPTR";
+    case V4L2_MEMORY_DMABUF:
+      return "V4L2_MEMORY_DMABUF";
+    case V4L2_MEMORY_OVERLAY:
+      return "V4L2_MEMORY_OVERLAY";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+// static
 std::string V4L2Device::V4L2FormatToString(const struct v4l2_format& format) {
   std::ostringstream s;
   s << "v4l2_format type: " << format.type;
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 0c4b344..3e7b27b7 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -204,6 +204,13 @@
   // released, or this call will fail.
   bool DeallocateBuffers();
 
+  // Returns the memory usage of v4l2 buffers owned by this V4L2Queue which are
+  // mapped in user space memory.
+  size_t GetMemoryUsage() const;
+
+  // Returns |memory_|, memory type of last buffers allocated by this V4L2Queue.
+  v4l2_memory GetMemoryType() const;
+
   // Return a unique pointer to a free buffer for the caller to prepare and
   // submit, or an empty pointer if no buffer is currently free.
   //
@@ -307,6 +314,9 @@
   static int32_t VideoCodecProfileToV4L2H264Profile(VideoCodecProfile profile);
   static int32_t H264LevelIdcToV4L2H264Level(uint8_t level_idc);
 
+  // Converts v4l2_memory to a string.
+  static std::string V4L2MemoryToString(const v4l2_memory memory);
+
   // Composes human readable string of v4l2_format.
   static std::string V4L2FormatToString(const struct v4l2_format& format);
 
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 34db6b48..1becd2b 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -20,8 +20,10 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "media/base/media_switches.h"
@@ -91,6 +93,7 @@
       scoped_refptr<DecoderBuffer> buffer,
       int32_t input_id);
   ~BitstreamBufferRef();
+
   const base::WeakPtr<Client> client;
   const scoped_refptr<base::SingleThreadTaskRunner> client_task_runner;
   scoped_refptr<DecoderBuffer> buffer;
@@ -276,6 +279,9 @@
 
   decoder_state_ = kInitialized;
 
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "media::V4l2VideoDecodeAccelerator", decoder_thread_.task_runner());
+
   // InitializeTask will NOTIFY_ERROR on failure.
   decoder_thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&V4L2VideoDecodeAccelerator::InitializeTask,
@@ -806,7 +812,7 @@
     return;
   }
 
-  decoder_input_queue_.push(std::move(bitstream_record));
+  decoder_input_queue_.push_back(std::move(bitstream_record));
   decoder_decode_buffer_tasks_scheduled_++;
   DecodeBufferTask();
 }
@@ -840,7 +846,7 @@
 
     // Setup to use the next buffer.
     decoder_current_bitstream_buffer_ = std::move(decoder_input_queue_.front());
-    decoder_input_queue_.pop();
+    decoder_input_queue_.pop_front();
     const auto& buffer = decoder_current_bitstream_buffer_->buffer;
     if (buffer) {
       DVLOGF(4) << "reading input_id="
@@ -1625,7 +1631,7 @@
   DCHECK(!decoder_flushing_);
 
   // Queue up an empty buffer -- this triggers the flush.
-  decoder_input_queue_.push(std::make_unique<BitstreamBufferRef>(
+  decoder_input_queue_.push_back(std::make_unique<BitstreamBufferRef>(
       decode_client_, decode_task_runner_, nullptr, kFlushBufferId));
   decoder_flushing_ = true;
   SendPictureReady();  // Send all pending PictureReady.
@@ -1740,7 +1746,7 @@
   }
   decoder_current_bitstream_buffer_.reset();
   while (!decoder_input_queue_.empty())
-    decoder_input_queue_.pop();
+    decoder_input_queue_.pop_front();
 
   current_input_buffer_ = V4L2WritableBufferRef();
 
@@ -1854,13 +1860,22 @@
   decoder_decode_buffer_tasks_scheduled_ = 0;
   decoder_frames_at_client_ = 0;
   while (!decoder_input_queue_.empty())
-    decoder_input_queue_.pop();
+    decoder_input_queue_.pop_front();
   decoder_flushing_ = false;
 
   image_processor_ = nullptr;
 
   DestroyInputBuffers();
   DestroyOutputBuffers();
+
+  if (decoder_thread_.IsRunning()) {
+    DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
+    // DestroyTask can be executed on not only decoder_thread but also child
+    // thread. When decoder thread is Stop(), |this| is not registered in
+    // MemoryDumpManager. So
+    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+        this);
+  }
 }
 
 bool V4L2VideoDecodeAccelerator::StartDevicePoll() {
@@ -2716,4 +2731,62 @@
   NOTIFY_ERROR(PLATFORM_FAILURE);
 }
 
+bool V4L2VideoDecodeAccelerator::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  // OnMemoryDump() must be performed on |decoder_thread_|.
+  DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
+
+  // |input_queue| and |output_queue| are owned by |decoder_thread_|.
+  size_t input_queue_buffers_count = 0;
+  size_t input_queue_memory_usage = 0;
+  std::string input_queue_buffers_memory_type;
+  if (input_queue_) {
+    input_queue_buffers_count = input_queue_->AllocatedBuffersCount();
+    input_queue_buffers_memory_type =
+        V4L2Device::V4L2MemoryToString(input_queue_->GetMemoryType());
+    if (output_queue_->GetMemoryType() == V4L2_MEMORY_MMAP)
+      input_queue_memory_usage = input_queue_->GetMemoryUsage();
+  }
+
+  size_t output_queue_buffers_count = 0;
+  size_t output_queue_memory_usage = 0;
+  std::string output_queue_buffers_memory_type;
+  if (output_queue_) {
+    output_queue_buffers_count = output_queue_->AllocatedBuffersCount();
+    output_queue_buffers_memory_type =
+        V4L2Device::V4L2MemoryToString(output_queue_->GetMemoryType());
+    if (output_queue_->GetMemoryType() == V4L2_MEMORY_MMAP)
+      output_queue_memory_usage = output_queue_->GetMemoryUsage();
+  }
+
+  const size_t total_usage =
+      input_queue_memory_usage + output_queue_memory_usage;
+
+  using ::base::trace_event::MemoryAllocatorDump;
+
+  auto dump_name = base::StringPrintf("gpu/v4l2/decoder/0x%" PRIxPTR,
+                                      reinterpret_cast<uintptr_t>(this));
+  MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name);
+  dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                  MemoryAllocatorDump::kUnitsBytes,
+                  static_cast<uint64_t>(total_usage));
+  dump->AddScalar("input_queue_memory_usage", MemoryAllocatorDump::kUnitsBytes,
+                  static_cast<uint64_t>(input_queue_memory_usage));
+  dump->AddScalar("input_queue_buffers_count",
+                  MemoryAllocatorDump::kUnitsObjects,
+                  static_cast<uint64_t>(input_queue_buffers_count));
+  dump->AddString("input_queue_buffers_memory_type", "",
+                  input_queue_buffers_memory_type);
+  dump->AddScalar("output_queue_memory_usage", MemoryAllocatorDump::kUnitsBytes,
+                  static_cast<uint64_t>(output_queue_memory_usage));
+  dump->AddScalar("output_queue_buffers_count",
+                  MemoryAllocatorDump::kUnitsObjects,
+                  static_cast<uint64_t>(output_queue_buffers_count));
+  dump->AddString("output_queue_buffers_memory_type", "",
+                  output_queue_buffers_memory_type);
+
+  return true;
+}
+
 }  // namespace media
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index 526394f..032a84d 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -22,6 +22,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "media/base/limits.h"
 #include "media/base/video_decoder_config.h"
 #include "media/gpu/gpu_video_decode_accelerator_helpers.h"
@@ -91,7 +92,8 @@
 //   buffrers. We cannot drop any frame during resolution change. So V4L2VDA
 //   should destroy output buffers after image processor returns all the frames.
 class MEDIA_GPU_EXPORT V4L2VideoDecodeAccelerator
-    : public VideoDecodeAccelerator {
+    : public VideoDecodeAccelerator,
+      public base::trace_event::MemoryDumpProvider {
  public:
   V4L2VideoDecodeAccelerator(
       EGLDisplay egl_display,
@@ -122,6 +124,10 @@
 
   static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles();
 
+  // base::trace_event::MemoryDumpProvider implementation.
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
  private:
   // These are rather subjectively tuned.
   enum {
@@ -483,8 +489,11 @@
   // Got a reset request while we were performing resolution change or waiting
   // picture buffers.
   bool reset_pending_;
-  // Input queue for decoder_thread_: BitstreamBuffers in.
-  base::queue<std::unique_ptr<BitstreamBufferRef>> decoder_input_queue_;
+  // Input queue for decoder_thread_: BitstreamBuffers in. Although the elements
+  // in |decoder_input_queue_| is push()/pop() in queue order, this needs to be
+  // base::circular_deque because we need to do random access in OnMemoryDump().
+  base::circular_deque<std::unique_ptr<BitstreamBufferRef>>
+      decoder_input_queue_;
   // For H264 decode, hardware requires that we send it frame-sized chunks.
   // We'll need to parse the stream.
   std::unique_ptr<H264Parser> decoder_h264_parser_;
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 4219c84..76354ad 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -24,7 +24,8 @@
 namespace test {
 
 namespace {
-// Test environment for video decode tests.
+// Test environment for video decode tests. Performs setup and teardown once for
+// the entire test run.
 class VideoDecoderTestEnvironment : public ::testing::Environment {
  public:
   explicit VideoDecoderTestEnvironment(const Video* video) : video_(video) {}
@@ -35,14 +36,8 @@
   // Tear down the video decode test environment, only called once.
   void TearDown() override;
 
-  std::unique_ptr<VideoPlayer> CreateVideoPlayer() {
-    return VideoPlayer::Create(dummy_frame_renderer_.get(),
-                               frame_validator_.get());
-  }
-
   std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_;
   std::unique_ptr<FrameRendererDummy> dummy_frame_renderer_;
-  std::unique_ptr<VideoFrameValidator> frame_validator_;
   const Video* const video_;
 
   // An exit manager is required to run callbacks on shutdown.
@@ -68,8 +63,6 @@
 
   dummy_frame_renderer_ = FrameRendererDummy::Create();
   ASSERT_NE(dummy_frame_renderer_, nullptr);
-
-  frame_validator_ = media::test::VideoFrameValidator::Create();
 }
 
 void VideoDecoderTestEnvironment::TearDown() {
@@ -79,147 +72,143 @@
 
 media::test::VideoDecoderTestEnvironment* g_env;
 
+// Video decode test class. Performs setup and teardown for each single test.
+class VideoDecoderTest : public ::testing::Test {
+ public:
+  std::unique_ptr<VideoPlayer> CreateVideoPlayer(const Video* video) {
+    frame_validator_ =
+        media::test::VideoFrameValidator::Create(video->FrameChecksums());
+    return VideoPlayer::Create(video, g_env->dummy_frame_renderer_.get(),
+                               {frame_validator_.get()});
+  }
+
+ protected:
+  std::unique_ptr<VideoFrameValidator> frame_validator_;
+};
+
 }  // namespace
 
 // Play video from start to end. Wait for the kFlushDone event at the end of the
 // stream, that notifies us all frames have been decoded.
-TEST(VideoDecodeAcceleratorTest, FlushAtEndOfStream) {
-  auto tvp = g_env->CreateVideoPlayer();
-  // TODO(dstaessens) Remove this function and provide the stream on
-  // construction of the VideoPlayer.
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, FlushAtEndOfStream) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Flush the decoder immediately after initialization.
-TEST(VideoDecodeAcceleratorTest, FlushAfterInitialize) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, FlushAfterInitialize) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Flush();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 2u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Flush the decoder immediately after doing a mid-stream reset, without waiting
 // for a kResetDone event.
-TEST(VideoDecodeAcceleratorTest, FlushBeforeResetDone) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, FlushBeforeResetDone) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFrameDecoded,
-                                g_env->video_->NumFrames() / 2));
+  EXPECT_TRUE(tvp->WaitForFrameDecoded(g_env->video_->NumFrames() / 2));
   tvp->Reset();
   tvp->Flush();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kResetDone));
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForResetDone());
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
   // As flush doesn't cancel reset, we should have received a single kResetDone
   // and kFlushDone event. We didn't decode the entire video, but more frames
   // might be decoded by the time we called reset, so we can only check whether
   // the decoded frame count is <= the total number of frames.
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kResetDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
-  EXPECT_LE(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_LE(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Reset the decoder immediately after initialization.
-TEST(VideoDecodeAcceleratorTest, ResetAfterInitialize) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, ResetAfterInitialize) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Reset();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kResetDone));
+  EXPECT_TRUE(tvp->WaitForResetDone());
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kResetDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Reset the decoder when the middle of the stream is reached.
-TEST(VideoDecodeAcceleratorTest, ResetMidStream) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, ResetMidStream) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFrameDecoded,
-                                g_env->video_->NumFrames() / 2));
+  EXPECT_TRUE(tvp->WaitForFrameDecoded(g_env->video_->NumFrames() / 2));
   tvp->Reset();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kResetDone));
-  size_t numFramesDecoded = tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded);
+  EXPECT_TRUE(tvp->WaitForResetDone());
+  size_t numFramesDecoded = tvp->GetFrameDecodedCount();
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kResetDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
+  EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(),
             numFramesDecoded + g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Reset the decoder when the end of the stream is reached.
-TEST(VideoDecodeAcceleratorTest, ResetEndOfStream) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, ResetEndOfStream) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
+  EXPECT_TRUE(tvp->WaitForFlushDone());
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
   tvp->Reset();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kResetDone));
+  EXPECT_TRUE(tvp->WaitForResetDone());
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
 
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kResetDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 2u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames() * 2);
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames() * 2);
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 // Reset the decoder immediately when the end-of-stream flush starts, without
 // waiting for a kFlushDone event.
-TEST(VideoDecodeAcceleratorTest, ResetBeforeFlushDone) {
-  auto tvp = g_env->CreateVideoPlayer();
-  tvp->SetStream(g_env->video_);
+TEST_F(VideoDecoderTest, ResetBeforeFlushDone) {
+  auto tvp = CreateVideoPlayer(g_env->video_);
 
   // Reset when a kFlushing event is received.
   tvp->Play();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushing));
+  EXPECT_TRUE(tvp->WaitForFlushDone());
   tvp->Reset();
-  EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kResetDone));
+  EXPECT_TRUE(tvp->WaitForResetDone());
 
   // Reset will cause the decoder to drop everything it's doing, including the
   // ongoing flush operation. However the flush might have been completed
   // already by the time reset is called. So depending on the timing of the
   // calls we should see 0 or 1 flushes, and the last few video frames might
   // have been dropped.
-  EXPECT_LE(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
-  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kResetDone), 1u);
-  EXPECT_LE(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded),
-            g_env->video_->NumFrames());
-  EXPECT_EQ(0u, g_env->frame_validator_->GetMismatchedFramesCount());
+  EXPECT_LE(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
+  EXPECT_LE(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
+  EXPECT_EQ(0u, frame_validator_->GetMismatchedFramesCount());
 }
 
 }  // namespace test
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index 3aa9e4d..4ee2c43 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -605,8 +605,8 @@
   if (video_frame_validator_) {
     auto video_frame = texture_it->second->ExportVideoFrame(visible_rect);
     ASSERT_NE(video_frame.get(), nullptr);
-    video_frame_validator_->EvaluateVideoFrame(std::move(video_frame),
-                                               frame_index_);
+    video_frame_validator_->ProcessVideoFrame(std::move(video_frame),
+                                              frame_index_);
     frame_index_++;
   }
   rendering_helper_->ConsumeVideoFrame(config_.window_id,
diff --git a/media/learning/common/labelled_example.cc b/media/learning/common/labelled_example.cc
index 459a79c..6c8cb7d 100644
--- a/media/learning/common/labelled_example.cc
+++ b/media/learning/common/labelled_example.cc
@@ -69,6 +69,8 @@
 
 TrainingData::~TrainingData() = default;
 
+TrainingData& TrainingData::operator=(TrainingData&& rhs) = default;
+
 TrainingData TrainingData::DeDuplicate() const {
   // flat_set has non-const iterators, while std::set does not.  const_cast is
   // not allowed by chromium style outside of getters, so flat_set it is.
diff --git a/media/learning/common/labelled_example.h b/media/learning/common/labelled_example.h
index 11ffaaf..63e4c1e 100644
--- a/media/learning/common/labelled_example.h
+++ b/media/learning/common/labelled_example.h
@@ -65,6 +65,8 @@
   TrainingData(const TrainingData& rhs);
   TrainingData(TrainingData&& rhs);
 
+  TrainingData& operator=(TrainingData&& rhs);
+
   ~TrainingData();
 
   // Add |example| with weight |weight|.
diff --git a/media/learning/common/learning_task.cc b/media/learning/common/learning_task.cc
index 07cc407..62c80a1e 100644
--- a/media/learning/common/learning_task.cc
+++ b/media/learning/common/learning_task.cc
@@ -17,7 +17,10 @@
     : name(name),
       model(model),
       feature_descriptions(std::move(feature_init_list)),
-      target_description(target_description) {}
+      target_description(target_description) {
+  // Default this to something sane.
+  uma_name = std::string("Media.Learning.") + name;
+}
 
 LearningTask::LearningTask(const LearningTask&) = default;
 
diff --git a/media/learning/common/learning_task.h b/media/learning/common/learning_task.h
index bd29a08..223bb7c 100644
--- a/media/learning/common/learning_task.h
+++ b/media/learning/common/learning_task.h
@@ -111,6 +111,20 @@
 
   // Number of trees in the random forest.
   size_t rf_number_of_trees = 100;
+
+  // Reporting parameters
+
+  // This is a hack for the initial media capabilities investigation. It
+  // represents the threshold that we'll use to decide if a prediction would be
+  // T / F.  We should not do this -- instead we should report the distribution
+  // average for the prediction and the observation via UKM.
+  //
+  // In particular, if the percentage of dropped frames is greater than this,
+  // then report "false" (not smooth), else we report true.
+  double smoothness_threshold = 0.1;
+
+  // For our hacky confusion matrix reporting, this is the UMA histogram name.
+  std::string uma_name;
 };
 
 }  // namespace learning
diff --git a/media/learning/impl/BUILD.gn b/media/learning/impl/BUILD.gn
index 7b7b82b..0887d11 100644
--- a/media/learning/impl/BUILD.gn
+++ b/media/learning/impl/BUILD.gn
@@ -7,6 +7,8 @@
   visibility = [ "//media/learning/impl:unit_tests" ]
 
   sources = [
+    "distribution_reporter.cc",
+    "distribution_reporter.h",
     "extra_trees_trainer.cc",
     "extra_trees_trainer.h",
     "learning_session_impl.cc",
@@ -32,6 +34,7 @@
 
   deps = [
     "//base",
+    "//services/metrics/public/cpp:metrics_cpp",
   ]
 
   public_deps = [
@@ -43,6 +46,7 @@
   testonly = true
 
   sources = [
+    "distribution_reporter_unittest.cc",
     "extra_trees_trainer_unittest.cc",
     "fisher_iris_dataset.cc",
     "fisher_iris_dataset.h",
diff --git a/media/learning/impl/distribution_reporter.cc b/media/learning/impl/distribution_reporter.cc
new file mode 100644
index 0000000..e8fe397
--- /dev/null
+++ b/media/learning/impl/distribution_reporter.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 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 "media/learning/impl/distribution_reporter.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
+
+namespace media {
+namespace learning {
+
+// Low order bit is "observed", second bit is "predicted".
+enum class ConfusionMatrix {
+  TrueNegative = 0,   // predicted == observed == false
+  FalseNegative = 1,  // predicted == false, observed == true
+  FalsePositive = 2,  // predicted == true, observed == false
+  TruePositive = 3,   // predicted == observed == true
+  kMaxValue = TruePositive
+};
+
+// TODO(liberato): Currently, this implementation is a hack to collect some
+// sanity-checking data for local learning with MediaCapabilities.  We assume
+// that the prediction is the "percentage of dropped frames".
+//
+// Please see https://chromium-review.googlesource.com/c/chromium/src/+/1385107
+// for an actual UKM-based implementation.
+class RegressionReporter : public DistributionReporter {
+ public:
+  RegressionReporter(const LearningTask& task) : DistributionReporter(task) {}
+
+  void OnPrediction(TargetDistribution observed,
+                    TargetDistribution predicted) override {
+    // As a complete hack, record accuracy with a fixed threshold.  The average
+    // is the observed / predicted percentage of dropped frames.
+    bool observed_smooth = observed.Average() <= task().smoothness_threshold;
+    bool predicted_smooth = predicted.Average() <= task().smoothness_threshold;
+
+    // Convert to a bucket from which we can get the confusion matrix.
+    ConfusionMatrix uma_bucket = static_cast<ConfusionMatrix>(
+        (observed_smooth ? 1 : 0) | (predicted_smooth ? 2 : 0));
+    base::UmaHistogramEnumeration(task().uma_name, uma_bucket);
+  }
+};
+
+std::unique_ptr<DistributionReporter> DistributionReporter::Create(
+    const LearningTask& task) {
+  if (task.target_description.ordering == LearningTask::Ordering::kNumeric)
+    return std::make_unique<RegressionReporter>(task);
+  return nullptr;
+}
+
+DistributionReporter::DistributionReporter(const LearningTask& task)
+    : task_(task), weak_factory_(this) {}
+
+DistributionReporter::~DistributionReporter() = default;
+
+Model::PredictionCB DistributionReporter::GetPredictionCallback(
+    TargetDistribution observed) {
+  return base::BindOnce(&DistributionReporter::OnPrediction,
+                        weak_factory_.GetWeakPtr(), observed);
+}
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/distribution_reporter.h b/media/learning/impl/distribution_reporter.h
new file mode 100644
index 0000000..78b22e65
--- /dev/null
+++ b/media/learning/impl/distribution_reporter.h
@@ -0,0 +1,54 @@
+// Copyright 2018 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 MEDIA_LEARNING_IMPL_DISTRIBUTION_REPORTER_H_
+#define MEDIA_LEARNING_IMPL_DISTRIBUTION_REPORTER_H_
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "media/learning/common/learning_task.h"
+#include "media/learning/impl/model.h"
+#include "media/learning/impl/target_distribution.h"
+
+namespace media {
+namespace learning {
+
+// Helper class to report on predicted distrubutions vs target distributions.
+// Use DistributionReporter::Create() to create one that's appropriate for a
+// specific learning task.
+class COMPONENT_EXPORT(LEARNING_IMPL) DistributionReporter {
+ public:
+  // Create a DistributionReporter that's suitable for |task|.
+  static std::unique_ptr<DistributionReporter> Create(const LearningTask& task);
+
+  virtual ~DistributionReporter();
+
+  // Returns a prediction CB that will be compared to |observed|.  |observed| is
+  // the total number of counts that we observed.
+  virtual Model::PredictionCB GetPredictionCallback(
+      TargetDistribution observed);
+
+ protected:
+  DistributionReporter(const LearningTask& task);
+
+  const LearningTask& task() const { return task_; }
+
+  // Implemented by subclasses to report a prediction.
+  virtual void OnPrediction(TargetDistribution observed,
+                            TargetDistribution predicted) = 0;
+
+ private:
+  LearningTask task_;
+
+  base::WeakPtrFactory<DistributionReporter> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DistributionReporter);
+};
+
+}  // namespace learning
+}  // namespace media
+
+#endif  // MEDIA_LEARNING_IMPL_DISTRIBUTION_REPORTER_H_
diff --git a/media/learning/impl/distribution_reporter_unittest.cc b/media/learning/impl/distribution_reporter_unittest.cc
new file mode 100644
index 0000000..085af55
--- /dev/null
+++ b/media/learning/impl/distribution_reporter_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 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 <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/learning/common/learning_task.h"
+#include "media/learning/impl/distribution_reporter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace learning {
+
+class DistributionReporterTest : public testing::Test {
+ public:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  LearningTask task_;
+
+  std::unique_ptr<DistributionReporter> reporter_;
+};
+
+TEST_F(DistributionReporterTest, DistributionReporterDoesNotCrash) {
+  task_.target_description.ordering = LearningTask::Ordering::kNumeric;
+  reporter_ = DistributionReporter::Create(task_);
+  EXPECT_NE(reporter_, nullptr);
+
+  const TargetValue Zero(0);
+  const TargetValue One(1);
+
+  TargetDistribution observed;
+  // Observe an average of 2 / 3.
+  observed[Zero] = 100;
+  observed[One] = 200;
+  auto cb = reporter_->GetPredictionCallback(observed);
+
+  TargetDistribution predicted;
+  // Predict an average of 5 / 9.
+  predicted[Zero] = 40;
+  predicted[One] = 50;
+  std::move(cb).Run(predicted);
+
+  // TODO(liberato): When we switch to ukm, use a TestUkmRecorder to make sure
+  // that it fills in the right stuff.
+  // https://chromium-review.googlesource.com/c/chromium/src/+/1385107 .
+}
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/extra_trees_trainer.cc b/media/learning/impl/extra_trees_trainer.cc
index 27e0346d..9d185829 100644
--- a/media/learning/impl/extra_trees_trainer.cc
+++ b/media/learning/impl/extra_trees_trainer.cc
@@ -6,8 +6,8 @@
 
 #include <set>
 
+#include "base/bind.h"
 #include "base/logging.h"
-#include "media/learning/impl/one_hot.h"
 #include "media/learning/impl/random_tree_trainer.h"
 #include "media/learning/impl/voting_ensemble.h"
 
@@ -18,31 +18,46 @@
 
 ExtraTreesTrainer::~ExtraTreesTrainer() = default;
 
-std::unique_ptr<Model> ExtraTreesTrainer::Train(
-    const LearningTask& task,
-    const TrainingData& training_data) {
-  int n_trees = task.rf_number_of_trees;
+void ExtraTreesTrainer::Train(const LearningTask& task,
+                              const TrainingData& training_data,
+                              TrainedModelCB model_cb) {
+  // Make sure that there is no training in progress.
+  DCHECK_EQ(trees_.size(), 0u);
+  DCHECK_EQ(converter_.get(), nullptr);
 
-  RandomTreeTrainer tree_trainer(rng());
-  std::vector<std::unique_ptr<Model>> trees;
-  trees.reserve(n_trees);
+  task_ = task;
+  trees_.reserve(task.rf_number_of_trees);
 
   // RandomTree requires one-hot vectors to properly choose split points the way
   // that ExtraTrees require.
-  std::unique_ptr<OneHotConverter> converter =
-      std::make_unique<OneHotConverter>(task, training_data);
-  TrainingData converted_training_data = converter->Convert(training_data);
+  // TODO(liberato): Modify it not to need this.  It's slow.
+  converter_ = std::make_unique<OneHotConverter>(task, training_data);
+  converted_training_data_ = converter_->Convert(training_data);
 
-  for (int i = 0; i < n_trees; i++) {
-    // Train the tree.
-    std::unique_ptr<Model> tree = tree_trainer.Train(
-        converter->converted_task(), converted_training_data);
+  // Start training.  Send in nullptr to start the process.
+  OnRandomTreeModel(std::move(model_cb), nullptr);
+}
 
-    trees.push_back(std::move(tree));
+void ExtraTreesTrainer::OnRandomTreeModel(TrainedModelCB model_cb,
+                                          std::unique_ptr<Model> model) {
+  // Allow a null Model to make it easy to start training.
+  if (model)
+    trees_.push_back(std::move(model));
+
+  // If this is the last tree, then return the finished model.
+  if (trees_.size() == task_.rf_number_of_trees) {
+    std::move(model_cb).Run(std::make_unique<ConvertingModel>(
+        std::move(converter_),
+        std::make_unique<VotingEnsemble>(std::move(trees_))));
+    return;
   }
 
-  return std::make_unique<ConvertingModel>(
-      std::move(converter), std::make_unique<VotingEnsemble>(std::move(trees)));
+  // Train the next tree.
+  auto cb = base::BindOnce(&ExtraTreesTrainer::OnRandomTreeModel, AsWeakPtr(),
+                           std::move(model_cb));
+  RandomTreeTrainer tree_trainer(rng());
+  tree_trainer.Train(converter_->converted_task(), converted_training_data_,
+                     std::move(cb));
 }
 
 }  // namespace learning
diff --git a/media/learning/impl/extra_trees_trainer.h b/media/learning/impl/extra_trees_trainer.h
index 34a99187..3eb0804 100644
--- a/media/learning/impl/extra_trees_trainer.h
+++ b/media/learning/impl/extra_trees_trainer.h
@@ -10,7 +10,9 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "media/learning/common/learning_task.h"
+#include "media/learning/impl/one_hot.h"
 #include "media/learning/impl/random_number_generator.h"
 #include "media/learning/impl/training_algorithm.h"
 
@@ -26,15 +28,25 @@
 //
 // These will automatically convert nominal values to one-hot vectors.
 class COMPONENT_EXPORT(LEARNING_IMPL) ExtraTreesTrainer
-    : public HasRandomNumberGenerator {
+    : public HasRandomNumberGenerator,
+      public base::SupportsWeakPtr<ExtraTreesTrainer> {
  public:
   ExtraTreesTrainer();
   ~ExtraTreesTrainer();
 
-  std::unique_ptr<Model> Train(const LearningTask& task,
-                               const TrainingData& training_data);
+  void Train(const LearningTask& task,
+             const TrainingData& training_data,
+             TrainedModelCB model_cb);
 
  private:
+  void OnRandomTreeModel(TrainedModelCB model_cb, std::unique_ptr<Model> model);
+
+  // In-flight training.
+  LearningTask task_;
+  std::vector<std::unique_ptr<Model>> trees_;
+  std::unique_ptr<OneHotConverter> converter_;
+  TrainingData converted_training_data_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtraTreesTrainer);
 };
 
diff --git a/media/learning/impl/extra_trees_trainer_unittest.cc b/media/learning/impl/extra_trees_trainer_unittest.cc
index aaa5929f..b7b502a 100644
--- a/media/learning/impl/extra_trees_trainer_unittest.cc
+++ b/media/learning/impl/extra_trees_trainer_unittest.cc
@@ -5,6 +5,7 @@
 #include "media/learning/impl/extra_trees_trainer.h"
 
 #include "base/memory/ref_counted.h"
+#include "base/test/scoped_task_environment.h"
 #include "media/learning/impl/fisher_iris_dataset.h"
 #include "media/learning/impl/test_random_number_generator.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,6 +28,21 @@
     }
   }
 
+  std::unique_ptr<Model> Train(const LearningTask& task,
+                               const TrainingData& data) {
+    std::unique_ptr<Model> model;
+    trainer_.Train(
+        task_, data,
+        base::BindOnce(
+            [](std::unique_ptr<Model>* model_out,
+               std::unique_ptr<Model> model) { *model_out = std::move(model); },
+            &model));
+    scoped_task_environment_.RunUntilIdle();
+    return model;
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   TestRandomNumberGenerator rng_;
   ExtraTreesTrainer trainer_;
   LearningTask task_;
@@ -36,7 +52,7 @@
 
 TEST_P(ExtraTreesTest, EmptyTrainingDataWorks) {
   TrainingData empty;
-  auto model = trainer_.Train(task_, empty);
+  auto model = Train(task_, empty);
   EXPECT_NE(model.get(), nullptr);
   EXPECT_EQ(model->PredictDistribution(FeatureVector()), TargetDistribution());
 }
@@ -45,7 +61,7 @@
   SetupFeatures(4);
   FisherIrisDataset iris;
   TrainingData training_data = iris.GetTrainingData();
-  auto model = trainer_.Train(task_, training_data);
+  auto model = Train(task_, training_data);
 
   // Verify predictions on the training set, just for sanity.
   size_t num_correct = 0;
@@ -82,7 +98,7 @@
 
   // Create a weighed set with |weight| for each example's weight.
   EXPECT_FALSE(training_data.is_unweighted());
-  auto model = trainer_.Train(task_, training_data);
+  auto model = Train(task_, training_data);
 
   // The singular max should be example_1.
   TargetDistribution distribution =
@@ -115,7 +131,7 @@
   task_.target_description.ordering = LearningTask::Ordering::kNumeric;
 
   // Create a weighed set with |weight| for each example's weight.
-  auto model = trainer_.Train(task_, training_data);
+  auto model = Train(task_, training_data);
 
   // Make sure that the results are in the right range.
   TargetDistribution distribution =
@@ -168,9 +184,9 @@
   }
 
   // Train a model on the binary classification task and the regression task.
-  auto c_model = trainer_.Train(task_, c_data);
+  auto c_model = Train(task_, c_data);
   task_.target_description.ordering = LearningTask::Ordering::kNumeric;
-  auto r_model = trainer_.Train(task_, r_data);
+  auto r_model = Train(task_, r_data);
 
   // Verify that, for all feature combinations, the models roughly agree.  Since
   // the data is separable, it probably should be exact.
diff --git a/media/learning/impl/learning_task_controller_impl.cc b/media/learning/impl/learning_task_controller_impl.cc
index 8d41ba6..af53091 100644
--- a/media/learning/impl/learning_task_controller_impl.cc
+++ b/media/learning/impl/learning_task_controller_impl.cc
@@ -13,23 +13,23 @@
 namespace media {
 namespace learning {
 
-LearningTaskControllerImpl::LearningTaskControllerImpl(const LearningTask& task)
-    : task_(task), training_data_(std::make_unique<TrainingData>()) {
+LearningTaskControllerImpl::LearningTaskControllerImpl(
+    const LearningTask& task,
+    std::unique_ptr<DistributionReporter> reporter)
+    : task_(task),
+      training_data_(std::make_unique<TrainingData>()),
+      reporter_(std::move(reporter)) {
   switch (task_.model) {
     case LearningTask::Model::kExtraTrees:
       training_cb_ = base::BindRepeating(
           [](const LearningTask& task, TrainingData training_data,
              TrainedModelCB model_cb) {
             ExtraTreesTrainer trainer;
-            std::move(model_cb).Run(trainer.Train(task, training_data));
+            trainer.Train(task, training_data, std::move(model_cb));
           },
           task_);
       break;
   }
-
-  // TODO(liberato): Record via UMA based on the task name.
-  accuracy_reporting_cb_ =
-      base::BindRepeating([](const LearningTask&, bool is_correct) {});
 }
 
 LearningTaskControllerImpl::~LearningTaskControllerImpl() = default;
@@ -39,15 +39,13 @@
   training_data_->push_back(example);
 
   // Once we have a model, see if we'd get |example| correct.
-  if (model_) {
-    TargetDistribution distribution =
+  if (model_ && reporter_) {
+    TargetDistribution predicted =
         model_->PredictDistribution(example.features);
 
-    TargetValue predicted_value;
-    const bool is_correct = distribution.FindSingularMax(&predicted_value) &&
-                            predicted_value == example.target_value;
-    accuracy_reporting_cb_.Run(task_, is_correct);
-    // TODO(liberato): record entropy / not representable?
+    TargetDistribution observed;
+    observed += example.target_value;
+    reporter_->GetPredictionCallback(observed).Run(predicted);
   }
 
   // Train every time we get a multiple of |data_set_size|.
@@ -67,7 +65,6 @@
 
 void LearningTaskControllerImpl::OnModelTrained(std::unique_ptr<Model> model) {
   model_ = std::move(model);
-  // TODO(liberato): record oob results.
 }
 
 void LearningTaskControllerImpl::SetTrainingCBForTesting(
@@ -75,10 +72,5 @@
   training_cb_ = std::move(cb);
 }
 
-void LearningTaskControllerImpl::SetAccuracyReportingCBForTesting(
-    AccuracyReportingCB cb) {
-  accuracy_reporting_cb_ = std::move(cb);
-}
-
 }  // namespace learning
 }  // namespace media
diff --git a/media/learning/impl/learning_task_controller_impl.h b/media/learning/impl/learning_task_controller_impl.h
index 8718e5136..fd1118e6 100644
--- a/media/learning/impl/learning_task_controller_impl.h
+++ b/media/learning/impl/learning_task_controller_impl.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
+#include "media/learning/impl/distribution_reporter.h"
 #include "media/learning/impl/learning_task_controller.h"
 #include "media/learning/impl/training_algorithm.h"
 
@@ -22,24 +23,18 @@
     : public LearningTaskController,
       public base::SupportsWeakPtr<LearningTaskControllerImpl> {
  public:
-  explicit LearningTaskControllerImpl(const LearningTask& task);
+  LearningTaskControllerImpl(
+      const LearningTask& task,
+      std::unique_ptr<DistributionReporter> reporter = nullptr);
   ~LearningTaskControllerImpl() override;
 
   // LearningTaskController
   void AddExample(const LabelledExample& example) override;
 
  private:
-  // Called with accuracy results as new examples are added.  Only tests should
-  // need to worry about this.
-  using AccuracyReportingCB =
-      base::RepeatingCallback<void(const LearningTask& task, bool is_correct)>;
-
   // Override the training CB for testing.
   void SetTrainingCBForTesting(TrainingAlgorithmCB cb);
 
-  // Override the reporting CB for testing.
-  void SetAccuracyReportingCBForTesting(AccuracyReportingCB cb);
-
   // Called by |training_cb_| when the model is trained.
   void OnModelTrained(std::unique_ptr<Model> model);
 
@@ -53,7 +48,8 @@
 
   TrainingAlgorithmCB training_cb_;
 
-  AccuracyReportingCB accuracy_reporting_cb_;
+  // Optional reporter for training accuracy.
+  std::unique_ptr<DistributionReporter> reporter_;
 
   friend class LearningTaskControllerImplTest;
 };
diff --git a/media/learning/impl/learning_task_controller_impl_unittest.cc b/media/learning/impl/learning_task_controller_impl_unittest.cc
index 4c8caff..61d363dc 100644
--- a/media/learning/impl/learning_task_controller_impl_unittest.cc
+++ b/media/learning/impl/learning_task_controller_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "media/learning/impl/learning_task_controller_impl.h"
 
 #include "base/bind.h"
+#include "media/learning/impl/distribution_reporter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -12,16 +13,37 @@
 
 class LearningTaskControllerImplTest : public testing::Test {
  public:
+  class FakeDistributionReporter : public DistributionReporter {
+   public:
+    FakeDistributionReporter(const LearningTask& task)
+        : DistributionReporter(task) {}
+
+   protected:
+    void OnPrediction(TargetDistribution observed,
+                      TargetDistribution predicted) override {
+      num_reported_++;
+      if (observed == predicted)
+        num_correct_++;
+    }
+
+   public:
+    int num_reported_ = 0;
+    int num_correct_ = 0;
+  };
+
   LearningTaskControllerImplTest()
       : predicted_target_(123), not_predicted_target_(456) {
     // Don't require too many training examples per report.
     task_.min_data_set_size = 4;
 
-    controller_ = std::make_unique<LearningTaskControllerImpl>(task_);
+    std::unique_ptr<FakeDistributionReporter> reporter =
+        std::make_unique<FakeDistributionReporter>(task_);
+    reporter_raw_ = reporter.get();
+
+    controller_ = std::make_unique<LearningTaskControllerImpl>(
+        task_, std::move(reporter));
     controller_->SetTrainingCBForTesting(base::BindRepeating(
         &LearningTaskControllerImplTest::OnTrain, base::Unretained(this)));
-    controller_->SetAccuracyReportingCBForTesting(base::BindRepeating(
-        &LearningTaskControllerImplTest::OnAccuracy, base::Unretained(this)));
   }
 
   // Model that always predicts a constant.
@@ -47,23 +69,15 @@
     std::move(model_cb).Run(std::make_unique<FakeModel>(predicted_target_));
   }
 
-  void OnAccuracy(const LearningTask& task, bool is_correct) {
-    num_reported_++;
-    if (is_correct)
-      num_correct_++;
-  }
-
   // Number of models that we trained.
   int num_models_ = 0;
 
-  // Results reported via OnAccuracy.
-  int num_reported_ = 0;
-  int num_correct_ = 0;
-
   // Two distinct targets.
   TargetValue predicted_target_;
   TargetValue not_predicted_target_;
 
+  FakeDistributionReporter* reporter_raw_ = nullptr;
+
   LearningTask task_;
   std::unique_ptr<LearningTaskControllerImpl> controller_;
 };
@@ -81,22 +95,22 @@
   EXPECT_EQ(num_models_, 1);
 
   // No results should be reported yet.
-  EXPECT_EQ(num_reported_, 0);
-  EXPECT_EQ(num_correct_, 0);
+  EXPECT_EQ(reporter_raw_->num_reported_, 0);
+  EXPECT_EQ(reporter_raw_->num_correct_, 0);
 
   // Adding one more example should report results.
   example.target_value = predicted_target_;
   controller_->AddExample(example);
   EXPECT_EQ(num_models_, 1);
-  EXPECT_EQ(num_reported_, 1);
-  EXPECT_EQ(num_correct_, 1);
+  EXPECT_EQ(reporter_raw_->num_reported_, 1);
+  EXPECT_EQ(reporter_raw_->num_correct_, 1);
 
   // Adding a value that doesn't match should report one more attempt.
   example.target_value = not_predicted_target_;
   controller_->AddExample(example);
   EXPECT_EQ(num_models_, 1);
-  EXPECT_EQ(num_reported_, 2);
-  EXPECT_EQ(num_correct_, 1);  // Still 1.
+  EXPECT_EQ(reporter_raw_->num_reported_, 2);
+  EXPECT_EQ(reporter_raw_->num_correct_, 1);  // Still 1.
 }
 
 }  // namespace learning
diff --git a/media/learning/impl/model.h b/media/learning/impl/model.h
index 95b7b852..0950b6c 100644
--- a/media/learning/impl/model.h
+++ b/media/learning/impl/model.h
@@ -18,10 +18,15 @@
 // can support it.
 class COMPONENT_EXPORT(LEARNING_IMPL) Model {
  public:
+  // Callback for asynchronous predictions.
+  using PredictionCB = base::OnceCallback<void(TargetDistribution predicted)>;
+
   virtual ~Model() = default;
 
   virtual TargetDistribution PredictDistribution(
       const FeatureVector& instance) = 0;
+
+  // TODO(liberato): Consider adding an async prediction helper.
 };
 
 }  // namespace learning
diff --git a/media/learning/impl/random_tree_trainer.cc b/media/learning/impl/random_tree_trainer.cc
index 3f2d6887..50a45ab 100644
--- a/media/learning/impl/random_tree_trainer.cc
+++ b/media/learning/impl/random_tree_trainer.cc
@@ -9,21 +9,11 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/optional.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 
 namespace media {
 namespace learning {
 
-// static
-TrainingAlgorithmCB RandomTreeTrainer::GetTrainingAlgorithmCB(
-    const LearningTask& task) {
-  return base::BindRepeating(
-      [](LearningTask task, TrainingData training_data,
-         TrainedModelCB model_cb) {
-        std::move(model_cb).Run(RandomTreeTrainer().Train(task, training_data));
-      },
-      task);
-}
-
 RandomTreeTrainer::Split::Split() = default;
 
 RandomTreeTrainer::Split::Split(int index) : split_index(index) {}
@@ -145,16 +135,19 @@
 
 RandomTreeTrainer::~RandomTreeTrainer() = default;
 
-std::unique_ptr<Model> RandomTreeTrainer::Train(
-    const LearningTask& task,
-    const TrainingData& training_data) {
+void RandomTreeTrainer::Train(const LearningTask& task,
+                              const TrainingData& training_data,
+                              TrainedModelCB model_cb) {
   // Start with all the training data.
   std::vector<size_t> training_idx;
   training_idx.reserve(training_data.size());
   for (size_t idx = 0; idx < training_data.size(); idx++)
     training_idx.push_back(idx);
 
-  return Train(task, training_data, training_idx);
+  // It's a little odd that we don't post training.  Perhaps we should.
+  auto model = Train(task, training_data, training_idx);
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(model_cb), std::move(model)));
 }
 
 std::unique_ptr<Model> RandomTreeTrainer::Train(
diff --git a/media/learning/impl/random_tree_trainer.h b/media/learning/impl/random_tree_trainer.h
index 29ee5af..ac9bd7c 100644
--- a/media/learning/impl/random_tree_trainer.h
+++ b/media/learning/impl/random_tree_trainer.h
@@ -80,19 +80,18 @@
   explicit RandomTreeTrainer(RandomNumberGenerator* rng = nullptr);
   ~RandomTreeTrainer();
 
-  // Return a callback that can be used to train a random tree.
-  static TrainingAlgorithmCB GetTrainingAlgorithmCB(const LearningTask& task);
+  // Train on all examples.  Calls |model_cb| with the trained model, which
+  // won't happen before this returns.
+  void Train(const LearningTask& task,
+             const TrainingData& examples,
+             TrainedModelCB model_cb);
 
-  // Train on all examples.
-  std::unique_ptr<Model> Train(const LearningTask& task,
-                               const TrainingData& examples);
-
+ private:
   // Train on the subset |training_idx|.
   std::unique_ptr<Model> Train(const LearningTask& task,
                                const TrainingData& examples,
                                const std::vector<size_t>& training_idx);
 
- private:
   // Set of feature indices.
   using FeatureSet = std::set<int>;
 
diff --git a/media/learning/impl/random_tree_trainer_unittest.cc b/media/learning/impl/random_tree_trainer_unittest.cc
index 5742546..8edfe95 100644
--- a/media/learning/impl/random_tree_trainer_unittest.cc
+++ b/media/learning/impl/random_tree_trainer_unittest.cc
@@ -29,6 +29,19 @@
     }
   }
 
+  std::unique_ptr<Model> Train(const LearningTask& task,
+                               const TrainingData& data) {
+    std::unique_ptr<Model> model;
+    trainer_.Train(
+        task_, data,
+        base::BindOnce(
+            [](std::unique_ptr<Model>* model_out,
+               std::unique_ptr<Model> model) { *model_out = std::move(model); },
+            &model));
+    scoped_task_environment_.RunUntilIdle();
+    return model;
+  }
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   TestRandomNumberGenerator rng_;
@@ -40,7 +53,7 @@
 
 TEST_P(RandomTreeTest, EmptyTrainingDataWorks) {
   TrainingData empty;
-  std::unique_ptr<Model> model = trainer_.Train(task_, empty);
+  std::unique_ptr<Model> model = Train(task_, empty);
   EXPECT_NE(model.get(), nullptr);
   EXPECT_EQ(model->PredictDistribution(FeatureVector()), TargetDistribution());
 }
@@ -53,7 +66,7 @@
   const size_t n_examples = 10;
   for (size_t i = 0; i < n_examples; i++)
     training_data.push_back(example);
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
 
   // The tree should produce a distribution for one value (our target), which
   // has |n_examples| counts.
@@ -63,34 +76,6 @@
   EXPECT_EQ(distribution[example.target_value], n_examples);
 }
 
-TEST_P(RandomTreeTest, UniformTrainingDataWorksWithCallback) {
-  SetupFeatures(2);
-  LabelledExample example({FeatureValue(123), FeatureValue(456)},
-                          TargetValue(789));
-  TrainingData training_data;
-  const size_t n_examples = 10;
-  for (size_t i = 0; i < n_examples; i++)
-    training_data.push_back(example);
-
-  // Construct a TrainedModelCB that will store the model locally.
-  std::unique_ptr<Model> model;
-  TrainedModelCB model_cb = base::BindOnce(
-      [](std::unique_ptr<Model>* model_out, std::unique_ptr<Model> model) {
-        *model_out = std::move(model);
-      },
-      &model);
-
-  // Run the trainer.
-  RandomTreeTrainer::GetTrainingAlgorithmCB(task_).Run(training_data,
-                                                       std::move(model_cb));
-  base::RunLoop().RunUntilIdle();
-
-  TargetDistribution distribution =
-      model->PredictDistribution(example.features);
-  EXPECT_EQ(distribution.size(), 1u);
-  EXPECT_EQ(distribution[example.target_value], n_examples);
-}
-
 TEST_P(RandomTreeTest, SimpleSeparableTrainingData) {
   SetupFeatures(1);
   TrainingData training_data;
@@ -98,7 +83,7 @@
   LabelledExample example_2({FeatureValue(456)}, TargetValue(2));
   training_data.push_back(example_1);
   training_data.push_back(example_2);
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
 
   // Each value should have a distribution with one target value with one count.
   TargetDistribution distribution =
@@ -139,7 +124,7 @@
     }
   }
 
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
   EXPECT_NE(model.get(), nullptr);
 
   // Each example should have a distribution that selects the right value.
@@ -159,7 +144,7 @@
   LabelledExample example_2({FeatureValue(123)}, TargetValue(2));
   training_data.push_back(example_1);
   training_data.push_back(example_2);
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
   EXPECT_NE(model.get(), nullptr);
 
   // Each value should have a distribution with two targets with one count each.
@@ -186,7 +171,7 @@
 
   task_.rt_unknown_value_handling =
       LearningTask::RTUnknownValueHandling::kEmptyDistribution;
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
   TargetDistribution distribution =
       model->PredictDistribution(FeatureVector({FeatureValue(789)}));
   if (ordering_ == LearningTask::Ordering::kUnordered) {
@@ -201,7 +186,7 @@
 
   task_.rt_unknown_value_handling =
       LearningTask::RTUnknownValueHandling::kUseAllSplits;
-  model = trainer_.Train(task_, training_data);
+  model = Train(task_, training_data);
   distribution = model->PredictDistribution(FeatureVector({FeatureValue(789)}));
   if (ordering_ == LearningTask::Ordering::kUnordered) {
     // OOV data should return with the sum of all splits.
@@ -229,7 +214,7 @@
 
   task_.rt_unknown_value_handling =
       LearningTask::RTUnknownValueHandling::kEmptyDistribution;
-  std::unique_ptr<Model> model = trainer_.Train(task_, training_data);
+  std::unique_ptr<Model> model = Train(task_, training_data);
   for (size_t i = 0; i < 4; i++) {
     // Get a prediction for the |i|-th feature value.
     TargetDistribution distribution = model->PredictDistribution(
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index c07333d..e9b228d5 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -25,7 +25,6 @@
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/client/shared_bitmap_reporter.h"
 #include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/stream_video_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -35,6 +34,9 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/command_buffer/common/shared_image_trace_utils.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "media/base/video_frame.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/video/half_float_maker.h"
@@ -299,31 +301,82 @@
 class VideoResourceUpdater::HardwarePlaneResource
     : public VideoResourceUpdater::PlaneResource {
  public:
+  // Provides a RAII scope to access the HardwarePlaneResource as a texture on a
+  // GL context. This will wait on the sync token and provide the shared image
+  // access scope.
+  class ScopedTexture {
+   public:
+    ScopedTexture(gpu::gles2::GLES2Interface* gl,
+                  HardwarePlaneResource* resource)
+        : gl_(gl) {
+      texture_id_ = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
+          resource->mailbox().name);
+      gl_->BeginSharedImageAccessDirectCHROMIUM(
+          texture_id_, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+    }
+
+    ~ScopedTexture() {
+      gl_->EndSharedImageAccessDirectCHROMIUM(texture_id_);
+      gl_->DeleteTextures(1, &texture_id_);
+    }
+
+    GLuint texture_id() const { return texture_id_; }
+
+   private:
+    gpu::gles2::GLES2Interface* gl_;
+    GLuint texture_id_;
+  };
+
   HardwarePlaneResource(uint32_t plane_resource_id,
                         const gfx::Size& size,
                         viz::ResourceFormat format,
-                        viz::ContextProvider* context_provider,
-                        viz::TextureAllocation allocation)
+                        const gfx::ColorSpace& color_space,
+                        bool use_gpu_memory_buffer_resources,
+                        viz::ContextProvider* context_provider)
       : PlaneResource(plane_resource_id, size, format, /*is_software=*/false),
-        context_provider_(context_provider),
-        allocation_(std::move(allocation)) {
+        context_provider_(context_provider) {
     DCHECK(context_provider_);
-    context_provider_->ContextGL()->ProduceTextureDirectCHROMIUM(
-        allocation_.texture_id, mailbox_.name);
+    const gpu::Capabilities& caps = context_provider_->ContextCapabilities();
+    overlay_candidate_ = use_gpu_memory_buffer_resources &&
+                         caps.texture_storage_image &&
+                         IsGpuMemoryBufferFormatSupported(format);
+    uint32_t shared_image_usage =
+        gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_DISPLAY;
+    if (overlay_candidate_) {
+      shared_image_usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
+      texture_target_ = gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
+                                                    BufferFormat(format), caps);
+    }
+    auto* sii = context_provider_->SharedImageInterface();
+    DCHECK(sii);
+    auto* gl = context_provider_->ContextGL();
+    DCHECK(gl);
+
+    mailbox_ =
+        sii->CreateSharedImage(format, size, color_space, shared_image_usage);
+    gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
   }
+
   ~HardwarePlaneResource() override {
-    context_provider_->ContextGL()->DeleteTextures(1, &allocation_.texture_id);
+    auto* sii = context_provider_->SharedImageInterface();
+    DCHECK(sii);
+    auto* gl = context_provider_->ContextGL();
+    DCHECK(gl);
+    gpu::SyncToken sync_token;
+    gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+    sii->DestroySharedImage(sync_token, mailbox_);
   }
 
   const gpu::Mailbox& mailbox() const { return mailbox_; }
-  GLuint texture_id() const { return allocation_.texture_id; }
-  GLenum texture_target() const { return allocation_.texture_target; }
-  bool overlay_candidate() const { return allocation_.overlay_candidate; }
+
+  GLenum texture_target() const { return texture_target_; }
+  bool overlay_candidate() const { return overlay_candidate_; }
 
  private:
   viz::ContextProvider* const context_provider_;
   gpu::Mailbox mailbox_;
-  const viz::TextureAllocation allocation_;
+  GLenum texture_target_ = GL_TEXTURE_2D;
+  bool overlay_candidate_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(HardwarePlaneResource);
 };
@@ -628,22 +681,9 @@
     all_resources_.push_back(std::make_unique<SoftwarePlaneResource>(
         plane_resource_id, plane_size, shared_bitmap_reporter_));
   } else {
-    // Video textures get composited into the display frame, the GPU doesn't
-    // draw to them directly.
-    constexpr bool kForFrameBufferAttachment = false;
-
-    viz::TextureAllocation alloc = viz::TextureAllocation::MakeTextureId(
-        context_provider_->ContextGL(),
-        context_provider_->ContextCapabilities(), format,
-        use_gpu_memory_buffer_resources_, kForFrameBufferAttachment);
-    viz::TextureAllocation::AllocateStorage(
-        context_provider_->ContextGL(),
-        context_provider_->ContextCapabilities(), format, plane_size, alloc,
-        color_space);
-
     all_resources_.push_back(std::make_unique<HardwarePlaneResource>(
-        plane_resource_id, plane_size, format, context_provider_,
-        std::move(alloc)));
+        plane_resource_id, plane_size, format, color_space,
+        use_gpu_memory_buffer_resources_, context_provider_));
   }
   return all_resources_.back().get();
 }
@@ -673,12 +713,17 @@
   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
 
   gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
-  uint32_t src_texture_id =
+  // TODO(piman): convert to CreateAndTexStorage2DSharedImageCHROMIUM once
+  // VideoFrame is all converted to SharedImage.
+  GLuint src_texture_id =
       gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name);
-  gl->CopySubTextureCHROMIUM(
-      src_texture_id, 0, GL_TEXTURE_2D, hardware_resource->texture_id(), 0, 0,
-      0, 0, 0, output_plane_resource_size.width(),
-      output_plane_resource_size.height(), false, false, false);
+  {
+    HardwarePlaneResource::ScopedTexture scope(gl, hardware_resource);
+    gl->CopySubTextureCHROMIUM(
+        src_texture_id, 0, GL_TEXTURE_2D, scope.texture_id(), 0, 0, 0, 0, 0,
+        output_plane_resource_size.width(), output_plane_resource_size.height(),
+        false, false, false);
+  }
   gl->DeleteTextures(1, &src_texture_id);
 
   // Pass an empty sync token to force generation of a new sync token.
@@ -899,13 +944,17 @@
 
         // Copy pixels into texture.
         auto* gl = context_provider_->ContextGL();
-        gl->BindTexture(hardware_resource->texture_target(),
-                        hardware_resource->texture_id());
+
         const gfx::Size& plane_size = hardware_resource->resource_size();
-        gl->TexSubImage2D(
-            hardware_resource->texture_target(), 0, 0, 0, plane_size.width(),
-            plane_size.height(), GLDataFormat(viz::ResourceFormat::RGBA_8888),
-            GLDataType(viz::ResourceFormat::RGBA_8888), upload_pixels_.get());
+        {
+          HardwarePlaneResource::ScopedTexture scope(gl, hardware_resource);
+          gl->BindTexture(hardware_resource->texture_target(),
+                          scope.texture_id());
+          gl->TexSubImage2D(
+              hardware_resource->texture_target(), 0, 0, 0, plane_size.width(),
+              plane_size.height(), GLDataFormat(viz::ResourceFormat::RGBA_8888),
+              GLDataType(viz::ResourceFormat::RGBA_8888), upload_pixels_.get());
+        }
       }
       plane_resource->SetUniqueId(video_frame->unique_id(), 0);
     }
@@ -1054,13 +1103,16 @@
     // Copy pixels into texture. TexSubImage2D() is applicable because
     // |yuv_resource_format| is LUMINANCE_F16, R16_EXT, LUMINANCE_8 or RED_8.
     auto* gl = context_provider_->ContextGL();
-    gl->BindTexture(plane_resource->texture_target(),
-                    plane_resource->texture_id());
     DCHECK(GLSupportsFormat(plane_resource_format));
-    gl->TexSubImage2D(
-        plane_resource->texture_target(), 0, 0, 0, resource_size_pixels.width(),
-        resource_size_pixels.height(), GLDataFormat(plane_resource_format),
-        GLDataType(plane_resource_format), pixels);
+    {
+      HardwarePlaneResource::ScopedTexture scope(gl, plane_resource);
+      gl->BindTexture(plane_resource->texture_target(), scope.texture_id());
+      gl->TexSubImage2D(plane_resource->texture_target(), 0, 0, 0,
+                        resource_size_pixels.width(),
+                        resource_size_pixels.height(),
+                        GLDataFormat(plane_resource_format),
+                        GLDataType(plane_resource_format), pixels);
+    }
 
     plane_resource->SetUniqueId(video_frame->unique_id(), i);
   }
@@ -1154,9 +1206,7 @@
       pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shm_guid, kImportance);
     } else {
       base::trace_event::MemoryAllocatorDumpGuid guid =
-          gl::GetGLTextureClientGUIDForTracing(
-              context_provider_->ContextSupport()->ShareGroupTracingGUID(),
-              resource->AsHardware()->texture_id());
+          gpu::GetSharedImageGUIDForTracing(resource->AsHardware()->mailbox());
       pmd->CreateSharedGlobalAllocatorDump(guid);
       pmd->AddOwnershipEdge(dump->guid(), guid, kImportance);
     }
diff --git a/media/renderers/video_resource_updater_unittest.cc b/media/renderers/video_resource_updater_unittest.cc
index 02743bd..ff8b426 100644
--- a/media/renderers/video_resource_updater_unittest.cc
+++ b/media/renderers/video_resource_updater_unittest.cc
@@ -58,41 +58,19 @@
     ++upload_count_;
   }
 
-  void TexStorage2DEXT(GLenum target,
-                       GLint levels,
-                       GLuint internalformat,
-                       GLint width,
-                       GLint height) override {}
-
-  void GenTextures(GLsizei n, GLuint* textures) override {
-    created_texture_count_ += n;
-    viz::TestGLES2Interface::GenTextures(n, textures);
-  }
-
-  void DeleteTextures(GLsizei n, const GLuint* textures) override {
-    created_texture_count_ -= n;
-    viz::TestGLES2Interface::DeleteTextures(n, textures);
-  }
-
   int UploadCount() { return upload_count_; }
   void ResetUploadCount() { upload_count_ = 0; }
 
-  int TextureCreationCount() { return created_texture_count_; }
-  void ResetTextureCreationCount() { created_texture_count_ = 0; }
-
  private:
   int upload_count_;
-  int created_texture_count_;
 };
 
 class VideoResourceUpdaterTest : public testing::Test {
  protected:
   VideoResourceUpdaterTest() {
-    std::unique_ptr<UploadCounterGLES2Interface> gl(
-        new UploadCounterGLES2Interface());
+    auto gl = std::make_unique<UploadCounterGLES2Interface>();
 
     gl_ = gl.get();
-    gl_->set_support_texture_storage(true);
 
     context_provider_ = viz::TestContextProvider::Create(std::move(gl));
     context_provider_->BindToCurrentThread();
@@ -256,6 +234,10 @@
     return video_frame;
   }
 
+  size_t GetSharedImageCount() {
+    return context_provider_->SharedImageInterface()->shared_image_count();
+  }
+
   static const gpu::SyncToken kMailboxSyncToken;
 
   // VideoResourceUpdater registers as a MemoryDumpProvider, which requires
@@ -560,7 +542,7 @@
   // Note that |use_stream_video_draw_quad| is true for this test.
   std::unique_ptr<VideoResourceUpdater> updater =
       CreateUpdaterForHardware(true);
-  gl_->ResetTextureCreationCount();
+  EXPECT_EQ(0u, GetSharedImageCount());
   scoped_refptr<media::VideoFrame> video_frame =
       CreateTestStreamTextureHardwareVideoFrame(false);
 
@@ -571,11 +553,10 @@
   EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES,
             resources.resources[0].mailbox_holder.texture_target);
   EXPECT_EQ(1u, resources.release_callbacks.size());
-  EXPECT_EQ(0, gl_->TextureCreationCount());
+  EXPECT_EQ(0u, GetSharedImageCount());
 
   // A copied stream texture should return an RGBA resource in a new
   // GL_TEXTURE_2D texture.
-  gl_->ResetTextureCreationCount();
   video_frame = CreateTestStreamTextureHardwareVideoFrame(true);
   resources = updater->CreateExternalResourcesFromVideoFrame(video_frame);
   EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type);
@@ -583,12 +564,12 @@
   EXPECT_EQ((GLenum)GL_TEXTURE_2D,
             resources.resources[0].mailbox_holder.texture_target);
   EXPECT_EQ(1u, resources.release_callbacks.size());
-  EXPECT_EQ(1, gl_->TextureCreationCount());
+  EXPECT_EQ(1u, GetSharedImageCount());
 }
 
 TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_TextureQuad) {
   std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware();
-  gl_->ResetTextureCreationCount();
+  EXPECT_EQ(0u, GetSharedImageCount());
   scoped_refptr<media::VideoFrame> video_frame =
       CreateTestStreamTextureHardwareVideoFrame(false);
 
@@ -599,7 +580,7 @@
   EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES,
             resources.resources[0].mailbox_holder.texture_target);
   EXPECT_EQ(1u, resources.release_callbacks.size());
-  EXPECT_EQ(0, gl_->TextureCreationCount());
+  EXPECT_EQ(0u, GetSharedImageCount());
 }
 
 // Passthrough the sync token returned by the compositor if we don't have an
@@ -695,7 +676,7 @@
 // of the underlying buffer, that is YUV_420_BIPLANAR.
 TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_SingleNV12) {
   std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware();
-  gl_->ResetTextureCreationCount();
+  EXPECT_EQ(0u, GetSharedImageCount());
   scoped_refptr<media::VideoFrame> video_frame = CreateTestHardwareVideoFrame(
       media::PIXEL_FORMAT_NV12, GL_TEXTURE_EXTERNAL_OES);
 
@@ -716,12 +697,12 @@
             resources.resources[0].mailbox_holder.texture_target);
   EXPECT_EQ(viz::YUV_420_BIPLANAR, resources.resources[0].format);
 
-  EXPECT_EQ(0, gl_->TextureCreationCount());
+  EXPECT_EQ(0u, GetSharedImageCount());
 }
 
 TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_DualNV12) {
   std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware();
-  gl_->ResetTextureCreationCount();
+  EXPECT_EQ(0u, GetSharedImageCount());
   scoped_refptr<media::VideoFrame> video_frame =
       CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 2,
                                       GL_TEXTURE_EXTERNAL_OES);
@@ -744,7 +725,7 @@
   EXPECT_EQ((GLenum)GL_TEXTURE_RECTANGLE_ARB,
             resources.resources[0].mailbox_holder.texture_target);
   EXPECT_EQ(viz::RGBA_8888, resources.resources[0].format);
-  EXPECT_EQ(0, gl_->TextureCreationCount());
+  EXPECT_EQ(0u, GetSharedImageCount());
 }
 
 }  // namespace
diff --git a/media/webrtc/audio_processor_controls.h b/media/webrtc/audio_processor_controls.h
index 4b3a28b..79d0cf9 100644
--- a/media/webrtc/audio_processor_controls.h
+++ b/media/webrtc/audio_processor_controls.h
@@ -6,7 +6,7 @@
 #define MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_
 
 #include "base/callback.h"
-#include "third_party/webrtc/api/mediastreaminterface.h"
+#include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace media {
 
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
index 6601325..1cd1677b 100644
--- a/mojo/public/cpp/bindings/binding.h
+++ b/mojo/public/cpp/bindings/binding.h
@@ -238,6 +238,18 @@
     return internal_state_.SwapImplForTesting(new_impl);
   }
 
+  // DO NOT INTRODUCE NEW USES OF THIS METHOD.
+  //
+  // Allows this Binding to dispatch multiple messages within the extent of a
+  // single scheduled task. Normally every incoming message is dispatched by a
+  // dedicated task on the Binding's SequencedTaskRunner, and this is preferred.
+  // Allowing a Binding to do batch dispatch can cause it to starve its sequence
+  // for long periods of time when spammed with messages.
+  //
+  // This will be removed and exists only temporarily to support some edge cases
+  // where an unintended dependency on batch dispatch remains in production.
+  void EnableBatchDispatch() { internal_state_.EnableBatchDispatch(); }
+
   // DO NOT USE. Exposed only for internal use and for testing.
   internal::BindingState<Interface, ImplRefTraits>* internal_state() {
     return &internal_state_;
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 6c982267..78d5b13 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -12,12 +12,14 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
+#include "base/containers/queue.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 #include "mojo/public/cpp/system/core.h"
 #include "mojo/public/cpp/system/handle_signal_tracker.h"
@@ -103,6 +105,13 @@
     enforce_errors_from_incoming_receiver_ = enforce;
   }
 
+  // If set to |true|, this Connector will always dispatch messages to its
+  // receiver as soon as they're read off the pipe, rather than scheduling
+  // individual dispatch tasks for each message.
+  void set_force_immediate_dispatch(bool force) {
+    force_immediate_dispatch_ = force;
+  }
+
   // Sets the error handler to receive notifications when an error is
   // encountered while reading from the pipe or waiting to read from the pipe.
   void set_connection_error_handler(base::OnceClosure error_handler) {
@@ -168,14 +177,6 @@
   // SyncHandleWatcher::AllowWokenUpBySyncWatchOnSameThread().
   void AllowWokenUpBySyncWatchOnSameThread();
 
-  // Watches |message_pipe_| (as well as other handles registered to be watched
-  // together) synchronously.
-  // This method:
-  //   - returns true when |should_stop| is set to true;
-  //   - return false when any error occurs, including |message_pipe_| being
-  //     closed.
-  bool SyncWatch(const bool* should_stop);
-
   // Whether currently the control flow is inside the sync handle watcher
   // callback.
   // It always returns false after CloseMessagePipe()/PassMessagePipe().
@@ -204,15 +205,35 @@
   void OnWatcherHandleReady(MojoResult result);
   // Callback of SyncHandleWatcher.
   void OnSyncHandleWatcherHandleReady(MojoResult result);
+
   void OnHandleReadyInternal(MojoResult result);
 
   void WaitToReadMore();
 
-  // Returns false if it is impossible to receive more messages in the future.
-  // |this| may have been destroyed in that case.
-  WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
+  // Attempts to read a single Message from the pipe. Returns |MOJO_RESULT_OK|
+  // and a valid message in |*message| iff a message was successfully read and
+  // prepared for dispatch.
+  MojoResult ReadMessage(Message* message);
 
-  // |this| can be destroyed during message dispatch.
+  // Dispatches |message| to the receiver. Returns |true| if the message was
+  // accepted by the receiver, and |false| otherwise (e.g. if it failed
+  // validation).
+  bool DispatchMessage(Message message);
+
+  // Used to schedule dispatch of a single message from the front of
+  // |dispatch_queue_|. Returns |true| if the dispatch succeeded and |false|
+  // otherwise (e.g. if the message failed validation).
+  bool DispatchNextMessageInQueue();
+
+  // Dispatches all queued messages to the receiver immediately. This is
+  // necessary to ensure proper ordering when beginning to wait for a sync
+  // response, because new incoming messages need to be dispatched as they
+  // arrive. Returns |true| if all queued messages were successfully dispatched,
+  // and |false| if any dispatch fails.
+  bool DispatchAllQueuedMessages();
+
+  // Reads all available messages off of the pipe, possibly dispatching one or
+  // more of them depending on the state of the Connector when this is called.
   void ReadAllAvailableMessages();
 
   // If |force_pipe_reset| is true, this method replaces the existing
@@ -226,6 +247,13 @@
 
   void EnsureSyncWatcherExists();
 
+  // Indicates whether this Connector should immediately dispatch any message
+  // it reads off the pipe, rather than queuing and/or scheduling an
+  // asynchronous dispatch operation per message.
+  bool should_dispatch_messages_immediately() const {
+    return force_immediate_dispatch_ || during_sync_handle_watcher_callback();
+  }
+
   base::OnceClosure connection_error_handler_;
 
   ScopedMessagePipeHandle message_pipe_;
@@ -241,6 +269,21 @@
 
   bool paused_ = false;
 
+  // See |set_force_immediate_dispatch()|.
+  bool force_immediate_dispatch_ = false;
+
+  // Messages which have been read off the pipe but not yet dispatched. This
+  // exists so that we can schedule individual dispatch tasks for each read
+  // message in parallel rather than having to do it in series as each message
+  // is read off the pipe.
+  base::queue<Message> dispatch_queue_;
+
+  // Indicates whether a non-fatal pipe error (i.e. peer closure and no more
+  // incoming messages) was detected while |dispatch_queue_| was non-empty.
+  // When |true|, ensures that an error will be propagated outward as soon as
+  // |dispatch_queue_| is fully flushed.
+  bool pending_error_dispatch_ = false;
+
   OutgoingSerializationMode outgoing_serialization_mode_;
   IncomingSerializationMode incoming_serialization_mode_;
 
@@ -249,6 +292,8 @@
   base::Optional<base::Lock> lock_;
 
   std::unique_ptr<SyncHandleWatcher> sync_watcher_;
+  std::unique_ptr<SequenceLocalSyncEventWatcher> dispatch_queue_watcher_;
+
   bool allow_woken_up_by_others_ = false;
   // If non-zero, currently the control flow is inside the sync handle watcher
   // callback.
diff --git a/mojo/public/cpp/bindings/lib/binding_state.cc b/mojo/public/cpp/bindings/lib/binding_state.cc
index 08278a9..edd5f1e 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.cc
+++ b/mojo/public/cpp/bindings/lib/binding_state.cc
@@ -75,6 +75,11 @@
   endpoint_client_->FlushForTesting();
 }
 
+void BindingStateBase::EnableBatchDispatch() {
+  DCHECK(is_bound());
+  router_->EnableBatchDispatch();
+}
+
 void BindingStateBase::EnableTestingMode() {
   DCHECK(is_bound());
   router_->EnableTestingMode();
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index 49cda59..cc2ac0c 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -76,6 +76,8 @@
 
   void FlushForTesting();
 
+  void EnableBatchDispatch();
+
   void EnableTestingMode();
 
   scoped_refptr<internal::MultiplexRouter> RouterForTesting();
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 0ef7039..543eee2 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -224,6 +224,9 @@
   // INDEFINITE deadlines at present, so we only support those.
   DCHECK(deadline == 0 || deadline == MOJO_DEADLINE_INDEFINITE);
 
+  if (!dispatch_queue_.empty())
+    return DispatchNextMessageInQueue();
+
   MojoResult rv = MOJO_RESULT_UNKNOWN;
   if (deadline == 0 && !message_pipe_->QuerySignalsState().readable())
     return false;
@@ -233,13 +236,21 @@
     if (rv != MOJO_RESULT_OK) {
       // Users that call WaitForIncomingMessage() should expect their code to be
       // re-entered, so we call the error handler synchronously.
-      HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
+      HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION /* force_pipe_reset */,
+                  false /* force_async_handler */);
       return false;
     }
   }
 
-  ignore_result(ReadSingleMessage(&rv));
-  return (rv == MOJO_RESULT_OK);
+  Message message;
+  if ((rv = ReadMessage(&message)) != MOJO_RESULT_OK) {
+    HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION /* force_pipe_reset */,
+                false /* force_async_handler */);
+    return false;
+  }
+
+  DCHECK(!message.IsNull());
+  return DispatchMessage(std::move(message));
 }
 
 void Connector::PauseIncomingMethodCallProcessing() {
@@ -258,6 +269,24 @@
   if (!paused_)
     return;
 
+  // Some number of queued dispatch tasks may have been aborted due to the
+  // Connector being paused at task execution time. We either dispatch them all
+  // now (if immediate dispatch is enabled) or schedule new tasks for each of
+  // them. Some of the scheduled tasks may be redundant, but that's OK.
+  if (should_dispatch_messages_immediately()) {
+    base::WeakPtr<Connector> weak_self = weak_self_;
+    DispatchAllQueuedMessages();
+    if (!weak_self)
+      return;
+  } else {
+    for (size_t i = 0; i < dispatch_queue_.size(); ++i) {
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(base::IgnoreResult(
+                                        &Connector::DispatchNextMessageInQueue),
+                                    weak_self_));
+    }
+  }
+
   paused_ = false;
   WaitToReadMore();
 }
@@ -331,18 +360,7 @@
 
   EnsureSyncWatcherExists();
   sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
-}
-
-bool Connector::SyncWatch(const bool* should_stop) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (error_)
-    return false;
-
-  ResumeIncomingMethodCallProcessing();
-
-  EnsureSyncWatcherExists();
-  return sync_watcher_->SyncWatch(should_stop);
+  dispatch_queue_watcher_->AllowWokenUpBySyncWatchOnSameSequence();
 }
 
 void Connector::SetWatcherHeapProfilerTag(const char* tag) {
@@ -380,11 +398,27 @@
 void Connector::OnHandleReadyInternal(MojoResult result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (result != MOJO_RESULT_OK) {
-    HandleError(result != MOJO_RESULT_FAILED_PRECONDITION, false);
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // No more messages on the pipe and the peer is closed.
+    if (dispatch_queue_.empty()) {
+      HandleError(false /* force_pipe_reset */,
+                  false /* force_async_handler */);
+      return;
+    } else {
+      // We don't want to propagate an error signal yet because we still have
+      // queued messages to dispatch.
+      pending_error_dispatch_ = true;
+    }
+  } else if (result != MOJO_RESULT_OK) {
+    // Some other fatal error condition was encountered. We can propagate this
+    // immediately.
+    HandleError(true /* force_pipe_reset */, false /* force_async_handler */);
     return;
   }
 
+  if (dispatch_queue_watcher_)
+    dispatch_queue_watcher_->ResetEvent();
+
   ReadAllAvailableMessages();
   // At this point, this object might have been deleted. Return.
 }
@@ -419,116 +453,168 @@
   if (allow_woken_up_by_others_) {
     EnsureSyncWatcherExists();
     sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
+    dispatch_queue_watcher_->AllowWokenUpBySyncWatchOnSameSequence();
   }
 }
 
-bool Connector::ReadSingleMessage(MojoResult* read_result) {
-  CHECK(!paused_);
+MojoResult Connector::ReadMessage(Message* message) {
+  ScopedMessageHandle handle;
+  MojoResult result =
+      ReadMessageNew(message_pipe_.get(), &handle, MOJO_READ_MESSAGE_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return result;
 
-  bool receiver_result = false;
+  *message = Message::CreateFromMessageHandle(&handle);
+  if (message->IsNull()) {
+    // Even if the read was successful, the Message may still be null if there
+    // was a problem extracting handles from it. We treat this essentially as
+    // a bad IPC because we don't really have a better option.
+    //
+    // We include |heap_profiler_tag_| in the error message since it usually
+    // (via this Connector's owner) provides useful information about which
+    // binding interface is using this Connector.
+    NotifyBadMessage(handle.get(),
+                     std::string(heap_profiler_tag_) +
+                         "One or more handle attachments were invalid.");
+    return MOJO_RESULT_ABORTED;
+  }
 
-  // Detect if |this| was destroyed or the message pipe was closed/transferred
-  // during message dispatch.
+  return MOJO_RESULT_OK;
+}
+
+bool Connector::DispatchMessage(Message message) {
+  DCHECK(!paused_);
+
   base::WeakPtr<Connector> weak_self = weak_self_;
+  base::Optional<ActiveDispatchTracker> dispatch_tracker;
+  if (!is_dispatching_ && nesting_observer_) {
+    is_dispatching_ = true;
+    dispatch_tracker.emplace(weak_self);
+  }
 
-  ScopedMessageHandle message_handle;
-  const MojoResult rv = ReadMessageNew(message_pipe_.get(), &message_handle,
-                                       MOJO_READ_MESSAGE_FLAG_NONE);
-  *read_result = rv;
-
-  if (rv == MOJO_RESULT_OK) {
-    Message message = Message::CreateFromMessageHandle(&message_handle);
-    if (message.IsNull()) {
-      // Even if the read was successful, the Message may still be null if there
-      // was a problem extracting handles from it. We treat this essentially as
-      // a bad IPC because we don't really have a better option.
-      //
-      // We include |heap_profiler_tag_| in the error message since it usually
-      // (via this Connector's owner) provides useful information about which
-      // binding interface is using this Connector.
-      NotifyBadMessage(message_handle.get(),
-                       std::string(heap_profiler_tag_) +
-                           "One or more handle attachments were invalid.");
-      HandleError(false /* force_pipe_reset */,
-                  false /* force_async_handler */);
-      return false;
-    }
-
-    base::Optional<ActiveDispatchTracker> dispatch_tracker;
-    if (!is_dispatching_ && nesting_observer_) {
-      is_dispatching_ = true;
-      dispatch_tracker.emplace(weak_self);
-    }
-
-    if (incoming_serialization_mode_ ==
-        IncomingSerializationMode::kSerializeBeforeDispatchForTesting) {
-      message.SerializeIfNecessary();
-    } else {
-      DCHECK_EQ(IncomingSerializationMode::kDispatchAsIs,
-                incoming_serialization_mode_);
-    }
+  if (incoming_serialization_mode_ ==
+      IncomingSerializationMode::kSerializeBeforeDispatchForTesting) {
+    message.SerializeIfNecessary();
+  } else {
+    DCHECK_EQ(IncomingSerializationMode::kDispatchAsIs,
+              incoming_serialization_mode_);
+  }
 
 #if !BUILDFLAG(MOJO_TRACE_ENABLED)
-    // This emits just full class name, and is inferior to mojo tracing.
-    TRACE_EVENT0("mojom", heap_profiler_tag_);
+  // This emits just full class name, and is inferior to mojo tracing.
+  TRACE_EVENT0("mojom", heap_profiler_tag_);
 #endif
 
-    receiver_result =
-        incoming_receiver_ && incoming_receiver_->Accept(&message);
+  bool receiver_result =
+      incoming_receiver_ && incoming_receiver_->Accept(&message);
+  if (!weak_self)
+    return receiver_result;
 
-    if (!weak_self)
-      return false;
-
-    if (dispatch_tracker) {
-      is_dispatching_ = false;
-      dispatch_tracker.reset();
-    }
-  } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
-    return true;
-  } else {
-    HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
-    return false;
+  if (dispatch_tracker) {
+    is_dispatching_ = false;
+    dispatch_tracker.reset();
   }
 
   if (enforce_errors_from_incoming_receiver_ && !receiver_result) {
-    HandleError(true, false);
+    HandleError(true /* force_pipe_reset */, false /* force_async_handler */);
     return false;
   }
+
+  return true;
+}
+
+bool Connector::DispatchNextMessageInQueue() {
+  if (error_ || paused_)
+    return false;
+
+  if (dispatch_queue_.empty())
+    return true;
+
+  Message message = std::move(dispatch_queue_.front());
+  dispatch_queue_.pop();
+
+  base::WeakPtr<Connector> weak_self = weak_self_;
+
+  // NOTE: May delete |this|.
+  bool result = DispatchMessage(std::move(message));
+  if (weak_self) {
+    // If that was our last queued message and we've detected a pipe error, we
+    // can propagate it now.
+    if (dispatch_queue_.empty() && pending_error_dispatch_) {
+      HandleError(false /* force_pipe_reset */,
+                  false /* force_async_handler */);
+    }
+  }
+
+  return result;
+}
+
+bool Connector::DispatchAllQueuedMessages() {
+  base::WeakPtr<Connector> weak_self = weak_self_;
+  while (weak_self && !dispatch_queue_.empty()) {
+    if (!DispatchNextMessageInQueue())
+      return false;
+  }
+
   return true;
 }
 
 void Connector::ReadAllAvailableMessages() {
-  while (!error_) {
-    base::WeakPtr<Connector> weak_self = weak_self_;
-    MojoResult rv;
-
-    // May delete |this.|
-    if (!ReadSingleMessage(&rv))
+  base::WeakPtr<Connector> weak_self = weak_self_;
+  if (should_dispatch_messages_immediately()) {
+    // If we're dispatching messages immediately, we have to ensure that the
+    // pending dispatch queue is flushed before we started reading and
+    // dispatching messages fresh off the pipe. Otherwise messages would get
+    // reordered.
+    if (!DispatchAllQueuedMessages() || !weak_self)
       return;
+  }
 
-    if (!weak_self || paused_)
-      return;
+  // Flush all messages from the pipe.
+  Message message;
+  MojoResult rv;
+  bool first_message_in_batch = dispatch_queue_.empty();
+  while ((rv = ReadMessage(&message)) == MOJO_RESULT_OK) {
+    DCHECK(!message.IsNull());
 
-    DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_SHOULD_WAIT);
-
-    if (rv == MOJO_RESULT_SHOULD_WAIT) {
-      // Attempt to re-arm the Watcher.
-      MojoResult ready_result;
-      MojoResult arm_result = handle_watcher_->Arm(&ready_result);
-      if (arm_result == MOJO_RESULT_OK)
+    if (first_message_in_batch || should_dispatch_messages_immediately()) {
+      // Dispatch immediately if this is the first available message or if
+      // immediate dispatch is currently enabled for whatever reason.
+      DCHECK(dispatch_queue_.empty());
+      if (!DispatchMessage(std::move(message)) || !weak_self || paused_)
         return;
-
-      // The watcher is already ready to notify again.
-      DCHECK_EQ(MOJO_RESULT_FAILED_PRECONDITION, arm_result);
-
-      if (ready_result == MOJO_RESULT_FAILED_PRECONDITION) {
-        HandleError(false, false);
-        return;
-      }
-
-      // There's more to read now, so we'll just keep looping.
-      DCHECK_EQ(MOJO_RESULT_OK, ready_result);
+    } else {
+      dispatch_queue_.push(std::move(message));
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(base::IgnoreResult(
+                                        &Connector::DispatchNextMessageInQueue),
+                                    weak_self_));
     }
+
+    first_message_in_batch = false;
+  }
+
+  if (!dispatch_queue_.empty() && dispatch_queue_watcher_)
+    dispatch_queue_watcher_->SignalEvent();
+
+  if (rv == MOJO_RESULT_SHOULD_WAIT) {
+    // We're done only because there are no more messages to read, so go back to
+    // watching the pipe for more.
+    handle_watcher_->ArmOrNotify();
+    return;
+  }
+
+  if (rv != MOJO_RESULT_FAILED_PRECONDITION) {
+    // A fatal error occurred on the pipe, handle it immediately.
+    HandleError(true /* force_pipe_reset */, false /* force_async_handler */);
+  } else if (dispatch_queue_.empty()) {
+    // The peer endpoint was closed and there are no more messages to read, and
+    // our dispatch queue is empty. We can signal an error right away.
+    HandleError(false /* force_pipe_reset */, false /* force_async_handler */);
+  } else {
+    // Peer closed but we still have messages to dispatch. Defer error
+    // propagation.
+    pending_error_dispatch_ = true;
   }
 }
 
@@ -536,6 +622,7 @@
   peer_remoteness_tracker_.reset();
   handle_watcher_.reset();
   sync_watcher_.reset();
+  dispatch_queue_watcher_.reset();
 }
 
 void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) {
@@ -577,8 +664,13 @@
     return;
   sync_watcher_.reset(new SyncHandleWatcher(
       message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
-      base::Bind(&Connector::OnSyncHandleWatcherHandleReady,
-                 base::Unretained(this))));
+      base::BindRepeating(&Connector::OnSyncHandleWatcherHandleReady,
+                          base::Unretained(this))));
+  dispatch_queue_watcher_ = std::make_unique<SequenceLocalSyncEventWatcher>(
+      base::BindRepeating(&Connector::OnSyncHandleWatcherHandleReady,
+                          base::Unretained(this), MOJO_RESULT_OK));
+  if (!dispatch_queue_.empty())
+    dispatch_queue_watcher_->SignalEvent();
 }
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 6a1c58b..3fc58449c 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -552,6 +552,10 @@
   return !base::ContainsKey(endpoints_, kMasterInterfaceId);
 }
 
+void MultiplexRouter::EnableBatchDispatch() {
+  connector_.set_force_immediate_dispatch(true);
+}
+
 void MultiplexRouter::EnableTestingMode() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   MayAutoLock locker(&lock_);
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index a8dc8ab..cca9e18 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -136,6 +136,9 @@
   // Whether there are any associated interfaces running currently.
   bool HasAssociatedEndpoints() const;
 
+  // See comments on Binding::EnableBatchDispatch().
+  void EnableBatchDispatch();
+
   // Sets this object to testing mode.
   // In testing mode, the object doesn't disconnect the underlying message pipe
   // when it receives unexpected or invalid messages.
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
index 3312a37..8bde2beb 100644
--- a/mojo/public/cpp/test_support/BUILD.gn
+++ b/mojo/public/cpp/test_support/BUILD.gn
@@ -12,6 +12,7 @@
   ]
 
   deps = [
+    "//mojo/core/embedder",
     "//mojo/public/c/test_support",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
diff --git a/mojo/public/cpp/test_support/lib/DEPS b/mojo/public/cpp/test_support/lib/DEPS
new file mode 100644
index 0000000..847e0d7
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  # Implementation of mojo/*public*/cpp/test_support/*lib* can depend on
+  # non-public parts of mojo.
+  "+mojo"
+]
diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc
index c876f42..10a899e 100644
--- a/mojo/public/cpp/test_support/lib/test_utils.cc
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -9,6 +9,7 @@
 
 #include <vector>
 
+#include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/system/core.h"
 #include "mojo/public/cpp/system/wait.h"
 #include "mojo/public/cpp/test_support/test_support.h"
@@ -77,5 +78,30 @@
                                "iterations/second");
 }
 
+BadMessageObserver::BadMessageObserver() : got_bad_message_(false) {
+  mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
+      &BadMessageObserver::OnReportBadMessage, base::Unretained(this)));
+}
+
+BadMessageObserver::~BadMessageObserver() {
+  mojo::core::SetDefaultProcessErrorCallback(
+      mojo::core::ProcessErrorCallback());
+}
+
+std::string BadMessageObserver::WaitForBadMessage() {
+  if (!got_bad_message_)
+    run_loop_.Run();
+  return last_error_for_bad_message_;
+}
+
+void BadMessageObserver::OnReportBadMessage(const std::string& message) {
+  if (got_bad_message_)
+    return;
+
+  last_error_for_bad_message_ = message;
+  got_bad_message_ = true;
+  run_loop_.Quit();
+}
+
 }  // namespace test
 }  // namespace mojo
diff --git a/mojo/public/cpp/test_support/test_utils.h b/mojo/public/cpp/test_support/test_utils.h
index 68d20f5a..54651c5d 100644
--- a/mojo/public/cpp/test_support/test_utils.h
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -6,10 +6,12 @@
 #define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
 
 #include <string>
+#include <utility>
 
-#include "mojo/public/cpp/system/core.h"
-
+#include "base/macros.h"
+#include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
 namespace test {
@@ -50,6 +52,28 @@
                           PerfTestSingleIteration single_iteration,
                           void* closure);
 
+// Intercepts a single bad message (reported via mojo::ReportBadMessage or
+// mojo::GetBadMessageCallback) that would be associated with the global bad
+// message handler (typically when the messages originate from a test
+// implementation of an interface hosted in the test process).
+class BadMessageObserver {
+ public:
+  BadMessageObserver();
+  ~BadMessageObserver();
+
+  // Waits for the bad message and returns the error string.
+  std::string WaitForBadMessage();
+
+ private:
+  void OnReportBadMessage(const std::string& message);
+
+  std::string last_error_for_bad_message_;
+  bool got_bad_message_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(BadMessageObserver);
+};
+
 }  // namespace test
 }  // namespace mojo
 
diff --git a/mojo/public/tools/bindings/generators/js_templates/lite/test/test.js b/mojo/public/tools/bindings/generators/js_templates/lite/test/test.js
index f358bf46..c0a2eba 100644
--- a/mojo/public/tools/bindings/generators/js_templates/lite/test/test.js
+++ b/mojo/public/tools/bindings/generators/js_templates/lite/test/test.js
@@ -6,16 +6,10 @@
   /** @type {test.mojom.TestPageHandlerProxy} */
   let proxy = test.mojom.TestPageHandler.getProxy()
 
-  /**
-   * @type {{values: !Array<!string>}}
-   * This type is not enforced, as Closure does not handle Promise return
-   * typing.
-   */
+  // Type infers {?{values: !Array<!string>}} from Promise return type.
   let result = await proxy.method1();
-  /**
-   * @type {Array<string>}
-   * This type is enforced because of the above cast.
-   */
+
+  /** @type {Array<string>} */
   let values = result.values;
 }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 81ad337..4b28cb9a 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1045,6 +1045,8 @@
       "socket/socks_client_socket.h",
       "socket/socks_client_socket_pool.cc",
       "socket/socks_client_socket_pool.h",
+      "socket/socks_connect_job.cc",
+      "socket/socks_connect_job.h",
       "socket/ssl_client_socket_pool.cc",
       "socket/ssl_client_socket_pool.h",
       "socket/ssl_server_socket.h",
@@ -5021,6 +5023,7 @@
     "socket/socks5_client_socket_unittest.cc",
     "socket/socks_client_socket_pool_unittest.cc",
     "socket/socks_client_socket_unittest.cc",
+    "socket/socks_connect_job_unittest.cc",
     "socket/ssl_client_socket_pool_unittest.cc",
     "socket/ssl_client_socket_unittest.cc",
     "socket/ssl_server_socket_unittest.cc",
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index fa35c0f..3c20ccf5 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -2323,8 +2323,9 @@
   if (!callback_.is_null()) {
     base::ResetAndReturn(&callback_).Run(net_error);
   }
+
   CloseAllStreams(net_error);
-  CloseAllHandles(net_error);
+
   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_CLOSE_ON_ERROR,
                     NetLog::IntCallback("net_error", net_error));
 
@@ -2332,6 +2333,7 @@
     connection()->CloseConnection(quic_error, "net error", behavior);
   DCHECK(!connection()->connected());
 
+  CloseAllHandles(net_error);
   NotifyFactoryOfSessionClosed();
 }
 
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 28c79bf..eee4128 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -405,7 +405,8 @@
   details->connection_info =
       ConnectionInfoFromQuicVersion(quic_session()->GetQuicVersion());
   quic_session()->PopulateNetErrorDetails(details);
-  if (quic_session()->IsCryptoHandshakeConfirmed() && stream_)
+  if (quic_session()->IsCryptoHandshakeConfirmed() && stream_ &&
+      stream_->connection_error() != quic::QUIC_NO_ERROR)
     details->quic_connection_error = stream_->connection_error();
 }
 
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index f533f74..fae3d65c 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -495,6 +495,19 @@
         packet_number, stream_id, should_include_version, fin, offset, data);
   }
 
+  std::unique_ptr<quic::QuicEncryptedPacket>
+  ConstructClientMultipleDataFramesPacket(
+      quic::QuicPacketNumber packet_number,
+      quic::QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      quic::QuicStreamOffset offset,
+      const std::vector<std::string> data_writes) {
+    return client_maker_.MakeMultipleDataFramesPacket(packet_number, stream_id,
+                                                      should_include_version,
+                                                      fin, offset, data_writes);
+  }
+
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckAndDataPacket(
       quic::QuicPacketNumber packet_number,
       bool include_version,
@@ -510,6 +523,22 @@
         smallest_received, least_unacked, fin, offset, data);
   }
 
+  std::unique_ptr<quic::QuicEncryptedPacket>
+  ConstructClientAckAndMultipleDataFramesPacket(
+      quic::QuicPacketNumber packet_number,
+      bool include_version,
+      quic::QuicStreamId stream_id,
+      quic::QuicPacketNumber largest_received,
+      quic::QuicPacketNumber smallest_received,
+      quic::QuicPacketNumber least_unacked,
+      bool fin,
+      quic::QuicStreamOffset offset,
+      const std::vector<std::string> data_writes) {
+    return client_maker_.MakeAckAndMultipleDataFramesPacket(
+        packet_number, include_version, stream_id, largest_received,
+        smallest_received, least_unacked, fin, offset, data_writes);
+  }
+
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientForceHolDataPacket(
       quic::QuicPacketNumber packet_number,
       quic::QuicStreamId stream_id,
@@ -7563,16 +7592,13 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  int write_packet_index = 1;
   mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructInitialSettingsPacket(write_packet_index++,
-                                                  &header_stream_offset));
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
   mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          true, false, ConnectRequestHeaders("mail.example.org:443"),
-          &header_stream_offset));
+      SYNCHRONOUS, ConstructClientRequestHeadersPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       false, ConnectRequestHeaders("mail.example.org:443"),
+                       &header_stream_offset));
   mock_quic_data.AddRead(
       ASYNC, ConstructServerResponseHeadersPacket(
                  1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
@@ -7585,21 +7611,16 @@
   quic::QuicString header = ConstructDataHeader(strlen(get_request));
   if (version_ != quic::QUIC_VERSION_99) {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, 0, quic::QuicStringPiece(get_request)));
+        SYNCHRONOUS,
+        ConstructClientAckAndDataPacket(
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0, quic::QuicStringPiece(get_request)));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, 0, header));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(
-            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-            false, false, header.length(), quic::QuicStringPiece(get_request)));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0, {header, quic::QuicString(get_request)}));
   }
 
   const char get_response[] =
@@ -7616,15 +7637,14 @@
                        3, GetNthClientInitiatedBidirectionalStreamId(0), false,
                        false, strlen(get_response) + header2.length(),
                        header3 + quic::QuicString("0123456789")));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 3, 2, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckPacket(4, 3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
-      ConstructClientRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          quic::QUIC_STREAM_CANCELLED, strlen(get_request) + header.length()));
+      ConstructClientRstPacket(5, GetNthClientInitiatedBidirectionalStreamId(0),
+                               quic::QUIC_STREAM_CANCELLED,
+                               strlen(get_request) + header.length()));
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -7664,16 +7684,13 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  int write_packet_index = 1;
   mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructInitialSettingsPacket(write_packet_index++,
-                                                  &header_stream_offset));
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
   mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          true, false, ConnectRequestHeaders("mail.example.org:443"),
-          &header_stream_offset));
+      SYNCHRONOUS, ConstructClientRequestHeadersPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       false, ConnectRequestHeaders("mail.example.org:443"),
+                       &header_stream_offset));
   mock_quic_data.AddRead(
       ASYNC, ConstructServerResponseHeadersPacket(
                  1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
@@ -7688,21 +7705,16 @@
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientAckAndDataPacket(
-            write_packet_index++, false,
-            GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1, false, 0,
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0,
             quic::QuicStringPiece(get_frame.data(), get_frame.size())));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, 0, header));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(
-            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-            false, false, header.length(),
-            quic::QuicStringPiece(get_frame.data(), get_frame.size())));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0,
+            {header, quic::QuicString(get_frame.data(), get_frame.size())}));
   }
   spdy::SpdySerializedFrame resp_frame =
       spdy_util.ConstructSpdyGetReply(nullptr, 0, 1);
@@ -7722,15 +7734,14 @@
           3, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
           resp_frame.size() + header2.length(),
           header3 + quic::QuicString(data_frame.data(), data_frame.size())));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 3, 2, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckPacket(4, 3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
-      ConstructClientRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          quic::QUIC_STREAM_CANCELLED, get_frame.size() + header.length()));
+      ConstructClientRstPacket(5, GetNthClientInitiatedBidirectionalStreamId(0),
+                               quic::QUIC_STREAM_CANCELLED,
+                               get_frame.size() + header.length()));
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -7803,16 +7814,11 @@
             client_data_offset, quic::QuicStringPiece(get_request_1)));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, client_data_offset, header));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(write_packet_index++,
-                                  GetNthClientInitiatedBidirectionalStreamId(0),
-                                  false, false, header.length(),
-                                  quic::QuicStringPiece(get_request_1)));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            write_packet_index++, false,
+            GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1, false,
+            client_data_offset, {header, quic::QuicString(get_request_1)}));
   }
 
   client_data_offset += strlen(get_request_1) + header.length();
@@ -7847,18 +7853,20 @@
   if (version_ == quic::QUIC_VERSION_99) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
+        ConstructClientMultipleDataFramesPacket(
+            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
+            false, false, client_data_offset,
+            {header4, quic::QuicString(get_request_2)}));
+    client_data_offset += header4.length() + strlen(get_request_2);
+  } else {
+    mock_quic_data.AddWrite(
+        SYNCHRONOUS,
         ConstructClientDataPacket(write_packet_index++,
                                   GetNthClientInitiatedBidirectionalStreamId(0),
-                                  false, false, client_data_offset, header4));
-    client_data_offset += header4.length();
+                                  false, false, client_data_offset,
+                                  quic::QuicStringPiece(get_request_2)));
+    client_data_offset += strlen(get_request_2);
   }
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientDataPacket(write_packet_index++,
-                                GetNthClientInitiatedBidirectionalStreamId(0),
-                                false, false, client_data_offset,
-                                quic::QuicStringPiece(get_request_2)));
-  client_data_offset += strlen(get_request_2);
 
   const char get_response_2[] =
       "HTTP/1.1 200 OK\r\n"
@@ -7942,18 +7950,15 @@
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
-  int write_packet_index = 1;
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructInitialSettingsPacket(
-                       write_packet_index++, &client_header_stream_offset));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
+                                           1, &client_header_stream_offset));
 
   // CONNECT request and response for first request
   mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          true, false, ConnectRequestHeaders("mail.example.org:443"),
-          &client_header_stream_offset));
+      SYNCHRONOUS, ConstructClientRequestHeadersPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       false, ConnectRequestHeaders("mail.example.org:443"),
+                       &client_header_stream_offset));
   mock_quic_data.AddRead(
       ASYNC, ConstructServerResponseHeadersPacket(
                  1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
@@ -7967,21 +7972,16 @@
   quic::QuicString header = ConstructDataHeader(strlen(get_request));
   if (version_ != quic::QUIC_VERSION_99) {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, 0, quic::QuicStringPiece(get_request)));
+        SYNCHRONOUS,
+        ConstructClientAckAndDataPacket(
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0, quic::QuicStringPiece(get_request)));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
-                         false, 0, header));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(
-            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-            false, false, header.length(), quic::QuicStringPiece(get_request)));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+            false, 0, {header, quic::QuicString(get_request)}));
   }
 
   const char get_response[] =
@@ -7998,15 +7998,14 @@
                        3, GetNthClientInitiatedBidirectionalStreamId(0), false,
                        false, strlen(get_response) + header2.length(),
                        header3 + quic::QuicString("0123456789")));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 3, 2, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckPacket(4, 3, 2, 1));
 
   // CONNECT request and response for second request
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-          false, false, ConnectRequestHeaders("different.example.org:443"),
+          5, GetNthClientInitiatedBidirectionalStreamId(1), false, false,
+          ConnectRequestHeaders("different.example.org:443"),
           GetNthClientInitiatedBidirectionalStreamId(0),
           &client_header_stream_offset));
   mock_quic_data.AddRead(
@@ -8023,21 +8022,16 @@
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientAckAndDataPacket(
-            write_packet_index++, false,
-            GetNthClientInitiatedBidirectionalStreamId(1), 4, 4, 1, false, 0,
+            6, false, GetNthClientInitiatedBidirectionalStreamId(1), 4, 4, 1,
+            false, 0,
             quic::QuicStringPiece(get_frame.data(), get_frame.size())));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(1), 4, 4, 1,
-                         false, 0, header4));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(
-            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-            false, false, header4.length(),
-            quic::QuicStringPiece(get_frame.data(), get_frame.size())));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            6, false, GetNthClientInitiatedBidirectionalStreamId(1), 4, 4, 1,
+            false, 0,
+            {header4, quic::QuicString(get_frame.data(), get_frame.size())}));
   }
 
   spdy::SpdySerializedFrame resp_frame =
@@ -8059,20 +8053,19 @@
           resp_frame.size() + header5.length(),
           header6 + quic::QuicString(data_frame.data(), data_frame.size())));
 
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 6, 5, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckPacket(7, 6, 5, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
-      ConstructClientRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          quic::QUIC_STREAM_CANCELLED, strlen(get_request) + header.length()));
+      ConstructClientRstPacket(8, GetNthClientInitiatedBidirectionalStreamId(0),
+                               quic::QUIC_STREAM_CANCELLED,
+                               strlen(get_request) + header.length()));
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
-      ConstructClientRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-          quic::QUIC_STREAM_CANCELLED, get_frame.size() + header4.length()));
+      ConstructClientRstPacket(9, GetNthClientInitiatedBidirectionalStreamId(1),
+                               quic::QUIC_STREAM_CANCELLED,
+                               get_frame.size() + header4.length()));
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -8215,33 +8208,28 @@
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
-  int write_packet_index = 1;
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(
+                                           1, &client_header_stream_offset));
   mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructInitialSettingsPacket(
-                       write_packet_index++, &client_header_stream_offset));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          true, false, ConnectRequestHeaders("mail.example.org:443"),
-          &client_header_stream_offset));
+      SYNCHRONOUS, ConstructClientRequestHeadersPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       false, ConnectRequestHeaders("mail.example.org:443"),
+                       &client_header_stream_offset));
   mock_quic_data.AddRead(
       ASYNC, ConstructServerResponseHeadersPacket(
                  1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
                  GetResponseHeaders("200 OK"), &server_header_stream_offset));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientAckAndRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0),
-          quic::QUIC_STREAM_CANCELLED, 1, 1, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS,
+                          ConstructClientAckAndRstPacket(
+                              3, GetNthClientInitiatedBidirectionalStreamId(0),
+                              quic::QUIC_STREAM_CANCELLED, 1, 1, 1));
 
   mock_quic_data.AddWrite(
-      SYNCHRONOUS,
-      ConstructClientRequestHeadersPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-          false, false, ConnectRequestHeaders("mail.example.org:443"),
-          GetNthClientInitiatedBidirectionalStreamId(0),
-          &client_header_stream_offset));
+      SYNCHRONOUS, ConstructClientRequestHeadersPacket(
+                       4, GetNthClientInitiatedBidirectionalStreamId(1), false,
+                       false, ConnectRequestHeaders("mail.example.org:443"),
+                       GetNthClientInitiatedBidirectionalStreamId(0),
+                       &client_header_stream_offset));
   mock_quic_data.AddRead(
       ASYNC, ConstructServerResponseHeadersPacket(
                  2, GetNthClientInitiatedBidirectionalStreamId(1), false, false,
@@ -8254,21 +8242,16 @@
   quic::QuicString header = ConstructDataHeader(strlen(get_request));
   if (version_ != quic::QUIC_VERSION_99) {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(1), 2, 2, 1,
-                         false, 0, quic::QuicStringPiece(get_request)));
+        SYNCHRONOUS,
+        ConstructClientAckAndDataPacket(
+            5, false, GetNthClientInitiatedBidirectionalStreamId(1), 2, 2, 1,
+            false, 0, quic::QuicStringPiece(get_request)));
   } else {
     mock_quic_data.AddWrite(
-        SYNCHRONOUS, ConstructClientAckAndDataPacket(
-                         write_packet_index++, false,
-                         GetNthClientInitiatedBidirectionalStreamId(1), 2, 2, 1,
-                         false, 0, header));
-    mock_quic_data.AddWrite(
         SYNCHRONOUS,
-        ConstructClientDataPacket(
-            write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-            false, false, header.length(), quic::QuicStringPiece(get_request)));
+        ConstructClientAckAndMultipleDataFramesPacket(
+            5, false, GetNthClientInitiatedBidirectionalStreamId(1), 2, 2, 1,
+            false, 0, {header, quic::QuicString(get_request)}));
   }
   const char get_response[] =
       "HTTP/1.1 200 OK\r\n"
@@ -8285,15 +8268,14 @@
                        4, GetNthClientInitiatedBidirectionalStreamId(1), false,
                        false, strlen(get_response) + header2.length(),
                        header3 + quic::QuicString("0123456789")));
-  mock_quic_data.AddWrite(
-      SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 4, 3, 1));
+  mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckPacket(6, 4, 3, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
-      ConstructClientRstPacket(
-          write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(1),
-          quic::QUIC_STREAM_CANCELLED, strlen(get_request) + header.length()));
+      ConstructClientRstPacket(7, GetNthClientInitiatedBidirectionalStreamId(1),
+                               quic::QUIC_STREAM_CANCELLED,
+                               strlen(get_request) + header.length()));
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 66fd2c9c..bafdfb3 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -328,6 +328,15 @@
                                         !kIncludeVersion, !kFin, offset, data);
   }
 
+  std::unique_ptr<quic::QuicReceivedPacket> ConstructMultipleDataFramesPacket(
+      quic::QuicPacketNumber packet_number,
+      quic::QuicStreamOffset offset,
+      const std::vector<std::string> data_writes) {
+    return client_maker_.MakeMultipleDataFramesPacket(
+        packet_number, client_data_stream_id1_, !kIncludeVersion, !kFin, offset,
+        data_writes);
+  }
+
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndDataPacket(
       quic::QuicPacketNumber packet_number,
       quic::QuicPacketNumber largest_received,
@@ -341,6 +350,20 @@
         data);
   }
 
+  std::unique_ptr<quic::QuicReceivedPacket>
+  ConstructAckAndMultipleDataFramesPacket(
+      quic::QuicPacketNumber packet_number,
+      quic::QuicPacketNumber largest_received,
+      quic::QuicPacketNumber smallest_received,
+      quic::QuicPacketNumber least_unacked,
+      quic::QuicStreamOffset offset,
+      const std::vector<std::string> data_writes) {
+    return client_maker_.MakeAckAndMultipleDataFramesPacket(
+        packet_number, !kIncludeVersion, client_data_stream_id1_,
+        largest_received, smallest_received, least_unacked, !kFin, offset,
+        data_writes);
+  }
+
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckPacket(
       quic::QuicPacketNumber packet_number,
       quic::QuicPacketNumber largest_received,
@@ -803,21 +826,18 @@
   mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   if (version_ == quic::QUIC_VERSION_99) {
     quic::QuicString header = ConstructDataHeader(kLen1);
-    mock_quic_data_.AddWrite(SYNCHRONOUS,
-                             ConstructAckAndDataPacket(3, 1, 1, 1, 0, header));
     mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructDataPacket(4, header.length(),
-                                         quic::QuicString(kMsg1, kLen1)));
+        SYNCHRONOUS,
+        ConstructAckAndMultipleDataFramesPacket(
+            3, 1, 1, 1, 0, {header, quic::QuicString(kMsg1, kLen1)}));
     quic::QuicString header2 = ConstructDataHeader(kLen2);
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructDataPacket(5, kLen1 + header.length(), header2));
+    mock_quic_data_.AddWrite(SYNCHRONOUS,
+                             ConstructMultipleDataFramesPacket(
+                                 4, kLen1 + header.length(),
+                                 {header2, quic::QuicString(kMsg2, kLen2)}));
     mock_quic_data_.AddWrite(
         SYNCHRONOUS,
-        ConstructDataPacket(6, kLen1 + header.length() + header2.length(),
-                            quic::QuicString(kMsg2, kLen2)));
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructRstPacket(7, quic::QUIC_STREAM_CANCELLED,
+        ConstructRstPacket(5, quic::QUIC_STREAM_CANCELLED,
                            kLen1 + kLen2 + header.length() + header2.length()));
   } else {
     mock_quic_data_.AddWrite(
@@ -853,41 +873,55 @@
         SYNCHRONOUS, ConstructAckAndDataPacket(write_packet_index++, 1, 1, 1, 0,
                                                quic::QuicString(kMsg1, kLen1)));
   } else {
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructAckAndDataPacket(write_packet_index++, 1, 1, 1, 0, header));
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructDataPacket(write_packet_index++, header.length(),
-                                         quic::QuicString(kMsg1, kLen1)));
+    mock_quic_data_.AddWrite(SYNCHRONOUS,
+                             ConstructAckAndMultipleDataFramesPacket(
+                                 write_packet_index++, 1, 1, 1, 0,
+                                 {header, quic::QuicString(kMsg1, kLen1)}));
   }
 
   // Expect |kNumDataPackets| data packets, each containing the max possible
   // amount of data.
-  const int kNumDataPackets = 3;
-  std::string data(kNumDataPackets * quic::kDefaultMaxPacketSize, 'x');
+  int numDataPackets = 3;
+  std::string data(numDataPackets * quic::kDefaultMaxPacketSize, 'x');
   quic::QuicStreamOffset offset = kLen1 + header.length();
-  size_t total_data_length = 0;
+
   if (version_ == quic::QUIC_VERSION_99) {
-    // 3973 is the data frame length from packet length.
-    quic::QuicString header2 = ConstructDataHeader(3973);
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructDataPacket(write_packet_index++, offset, header2));
-    offset += header2.length();
+    numDataPackets++;
   }
-  for (int i = 0; i < kNumDataPackets; ++i) {
+  size_t total_data_length = 0;
+  for (int i = 0; i < numDataPackets; ++i) {
     size_t max_packet_data_length = GetStreamFrameDataLengthFromPacketLength(
         quic::kDefaultMaxPacketSize, version_, !kIncludeVersion,
         !kIncludeDiversificationNonce, quic::PACKET_8BYTE_CONNECTION_ID,
         quic::PACKET_1BYTE_PACKET_NUMBER, offset);
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructDataPacket(
-            write_packet_index++, offset,
-            quic::QuicString(data.c_str(), max_packet_data_length)));
-    offset += max_packet_data_length;
-    total_data_length += max_packet_data_length;
+    if (version_ == quic::QUIC_VERSION_99 && i == 0) {
+      // 3973 is the data frame length from packet length.
+      quic::QuicString header2 = ConstructDataHeader(3973);
+      mock_quic_data_.AddWrite(
+          SYNCHRONOUS,
+          ConstructMultipleDataFramesPacket(
+              write_packet_index++, offset,
+              {header2,
+               quic::QuicString(data.c_str(), max_packet_data_length - 7)}));
+      offset += max_packet_data_length - header2.length() - 1;
+    } else if (version_ == quic::QUIC_VERSION_99 && i == numDataPackets - 1) {
+      mock_quic_data_.AddWrite(
+          SYNCHRONOUS, ConstructDataPacket(write_packet_index++, offset,
+                                           quic::QuicString(data.c_str(), 7)));
+      offset += 7;
+    } else {
+      mock_quic_data_.AddWrite(
+          SYNCHRONOUS,
+          ConstructDataPacket(
+              write_packet_index++, offset,
+              quic::QuicString(data.c_str(), max_packet_data_length)));
+      offset += max_packet_data_length;
+    }
+    if (i != 3) {
+      total_data_length += max_packet_data_length;
+    }
   }
+
   mock_quic_data_.AddWrite(
       SYNCHRONOUS, ConstructRstPacket(write_packet_index++,
                                       quic::QUIC_STREAM_CANCELLED, offset));
@@ -1229,12 +1263,15 @@
 
   quic::QuicString header2 = ConstructDataHeader(kLen2);
   if (version_ == quic::QUIC_VERSION_99) {
+    mock_quic_data_.AddWrite(SYNCHRONOUS,
+                             ConstructMultipleDataFramesPacket(
+                                 write_packet_index++, 0,
+                                 {header2, quic::QuicString(kMsg2, kLen2)}));
+  } else {
     mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructDataPacket(write_packet_index++, 0, header2));
-  }
-  mock_quic_data_.AddWrite(
       SYNCHRONOUS, ConstructDataPacket(write_packet_index++, header2.length(),
                                        quic::QuicString(kMsg2, kLen2)));
+  }
 
   mock_quic_data_.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
 
@@ -1270,11 +1307,8 @@
 }
 
 TEST_P(QuicProxyClientSocketTest, AsyncWriteAroundReads) {
-  int write_packet_index = 1;
-  mock_quic_data_.AddWrite(SYNCHRONOUS,
-                           ConstructSettingsPacket(write_packet_index++));
-  mock_quic_data_.AddWrite(SYNCHRONOUS,
-                           ConstructConnectRequestPacket(write_packet_index++));
+  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
+  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructConnectRequestPacket(2));
   mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
   mock_quic_data_.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
 
@@ -1282,8 +1316,7 @@
   mock_quic_data_.AddRead(
       ASYNC,
       ConstructServerDataPacket(2, 0, header + quic::QuicString(kMsg1, kLen1)));
-  mock_quic_data_.AddWrite(SYNCHRONOUS,
-                           ConstructAckPacket(write_packet_index++, 2, 1, 1));
+  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 1, 1));
   mock_quic_data_.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
 
   quic::QuicString header2 = ConstructDataHeader(kLen3);
@@ -1297,32 +1330,23 @@
   quic::QuicString header3 = ConstructDataHeader(kLen2);
   if (version_ != quic::QUIC_VERSION_99) {
     mock_quic_data_.AddWrite(
-        ASYNC, ConstructDataPacket(write_packet_index++, 0,
-                                   quic::QuicString(kMsg2, kLen2)));
+        ASYNC, ConstructDataPacket(4, 0, quic::QuicString(kMsg2, kLen2)));
     mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructAckAndDataPacket(write_packet_index++, 3, 3, 1, kLen2,
-                                  quic::QuicString(kMsg2, kLen2)));
+        SYNCHRONOUS, ConstructAckAndDataPacket(5, 3, 3, 1, kLen2,
+                                               quic::QuicString(kMsg2, kLen2)));
   } else {
     mock_quic_data_.AddWrite(
-        ASYNC, ConstructDataPacket(write_packet_index++, 0, header3));
+        ASYNC, ConstructMultipleDataFramesPacket(
+                   4, 0, {header3, quic::QuicString(kMsg2, kLen2)}));
     mock_quic_data_.AddWrite(
-        ASYNC, ConstructAckAndDataPacket(write_packet_index++, 3, 3, 1,
-                                         header3.length(),
-                                         quic::QuicString(kMsg2, kLen2)));
+        ASYNC,
+        ConstructAckAndDataPacket(5, 3, 3, 1, header3.length() + kLen2,
+                                  header3 + quic::QuicString(kMsg2, kLen2)));
   }
 
-  if (version_ == quic::QUIC_VERSION_99) {
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructRstPacket(write_packet_index++, quic::QUIC_STREAM_CANCELLED,
-                           kLen2 + header3.length()));
-  } else {
-    mock_quic_data_.AddWrite(
-        SYNCHRONOUS,
-        ConstructRstPacket(write_packet_index++, quic::QUIC_STREAM_CANCELLED,
-                           kLen2 + kLen2));
-  }
+  mock_quic_data_.AddWrite(
+      SYNCHRONOUS, ConstructRstPacket(6, quic::QUIC_STREAM_CANCELLED,
+                                      kLen2 + kLen2 + 2 * header3.length()));
 
   Initialize();
 
@@ -1336,12 +1360,7 @@
   // asynchronous starting with the second time it's called while the UDP socket
   // is write-blocked. Therefore, at least two writes need to be called on
   // |sock_| to get an asynchronous one.
-
-  // In version 99, two actual writes(header, body) will be done for each
-  // WriteStreamData, so we don't need 2 calls here.
-  if (version_ != quic::QUIC_VERSION_99) {
-    AssertWriteReturns(kMsg2, kLen2, kLen2);
-  }
+  AssertWriteReturns(kMsg2, kLen2, kLen2);
   AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING);
 
   AssertAsyncReadEquals(kMsg3, kLen3);
@@ -1503,12 +1522,7 @@
   // asynchronous starting with the second time it's called while the UDP socket
   // is write-blocked. Therefore, at least two writes need to be called on
   // |sock_| to get an asynchronous one.
-
-  // In version 99, 2 writes(header, body) will be called for each
-  // WriteStreamData, so only 1 Write call is needed here.
-  if (version_ != quic::QUIC_VERSION_99) {
-    AssertWriteReturns(kMsg1, kLen1, kLen1);
-  }
+  AssertWriteReturns(kMsg1, kLen1, kLen1);
 
   // This second write will be async. This is the pending write that's being
   // tested.
@@ -1539,12 +1553,7 @@
   // asynchronous starting with the second time it's called while the UDP socket
   // is write-blocked. Therefore, at least two writes need to be called on
   // |sock_| to get an asynchronous one.
-
-  // In version 99, 2 writes(header, body) will be called for each
-  // WriteStreamData, so only 1 Write call is needed here.
-  if (version_ != quic::QUIC_VERSION_99) {
-    AssertWriteReturns(kMsg1, kLen1, kLen1);
-  }
+  AssertWriteReturns(kMsg1, kLen1, kLen1);
 
   // This second write will be async. This is the pending write that's being
   // tested.
@@ -1610,11 +1619,13 @@
         SYNCHRONOUS, ConstructAckAndRstPacket(4, quic::QUIC_RST_ACKNOWLEDGEMENT,
                                               2, 2, 1, kLen2));
   } else {
-    mock_quic_data_.AddWrite(ASYNC,
-                             ConstructAckAndDataPacket(3, 1, 1, 1, 0, header));
     mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructAckAndRstPacket(4, quic::QUIC_RST_ACKNOWLEDGEMENT,
-                                              2, 2, 1, header.length()));
+        ASYNC, ConstructAckAndMultipleDataFramesPacket(
+                   3, 1, 1, 1, 0, {header, quic::QuicString(kMsg2, kLen2)}));
+    mock_quic_data_.AddWrite(
+        SYNCHRONOUS,
+        ConstructAckAndRstPacket(4, quic::QUIC_RST_ACKNOWLEDGEMENT, 2, 2, 1,
+                                 header.length() + kLen2));
   }
 
   Initialize();
@@ -1630,12 +1641,8 @@
   // asynchronous starting with the second time it's called while the UDP socket
   // is write-blocked. Therefore, at least two writes need to be called on
   // |sock_| to get an asynchronous one.
+  AssertWriteReturns(kMsg2, kLen2, kLen2);
 
-  // In version 99, 2 writes(header, body) will be called for each
-  // WriteStreamData, so only 1 Write call is needed here.
-  if (version_ != quic::QUIC_VERSION_99) {
-    AssertWriteReturns(kMsg2, kLen2, kLen2);
-  }
   AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING);
 
   ResumeAndRun();
@@ -1747,11 +1754,13 @@
                                               2, 2, 1, kLen1));
   } else {
     quic::QuicString header = ConstructDataHeader(kLen1);
-    mock_quic_data_.AddWrite(ASYNC,
-                             ConstructAckAndDataPacket(3, 1, 1, 1, 0, header));
     mock_quic_data_.AddWrite(
-        SYNCHRONOUS, ConstructAckAndRstPacket(4, quic::QUIC_RST_ACKNOWLEDGEMENT,
-                                              2, 2, 1, header.length()));
+        ASYNC, ConstructAckAndMultipleDataFramesPacket(
+                   3, 1, 1, 1, 0, {header, quic::QuicString(kMsg1, kLen1)}));
+    mock_quic_data_.AddWrite(
+        SYNCHRONOUS,
+        ConstructAckAndRstPacket(4, quic::QUIC_RST_ACKNOWLEDGEMENT, 2, 2, 1,
+                                 header.length() + kLen1));
   }
 
   Initialize();
@@ -1769,12 +1778,8 @@
   // asynchronous starting with the second time it's called while the UDP socket
   // is write-blocked. Therefore, at least two writes need to be called on
   // |sock_| to get an asynchronous one.
+  AssertWriteReturns(kMsg1, kLen1, kLen1);
 
-  // In version 99, 2 writes(header, body) will be called for each
-  // WriteStreamData, so only 1 write call is needed here.
-  if (version_ != quic::QUIC_VERSION_99) {
-    AssertWriteReturns(kMsg1, kLen1, kLen1);
-  }
   AssertWriteReturns(kMsg1, kLen1, ERR_IO_PENDING);
 
   ResumeAndRun();
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 9b8f6c99..ada58429 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -5389,6 +5389,11 @@
   EXPECT_EQ(ERR_NETWORK_CHANGED,
             stream->ReadResponseHeaders(callback_.callback()));
 
+  NetErrorDetails error_details;
+  stream->PopulateNetErrorDetails(&error_details);
+  EXPECT_EQ(error_details.quic_connection_error,
+            quic::QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK);
+
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index 14c5162..1adb373 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -17,6 +17,7 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/socks_client_socket_pool.h"
+#include "net/socket/socks_connect_job.h"
 #include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/socket/transport_connect_job.h"
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index e43dba7..68617d63 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -2322,6 +2322,9 @@
   return socket;
 }
 
+const char kSOCKS4TestHost[] = "127.0.0.1";
+const int kSOCKS4TestPort = 80;
+
 const char kSOCKS4OkRequestLocalHostPort80[] = {0x04, 0x01, 0x00, 0x50, 127,
                                                 0,    0,    1,    0};
 const int kSOCKS4OkRequestLocalHostPort80Length =
@@ -2330,6 +2333,9 @@
 const char kSOCKS4OkReply[] = {0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0};
 const int kSOCKS4OkReplyLength = base::size(kSOCKS4OkReply);
 
+const char kSOCKS5TestHost[] = "host";
+const int kSOCKS5TestPort = 80;
+
 const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 };
 const int kSOCKS5GreetRequestLength = base::size(kSOCKS5GreetRequest);
 
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 77fb118..b2237ad 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -1378,15 +1378,24 @@
   DISALLOW_COPY_AND_ASSIGN(MockTaggingClientSocketFactory);
 };
 
-// Constants for a successful SOCKS v4 handshake (connecting to localhost on
-// port 80, for the request).
+// Host / port used for SOCKS4 test strings.
+extern const char kSOCKS4TestHost[];
+extern const int kSOCKS4TestPort;
+
+// Constants for a successful SOCKS v4 handshake (connecting to kSOCKS4TestHost
+// on port kSOCKS4TestPort, for the request).
 extern const char kSOCKS4OkRequestLocalHostPort80[];
 extern const int kSOCKS4OkRequestLocalHostPort80Length;
 
 extern const char kSOCKS4OkReply[];
 extern const int kSOCKS4OkReplyLength;
 
-// Constants for a successful SOCKS v5 handshake.
+// Host / port used for SOCKS5 test strings.
+extern const char kSOCKS5TestHost[];
+extern const int kSOCKS5TestPort;
+
+// Constants for a successful SOCKS v5 handshake (connecting to kSOCKS5TestHost
+// on port kSOCKS5TestPort, for the request)..
 extern const char kSOCKS5GreetRequest[];
 extern const int kSOCKS5GreetRequestLength;
 
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
index 474aa41d6..63de317 100644
--- a/net/socket/socks_client_socket_pool.cc
+++ b/net/socket/socks_client_socket_pool.cc
@@ -8,188 +8,16 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
 #include "net/log/net_log_source_type.h"
 #include "net/log/net_log_with_source.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
-#include "net/socket/client_socket_pool_base.h"
-#include "net/socket/socks5_client_socket.h"
-#include "net/socket/socks_client_socket.h"
 #include "net/socket/transport_client_socket_pool.h"
-#include "net/socket/transport_connect_job.h"
 
 namespace net {
 
-// SOCKSConnectJobs will time out after this many seconds.  Note this is in
-// addition to the timeout for the transport socket.
-static const int kSOCKSConnectJobTimeoutInSeconds = 30;
-
-class NetLog;
-
-SOCKSSocketParams::SOCKSSocketParams(
-    const scoped_refptr<TransportSocketParams>& proxy_server,
-    bool socks_v5,
-    const HostPortPair& host_port_pair,
-    const NetworkTrafficAnnotationTag& traffic_annotation)
-    : transport_params_(proxy_server),
-      destination_(host_port_pair),
-      socks_v5_(socks_v5),
-      traffic_annotation_(traffic_annotation) {}
-
-SOCKSSocketParams::~SOCKSSocketParams() = default;
-
-SOCKSConnectJob::SOCKSConnectJob(
-    const std::string& group_name,
-    RequestPriority priority,
-    const SocketTag& socket_tag,
-    ClientSocketPool::RespectLimits respect_limits,
-    const scoped_refptr<SOCKSSocketParams>& socks_params,
-    TransportClientSocketPool* transport_pool,
-    HostResolver* host_resolver,
-    Delegate* delegate,
-    NetLog* net_log)
-    : ConnectJob(
-          group_name,
-          ConnectionTimeout(),
-          priority,
-          socket_tag,
-          respect_limits == ClientSocketPool::RespectLimits::ENABLED,
-          delegate,
-          NetLogWithSource::Make(net_log, NetLogSourceType::SOCKS_CONNECT_JOB)),
-      socks_params_(socks_params),
-      transport_pool_(transport_pool),
-      resolver_(host_resolver) {}
-
-SOCKSConnectJob::~SOCKSConnectJob() {
-  // We don't worry about cancelling the tcp socket since the destructor in
-  // std::unique_ptr<ClientSocketHandle> transport_socket_handle_ will take care
-  // of
-  // it.
-}
-
-LoadState SOCKSConnectJob::GetLoadState() const {
-  switch (next_state_) {
-    case STATE_TRANSPORT_CONNECT:
-    case STATE_TRANSPORT_CONNECT_COMPLETE:
-      return transport_socket_handle_->GetLoadState();
-    case STATE_SOCKS_CONNECT:
-    case STATE_SOCKS_CONNECT_COMPLETE:
-      return LOAD_STATE_CONNECTING;
-    default:
-      NOTREACHED();
-      return LOAD_STATE_IDLE;
-  }
-}
-
-base::TimeDelta SOCKSConnectJob::ConnectionTimeout() {
-  return TransportConnectJob::ConnectionTimeout() +
-         base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds);
-}
-
-void SOCKSConnectJob::OnIOComplete(int result) {
-  int rv = DoLoop(result);
-  if (rv != ERR_IO_PENDING)
-    NotifyDelegateOfCompletion(rv);  // Deletes |this|
-}
-
-int SOCKSConnectJob::DoLoop(int result) {
-  DCHECK_NE(next_state_, STATE_NONE);
-
-  int rv = result;
-  do {
-    State state = next_state_;
-    next_state_ = STATE_NONE;
-    switch (state) {
-      case STATE_TRANSPORT_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoTransportConnect();
-        break;
-      case STATE_TRANSPORT_CONNECT_COMPLETE:
-        rv = DoTransportConnectComplete(rv);
-        break;
-      case STATE_SOCKS_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoSOCKSConnect();
-        break;
-      case STATE_SOCKS_CONNECT_COMPLETE:
-        rv = DoSOCKSConnectComplete(rv);
-        break;
-      default:
-        NOTREACHED() << "bad state";
-        rv = ERR_FAILED;
-        break;
-    }
-  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
-
-  return rv;
-}
-
-int SOCKSConnectJob::DoTransportConnect() {
-  next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
-  transport_socket_handle_.reset(new ClientSocketHandle());
-  CompletionOnceCallback callback =
-      base::BindOnce(&SOCKSConnectJob::OnIOComplete, base::Unretained(this));
-  return transport_socket_handle_->Init(
-      group_name(),
-      TransportClientSocketPool::SocketParams::CreateFromTransportSocketParams(
-          socks_params_->transport_params()),
-      priority(), socket_tag(), respect_limits(), std::move(callback),
-      transport_pool_, net_log());
-}
-
-int SOCKSConnectJob::DoTransportConnectComplete(int result) {
-  if (result != OK)
-    return ERR_PROXY_CONNECTION_FAILED;
-
-  // Reset the timer to just the length of time allowed for SOCKS handshake
-  // so that a fast TCP connection plus a slow SOCKS failure doesn't take
-  // longer to timeout than it should.
-  ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds));
-  next_state_ = STATE_SOCKS_CONNECT;
-  return result;
-}
-
-int SOCKSConnectJob::DoSOCKSConnect() {
-  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
-
-  // Add a SOCKS connection on top of the tcp socket.
-  if (socks_params_->is_socks_v5()) {
-    socket_.reset(new SOCKS5ClientSocket(std::move(transport_socket_handle_),
-                                         socks_params_->destination(),
-                                         socks_params_->traffic_annotation()));
-  } else {
-    socket_.reset(new SOCKSClientSocket(
-        std::move(transport_socket_handle_), socks_params_->destination(),
-        priority(), resolver_, socks_params_->traffic_annotation()));
-  }
-  return socket_->Connect(
-      base::Bind(&SOCKSConnectJob::OnIOComplete, base::Unretained(this)));
-}
-
-int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
-  if (result != OK) {
-    socket_->Disconnect();
-    return result;
-  }
-
-  SetSocket(std::move(socket_));
-  return result;
-}
-
-int SOCKSConnectJob::ConnectInternal() {
-  next_state_ = STATE_TRANSPORT_CONNECT;
-  return DoLoop(OK);
-}
-
-void SOCKSConnectJob::ChangePriorityInternal(RequestPriority priority) {
-  // Currently doesn't change host resolution request priority for SOCKS4 case.
-  if (transport_socket_handle_)
-    transport_socket_handle_->SetPriority(priority);
-}
-
 std::unique_ptr<ConnectJob>
 SOCKSClientSocketPool::SOCKSConnectJobFactory::NewConnectJob(
     const std::string& group_name,
@@ -197,8 +25,8 @@
     ConnectJob::Delegate* delegate) const {
   return std::make_unique<SOCKSConnectJob>(
       group_name, request.priority(), request.socket_tag(),
-      request.respect_limits(), request.params(), transport_pool_,
-      host_resolver_, delegate, net_log_);
+      request.respect_limits() == ClientSocketPool::RespectLimits::ENABLED,
+      request.params(), transport_pool_, host_resolver_, delegate, net_log_);
 }
 
 SOCKSClientSocketPool::SOCKSClientSocketPool(
diff --git a/net/socket/socks_client_socket_pool.h b/net/socket/socks_client_socket_pool.h
index dca9b98..4816b8b5 100644
--- a/net/socket/socks_client_socket_pool.h
+++ b/net/socket/socks_client_socket_pool.h
@@ -11,112 +11,21 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/time/time.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
 #include "net/dns/host_resolver.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
+#include "net/socket/socks_connect_job.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace net {
 
 class ConnectJobFactory;
+class NetLog;
 class SocketPerformanceWatcherFactory;
 class TransportClientSocketPool;
-class TransportSocketParams;
-
-class NET_EXPORT_PRIVATE SOCKSSocketParams
-    : public base::RefCounted<SOCKSSocketParams> {
- public:
-  SOCKSSocketParams(const scoped_refptr<TransportSocketParams>& proxy_server,
-                    bool socks_v5,
-                    const HostPortPair& host_port_pair,
-                    const NetworkTrafficAnnotationTag& traffic_annotation);
-
-  const scoped_refptr<TransportSocketParams>& transport_params() const {
-    return transport_params_;
-  }
-  const HostResolver::RequestInfo& destination() const { return destination_; }
-  bool is_socks_v5() const { return socks_v5_; }
-
-  const NetworkTrafficAnnotationTag traffic_annotation() {
-    return traffic_annotation_;
-  }
-
- private:
-  friend class base::RefCounted<SOCKSSocketParams>;
-  ~SOCKSSocketParams();
-
-  // The transport (likely TCP) connection must point toward the proxy server.
-  const scoped_refptr<TransportSocketParams> transport_params_;
-  // This is the HTTP destination.
-  HostResolver::RequestInfo destination_;
-  const bool socks_v5_;
-
-  NetworkTrafficAnnotationTag traffic_annotation_;
-
-  DISALLOW_COPY_AND_ASSIGN(SOCKSSocketParams);
-};
-
-// SOCKSConnectJob handles the handshake to a socks server after setting up
-// an underlying transport socket.
-class SOCKSConnectJob : public ConnectJob {
- public:
-  SOCKSConnectJob(const std::string& group_name,
-                  RequestPriority priority,
-                  const SocketTag& socket_tag,
-                  ClientSocketPool::RespectLimits respect_limits,
-                  const scoped_refptr<SOCKSSocketParams>& params,
-                  TransportClientSocketPool* transport_pool,
-                  HostResolver* host_resolver,
-                  Delegate* delegate,
-                  NetLog* net_log);
-  ~SOCKSConnectJob() override;
-
-  // ConnectJob methods.
-  LoadState GetLoadState() const override;
-
-  // Returns the connection timeout used by SOCKSConnectJobs.
-  static base::TimeDelta ConnectionTimeout();
-
- private:
-  enum State {
-    STATE_TRANSPORT_CONNECT,
-    STATE_TRANSPORT_CONNECT_COMPLETE,
-    STATE_SOCKS_CONNECT,
-    STATE_SOCKS_CONNECT_COMPLETE,
-    STATE_NONE,
-  };
-
-  void OnIOComplete(int result);
-
-  // Runs the state transition loop.
-  int DoLoop(int result);
-
-  int DoTransportConnect();
-  int DoTransportConnectComplete(int result);
-  int DoSOCKSConnect();
-  int DoSOCKSConnectComplete(int result);
-
-  // Begins the transport connection and the SOCKS handshake.  Returns OK on
-  // success and ERR_IO_PENDING if it cannot immediately service the request.
-  // Otherwise, it returns a net error code.
-  int ConnectInternal() override;
-
-  void ChangePriorityInternal(RequestPriority priority) override;
-
-  scoped_refptr<SOCKSSocketParams> socks_params_;
-  TransportClientSocketPool* const transport_pool_;
-  HostResolver* const resolver_;
-
-  State next_state_;
-  std::unique_ptr<ClientSocketHandle> transport_socket_handle_;
-  std::unique_ptr<StreamSocket> socket_;
-
-  DISALLOW_COPY_AND_ASSIGN(SOCKSConnectJob);
-};
 
 class NET_EXPORT_PRIVATE SOCKSClientSocketPool
     : public ClientSocketPool, public HigherLayeredPool {
diff --git a/net/socket/socks_client_socket_pool_unittest.cc b/net/socket/socks_client_socket_pool_unittest.cc
index 1bc43c8..8a921e7 100644
--- a/net/socket/socks_client_socket_pool_unittest.cc
+++ b/net/socket/socks_client_socket_pool_unittest.cc
@@ -22,6 +22,7 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
+#include "net/socket/socks_connect_job.h"
 #include "net/socket/transport_connect_job.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_scoped_task_environment.h"
@@ -39,34 +40,11 @@
 const int kMaxSockets = 32;
 const int kMaxSocketsPerGroup = 6;
 
-// Make sure |handle|'s load times are set correctly.  Only connect times should
-// be set.
-void TestLoadTimingInfo(const ClientSocketHandle& handle) {
-  LoadTimingInfo load_timing_info;
-  EXPECT_TRUE(handle.GetLoadTimingInfo(false, &load_timing_info));
-
-  // None of these tests use a NetLog.
-  EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
-
-  EXPECT_FALSE(load_timing_info.socket_reused);
-
-  ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
-                              CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY);
-  ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
-}
-
-
 scoped_refptr<TransportSocketParams> CreateProxyHostParams() {
   return new TransportSocketParams(HostPortPair("proxy", 80), false,
                                    OnHostResolutionCallback());
 }
 
-scoped_refptr<SOCKSSocketParams> CreateSOCKSv4Params() {
-  return new SOCKSSocketParams(CreateProxyHostParams(), false /* socks_v5 */,
-                               HostPortPair("host", 80),
-                               TRAFFIC_ANNOTATION_FOR_TESTS);
-}
-
 scoped_refptr<SOCKSSocketParams> CreateSOCKSv5Params() {
   return new SOCKSSocketParams(CreateProxyHostParams(), true /* socks_v5 */,
                                HostPortPair("host", 80),
@@ -149,179 +127,6 @@
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(handle.is_initialized());
   EXPECT_TRUE(handle.socket());
-  TestLoadTimingInfo(handle);
-}
-
-// Make sure that SOCKSConnectJob passes on its priority to its
-// socket request on Init.
-TEST_F(SOCKSClientSocketPoolTest, SetSocketRequestPriorityOnInit) {
-  for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
-    RequestPriority priority = static_cast<RequestPriority>(i);
-    SOCKS5MockData data(SYNCHRONOUS);
-    data.data_provider()->set_connect_data(MockConnect(SYNCHRONOUS, OK));
-    transport_client_socket_factory_.AddSocketDataProvider(
-        data.data_provider());
-
-    ClientSocketHandle handle;
-    EXPECT_EQ(
-        OK, handle.Init("a", CreateSOCKSv5Params(), priority, SocketTag(),
-                        ClientSocketPool::RespectLimits::ENABLED,
-                        CompletionOnceCallback(), &pool_, NetLogWithSource()));
-    EXPECT_EQ(priority, transport_socket_pool_.last_request_priority());
-    EXPECT_EQ(priority, transport_socket_pool_.requests()[i]->priority());
-    handle.socket()->Disconnect();
-  }
-}
-
-TEST_F(SOCKSClientSocketPoolTest, SetSocketRequestPriority) {
-  SOCKS5MockData data1(ASYNC);
-  transport_client_socket_factory_.AddSocketDataProvider(data1.data_provider());
-  SOCKS5MockData data2(ASYNC);
-  transport_client_socket_factory_.AddSocketDataProvider(data2.data_provider());
-
-  TestCompletionCallback callback1;
-  ClientSocketHandle handle1;
-  int rv1 = handle1.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                         ClientSocketPool::RespectLimits::ENABLED,
-                         callback1.callback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv1, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle1.is_initialized());
-  EXPECT_FALSE(handle1.socket());
-  EXPECT_EQ(LOW, transport_socket_pool_.requests()[0]->priority());
-
-  TestCompletionCallback callback2;
-  ClientSocketHandle handle2;
-  int rv2 = handle2.Init("a", CreateSOCKSv5Params(), MEDIUM, SocketTag(),
-                         ClientSocketPool::RespectLimits::ENABLED,
-                         callback2.callback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv2, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle2.is_initialized());
-  EXPECT_FALSE(handle2.socket());
-  EXPECT_EQ(MEDIUM, transport_socket_pool_.requests()[1]->priority());
-
-  pool_.SetPriority("a", &handle1, HIGHEST);
-  EXPECT_EQ(HIGHEST, transport_socket_pool_.requests()[0]->priority());
-  EXPECT_EQ(MEDIUM, transport_socket_pool_.requests()[1]->priority());
-}
-
-// Make sure that SOCKSConnectJob passes on its priority to its
-// HostResolver request (for non-SOCKS5) on Init.
-TEST_F(SOCKSClientSocketPoolTest, SetResolvePriorityOnInit) {
-  for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
-    RequestPriority priority = static_cast<RequestPriority>(i);
-    SOCKS5MockData data(SYNCHRONOUS);
-    data.data_provider()->set_connect_data(MockConnect(SYNCHRONOUS, OK));
-    transport_client_socket_factory_.AddSocketDataProvider(
-        data.data_provider());
-
-    ClientSocketHandle handle;
-    EXPECT_EQ(
-        ERR_IO_PENDING,
-        handle.Init("a", CreateSOCKSv4Params(), priority, SocketTag(),
-                    ClientSocketPool::RespectLimits::ENABLED,
-                    CompletionOnceCallback(), &pool_, NetLogWithSource()));
-    EXPECT_EQ(priority, transport_socket_pool_.last_request_priority());
-    EXPECT_EQ(priority, host_resolver_.last_request_priority());
-    EXPECT_EQ(priority,
-              host_resolver_.request_priority(static_cast<size_t>(i) + 1));
-    EXPECT_TRUE(handle.socket() == NULL);
-  }
-}
-
-TEST_F(SOCKSClientSocketPoolTest, Async) {
-  SOCKS5MockData data(ASYNC);
-  transport_client_socket_factory_.AddSocketDataProvider(data.data_provider());
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  TestLoadTimingInfo(handle);
-}
-
-TEST_F(SOCKSClientSocketPoolTest, TransportConnectError) {
-  StaticSocketDataProvider socket_data;
-  socket_data.set_connect_data(MockConnect(SYNCHRONOUS,
-                                           ERR_CONNECTION_REFUSED));
-  transport_client_socket_factory_.AddSocketDataProvider(&socket_data);
-
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       CompletionOnceCallback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_PROXY_CONNECTION_FAILED));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-}
-
-TEST_F(SOCKSClientSocketPoolTest, AsyncTransportConnectError) {
-  StaticSocketDataProvider socket_data;
-  socket_data.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED));
-  transport_client_socket_factory_.AddSocketDataProvider(&socket_data);
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_PROXY_CONNECTION_FAILED));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-}
-
-TEST_F(SOCKSClientSocketPoolTest, SOCKSConnectError) {
-  MockRead failed_read[] = {
-    MockRead(SYNCHRONOUS, 0),
-  };
-  StaticSocketDataProvider socket_data(failed_read, base::span<MockWrite>());
-  socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  transport_client_socket_factory_.AddSocketDataProvider(&socket_data);
-
-  ClientSocketHandle handle;
-  EXPECT_EQ(0, transport_socket_pool_.release_count());
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       CompletionOnceCallback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_SOCKS_CONNECTION_FAILED));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-  EXPECT_EQ(1, transport_socket_pool_.release_count());
-}
-
-TEST_F(SOCKSClientSocketPoolTest, AsyncSOCKSConnectError) {
-  MockRead failed_read[] = {
-    MockRead(ASYNC, 0),
-  };
-  StaticSocketDataProvider socket_data(failed_read, base::span<MockWrite>());
-  socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
-  transport_client_socket_factory_.AddSocketDataProvider(&socket_data);
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  EXPECT_EQ(0, transport_socket_pool_.release_count());
-  int rv = handle.Init("a", CreateSOCKSv5Params(), LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool_, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_SOCKS_CONNECTION_FAILED));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-  EXPECT_EQ(1, transport_socket_pool_.release_count());
 }
 
 TEST_F(SOCKSClientSocketPoolTest, CancelDuringTransportConnect) {
@@ -393,8 +198,6 @@
   (*requests())[1]->handle()->Reset();
 }
 
-// It would be nice to also test the timeouts in SOCKSClientSocketPool.
-
 // Test that SocketTag passed into SOCKSClientSocketPool is applied to returned
 // sockets.
 #if defined(OS_ANDROID)
diff --git a/net/socket/socks_connect_job.cc b/net/socket/socks_connect_job.cc
new file mode 100644
index 0000000..169eacc
--- /dev/null
+++ b/net/socket/socks_connect_job.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 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 "net/socket/socks_connect_job.h"
+
+#include "base/bind.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/socks5_client_socket.h"
+#include "net/socket/socks_client_socket.h"
+#include "net/socket/transport_client_socket_pool.h"
+#include "net/socket/transport_connect_job.h"
+
+namespace net {
+
+// SOCKSConnectJobs will time out after this many seconds.  Note this is in
+// addition to the timeout for the transport socket.
+static const int kSOCKSConnectJobTimeoutInSeconds = 30;
+
+SOCKSSocketParams::SOCKSSocketParams(
+    const scoped_refptr<TransportSocketParams>& proxy_server,
+    bool socks_v5,
+    const HostPortPair& host_port_pair,
+    const NetworkTrafficAnnotationTag& traffic_annotation)
+    : transport_params_(proxy_server),
+      destination_(host_port_pair),
+      socks_v5_(socks_v5),
+      traffic_annotation_(traffic_annotation) {}
+
+SOCKSSocketParams::~SOCKSSocketParams() = default;
+
+SOCKSConnectJob::SOCKSConnectJob(
+    const std::string& group_name,
+    RequestPriority priority,
+    const SocketTag& socket_tag,
+    bool respect_limits,
+    const scoped_refptr<SOCKSSocketParams>& socks_params,
+    TransportClientSocketPool* transport_pool,
+    HostResolver* host_resolver,
+    Delegate* delegate,
+    NetLog* net_log)
+    : ConnectJob(
+          group_name,
+          ConnectionTimeout(),
+          priority,
+          socket_tag,
+          respect_limits,
+          delegate,
+          NetLogWithSource::Make(net_log, NetLogSourceType::SOCKS_CONNECT_JOB)),
+      socks_params_(socks_params),
+      transport_pool_(transport_pool),
+      resolver_(host_resolver) {}
+
+SOCKSConnectJob::~SOCKSConnectJob() {
+  // We don't worry about cancelling the tcp socket since the destructor in
+  // std::unique_ptr<ClientSocketHandle> transport_socket_handle_ will take care
+  // of
+  // it.
+}
+
+LoadState SOCKSConnectJob::GetLoadState() const {
+  switch (next_state_) {
+    case STATE_TRANSPORT_CONNECT:
+    case STATE_TRANSPORT_CONNECT_COMPLETE:
+      return transport_socket_handle_->GetLoadState();
+    case STATE_SOCKS_CONNECT:
+    case STATE_SOCKS_CONNECT_COMPLETE:
+      return LOAD_STATE_CONNECTING;
+    default:
+      NOTREACHED();
+      return LOAD_STATE_IDLE;
+  }
+}
+
+base::TimeDelta SOCKSConnectJob::ConnectionTimeout() {
+  return TransportConnectJob::ConnectionTimeout() +
+         base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds);
+}
+
+void SOCKSConnectJob::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    NotifyDelegateOfCompletion(rv);  // Deletes |this|
+}
+
+int SOCKSConnectJob::DoLoop(int result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_TRANSPORT_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoTransportConnect();
+        break;
+      case STATE_TRANSPORT_CONNECT_COMPLETE:
+        rv = DoTransportConnectComplete(rv);
+        break;
+      case STATE_SOCKS_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoSOCKSConnect();
+        break;
+      case STATE_SOCKS_CONNECT_COMPLETE:
+        rv = DoSOCKSConnectComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int SOCKSConnectJob::DoTransportConnect() {
+  next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  CompletionOnceCallback callback =
+      base::BindOnce(&SOCKSConnectJob::OnIOComplete, base::Unretained(this));
+  return transport_socket_handle_->Init(
+      group_name(),
+      TransportClientSocketPool::SocketParams::CreateFromTransportSocketParams(
+          socks_params_->transport_params()),
+      priority(), socket_tag(), respect_limits(), std::move(callback),
+      transport_pool_, net_log());
+}
+
+int SOCKSConnectJob::DoTransportConnectComplete(int result) {
+  if (result != OK)
+    return ERR_PROXY_CONNECTION_FAILED;
+
+  // Reset the timer to just the length of time allowed for SOCKS handshake
+  // so that a fast TCP connection plus a slow SOCKS failure doesn't take
+  // longer to timeout than it should.
+  ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds));
+  next_state_ = STATE_SOCKS_CONNECT;
+  return result;
+}
+
+int SOCKSConnectJob::DoSOCKSConnect() {
+  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
+
+  // Add a SOCKS connection on top of the tcp socket.
+  if (socks_params_->is_socks_v5()) {
+    socket_.reset(new SOCKS5ClientSocket(std::move(transport_socket_handle_),
+                                         socks_params_->destination(),
+                                         socks_params_->traffic_annotation()));
+  } else {
+    socket_.reset(new SOCKSClientSocket(
+        std::move(transport_socket_handle_), socks_params_->destination(),
+        priority(), resolver_, socks_params_->traffic_annotation()));
+  }
+  return socket_->Connect(
+      base::BindOnce(&SOCKSConnectJob::OnIOComplete, base::Unretained(this)));
+}
+
+int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
+  if (result != OK) {
+    socket_->Disconnect();
+    return result;
+  }
+
+  SetSocket(std::move(socket_));
+  return result;
+}
+
+int SOCKSConnectJob::ConnectInternal() {
+  next_state_ = STATE_TRANSPORT_CONNECT;
+  return DoLoop(OK);
+}
+
+void SOCKSConnectJob::ChangePriorityInternal(RequestPriority priority) {
+  // Currently doesn't change host resolution request priority for SOCKS4 case.
+  if (transport_socket_handle_)
+    transport_socket_handle_->SetPriority(priority);
+}
+
+}  // namespace net
diff --git a/net/socket/socks_connect_job.h b/net/socket/socks_connect_job.h
new file mode 100644
index 0000000..70d2263
--- /dev/null
+++ b/net/socket/socks_connect_job.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 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 NET_SOCKET_SOCKS_CONNECT_JOB_H_
+#define NET_SOCKET_SOCKS_CONNECT_JOB_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/net_export.h"
+#include "net/base/request_priority.h"
+#include "net/dns/host_resolver.h"
+#include "net/socket/connect_job.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
+namespace net {
+
+class ClientSocketHandle;
+class HostPortPair;
+class NetLog;
+class StreamSocket;
+class TransportClientSocketPool;
+class TransportSocketParams;
+
+class NET_EXPORT_PRIVATE SOCKSSocketParams
+    : public base::RefCounted<SOCKSSocketParams> {
+ public:
+  SOCKSSocketParams(const scoped_refptr<TransportSocketParams>& proxy_server,
+                    bool socks_v5,
+                    const HostPortPair& host_port_pair,
+                    const NetworkTrafficAnnotationTag& traffic_annotation);
+
+  const scoped_refptr<TransportSocketParams>& transport_params() const {
+    return transport_params_;
+  }
+  const HostResolver::RequestInfo& destination() const { return destination_; }
+  bool is_socks_v5() const { return socks_v5_; }
+
+  const NetworkTrafficAnnotationTag traffic_annotation() {
+    return traffic_annotation_;
+  }
+
+ private:
+  friend class base::RefCounted<SOCKSSocketParams>;
+  ~SOCKSSocketParams();
+
+  // The transport (likely TCP) connection must point toward the proxy server.
+  const scoped_refptr<TransportSocketParams> transport_params_;
+  // This is the HTTP destination.
+  HostResolver::RequestInfo destination_;
+  const bool socks_v5_;
+
+  NetworkTrafficAnnotationTag traffic_annotation_;
+
+  DISALLOW_COPY_AND_ASSIGN(SOCKSSocketParams);
+};
+
+// SOCKSConnectJob handles the handshake to a socks server after setting up
+// an underlying transport socket.
+class NET_EXPORT_PRIVATE SOCKSConnectJob : public ConnectJob {
+ public:
+  SOCKSConnectJob(const std::string& group_name,
+                  RequestPriority priority,
+                  const SocketTag& socket_tag,
+                  bool respect_limits,
+                  const scoped_refptr<SOCKSSocketParams>& params,
+                  TransportClientSocketPool* transport_pool,
+                  HostResolver* host_resolver,
+                  Delegate* delegate,
+                  NetLog* net_log);
+  ~SOCKSConnectJob() override;
+
+  // ConnectJob methods.
+  LoadState GetLoadState() const override;
+
+  // Returns the connection timeout used by SOCKSConnectJobs.
+  static base::TimeDelta ConnectionTimeout();
+
+ private:
+  enum State {
+    STATE_TRANSPORT_CONNECT,
+    STATE_TRANSPORT_CONNECT_COMPLETE,
+    STATE_SOCKS_CONNECT,
+    STATE_SOCKS_CONNECT_COMPLETE,
+    STATE_NONE,
+  };
+
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  int DoTransportConnect();
+  int DoTransportConnectComplete(int result);
+  int DoSOCKSConnect();
+  int DoSOCKSConnectComplete(int result);
+
+  // Begins the transport connection and the SOCKS handshake.  Returns OK on
+  // success and ERR_IO_PENDING if it cannot immediately service the request.
+  // Otherwise, it returns a net error code.
+  int ConnectInternal() override;
+
+  void ChangePriorityInternal(RequestPriority priority) override;
+
+  scoped_refptr<SOCKSSocketParams> socks_params_;
+  TransportClientSocketPool* const transport_pool_;
+  HostResolver* const resolver_;
+
+  State next_state_;
+  std::unique_ptr<ClientSocketHandle> transport_socket_handle_;
+  std::unique_ptr<StreamSocket> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(SOCKSConnectJob);
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SOCKS_CONNECT_JOB_H_
diff --git a/net/socket/socks_connect_job_unittest.cc b/net/socket/socks_connect_job_unittest.cc
new file mode 100644
index 0000000..713fc55
--- /dev/null
+++ b/net/socket/socks_connect_job_unittest.cc
@@ -0,0 +1,385 @@
+// 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 "net/socket/socks_client_socket_pool.h"
+
+#include "base/callback.h"
+#include "base/containers/span.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "net/base/load_timing_info.h"
+#include "net/base/load_timing_info_test_util.h"
+#include "net/base/net_errors.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/log/net_log.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/connect_job_test_util.h"
+#include "net/socket/socket_tag.h"
+#include "net/socket/socket_test_util.h"
+#include "net/socket/socks_connect_job.h"
+#include "net/socket/transport_client_socket_pool.h"
+#include "net/socket/transport_connect_job.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+const char kProxyHostName[] = "proxy.test";
+const int kProxyPort = 4321;
+
+constexpr base::TimeDelta kTinyTime = base::TimeDelta::FromMicroseconds(1);
+
+class SOCKSConnectJobTest : public testing::Test,
+                            public WithScopedTaskEnvironment {
+ public:
+  enum class SOCKSVersion {
+    V4,
+    V5,
+  };
+
+  SOCKSConnectJobTest()
+      : WithScopedTaskEnvironment(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+            base::test::ScopedTaskEnvironment::NowSource::
+                MAIN_THREAD_MOCK_TIME),
+        transport_pool_(2 /* max_sockets */,
+                        2 /* max_sockets_per_group */,
+                        &host_resolver_,
+                        &client_socket_factory_,
+                        nullptr /* socket_performance_watcher_factory */,
+                        &net_log_) {
+    // Set an initial delay to ensure that the first call to TimeTicks::Now()
+    // before incrementing the counter does not return a null value.
+    FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+
+  ~SOCKSConnectJobTest() override {}
+
+  static scoped_refptr<SOCKSSocketParams> CreateSOCKSParams(
+      SOCKSVersion socks_version) {
+    return base::MakeRefCounted<SOCKSSocketParams>(
+        base::MakeRefCounted<TransportSocketParams>(
+            HostPortPair(kProxyHostName, kProxyPort), true /* respect_limits */,
+            OnHostResolutionCallback()),
+        socks_version == SOCKSVersion::V5,
+        socks_version == SOCKSVersion::V4
+            ? HostPortPair(kSOCKS4TestHost, kSOCKS4TestPort)
+            : HostPortPair(kSOCKS5TestHost, kSOCKS5TestPort),
+        TRAFFIC_ANNOTATION_FOR_TESTS);
+  }
+
+ protected:
+  NetLog net_log_;
+  MockHostResolver host_resolver_;
+  MockTaggingClientSocketFactory client_socket_factory_;
+  TransportClientSocketPool transport_pool_;
+};
+
+TEST_F(SOCKSConnectJobTest, HostResolutionFailure) {
+  host_resolver_.rules()->AddSimulatedFailure(kProxyHostName);
+
+  for (bool failure_synchronous : {false, true}) {
+    host_resolver_.set_synchronous_mode(failure_synchronous);
+    TestConnectJobDelegate test_delegate;
+    SOCKSConnectJob socks_connect_job(
+        kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+        true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+        &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+    test_delegate.StartJobExpectingResult(
+        &socks_connect_job, ERR_PROXY_CONNECTION_FAILED, failure_synchronous);
+  }
+}
+
+TEST_F(SOCKSConnectJobTest, HandshakeError) {
+  for (bool host_resolution_synchronous : {false, true}) {
+    for (bool write_failure_synchronous : {false, true}) {
+      host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+
+      // No need to distinguish which part of the handshake fails. Those details
+      // are all handled at the StreamSocket layer, not the SOCKSConnectJob.
+      MockWrite writes[] = {
+          MockWrite(write_failure_synchronous ? SYNCHRONOUS : ASYNC,
+                    ERR_UNEXPECTED, 0),
+      };
+      SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
+      // Host resolution is used to switch between sync and async connection
+      // behavior. The SOCKS layer can't distinguish between sync and async host
+      // resolution vs sync and async connection establishment, so just always
+      // make connection establishment synchroonous.
+      sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+      client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
+
+      TestConnectJobDelegate test_delegate;
+      SOCKSConnectJob socks_connect_job(
+          kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+          true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+          &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+      test_delegate.StartJobExpectingResult(
+          &socks_connect_job, ERR_UNEXPECTED,
+          host_resolution_synchronous && write_failure_synchronous);
+    }
+  }
+}
+
+TEST_F(SOCKSConnectJobTest, SOCKS4) {
+  for (bool host_resolution_synchronous : {false, true}) {
+    for (bool read_and_writes_synchronous : {true}) {
+      host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+
+      MockWrite writes[] = {
+          MockWrite(SYNCHRONOUS, kSOCKS4OkRequestLocalHostPort80,
+                    kSOCKS4OkRequestLocalHostPort80Length, 0),
+      };
+
+      MockRead reads[] = {
+          MockRead(SYNCHRONOUS, kSOCKS4OkReply, kSOCKS4OkReplyLength, 1),
+      };
+
+      SequencedSocketData sequenced_socket_data(reads, writes);
+      // Host resolution is used to switch between sync and async connection
+      // behavior. The SOCKS layer can't distinguish between sync and async host
+      // resolution vs sync and async connection establishment, so just always
+      // make connection establishment synchroonous.
+      sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+      client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
+
+      TestConnectJobDelegate test_delegate;
+      SOCKSConnectJob socks_connect_job(
+          kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+          true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V4),
+          &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+      test_delegate.StartJobExpectingResult(
+          &socks_connect_job, OK,
+          host_resolution_synchronous && read_and_writes_synchronous);
+    }
+  }
+}
+
+TEST_F(SOCKSConnectJobTest, SOCKS5) {
+  for (bool host_resolution_synchronous : {false, true}) {
+    for (bool read_and_writes_synchronous : {true}) {
+      host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+
+      MockWrite writes[] = {
+          MockWrite(SYNCHRONOUS, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength,
+                    0),
+          MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 2),
+      };
+
+      MockRead reads[] = {
+          MockRead(SYNCHRONOUS, kSOCKS5GreetResponse,
+                   kSOCKS5GreetResponseLength, 1),
+          MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 3),
+      };
+
+      SequencedSocketData sequenced_socket_data(reads, writes);
+      // Host resolution is used to switch between sync and async connection
+      // behavior. The SOCKS layer can't distinguish between sync and async host
+      // resolution vs sync and async connection establishment, so just always
+      // make connection establishment synchroonous.
+      sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+      client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
+
+      TestConnectJobDelegate test_delegate;
+      SOCKSConnectJob socks_connect_job(
+          kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+          true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+          &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+      test_delegate.StartJobExpectingResult(
+          &socks_connect_job, OK,
+          host_resolution_synchronous && read_and_writes_synchronous);
+    }
+  }
+}
+
+// Check that TransportConnectJob's timeout is respected for the nested
+// TransportConnectJob.
+TEST_F(SOCKSConnectJobTest, TimeoutDuringDnsResolution) {
+  // Set HostResolver to hang.
+  host_resolver_.set_ondemand_mode(true);
+
+  TestConnectJobDelegate test_delegate;
+  SOCKSConnectJob socks_connect_job(
+      kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+      &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+  socks_connect_job.Connect();
+
+  // Just before the TransportConnectJob's timeout, nothing should have
+  // happened.
+  FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
+  EXPECT_TRUE(host_resolver_.has_pending_requests());
+  EXPECT_FALSE(test_delegate.has_result());
+
+  // Wait for exactly the TransportConnectJob's timeout to have passed. The Job
+  // should time out.
+  FastForwardBy(kTinyTime);
+  EXPECT_FALSE(host_resolver_.has_pending_requests());
+  EXPECT_TRUE(test_delegate.has_result());
+  EXPECT_THAT(test_delegate.WaitForResult(),
+              test::IsError(ERR_PROXY_CONNECTION_FAILED));
+}
+
+// Check that SOCKSConnectJob's timeout is respected for the handshake phase.
+TEST_F(SOCKSConnectJobTest, TimeoutDuringHandshake) {
+  // This test assumes TransportConnectJobs have a shorter timeout than
+  // SOCKSConnectJobs.
+  ASSERT_LT(TransportConnectJob::ConnectionTimeout(),
+            SOCKSConnectJob::ConnectionTimeout());
+
+  host_resolver_.set_ondemand_mode(true);
+
+  MockWrite writes[] = {
+      MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0),
+  };
+
+  SequencedSocketData sequenced_socket_data(base::span<MockRead>(), writes);
+  sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
+
+  TestConnectJobDelegate test_delegate;
+  SOCKSConnectJob socks_connect_job(
+      kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+      &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+  socks_connect_job.Connect();
+
+  // Just before the TransportConnectJob's timeout, nothing should have
+  // happened.
+  FastForwardBy(TransportConnectJob::ConnectionTimeout() - kTinyTime);
+  EXPECT_FALSE(test_delegate.has_result());
+  EXPECT_TRUE(host_resolver_.has_pending_requests());
+
+  // DNS resolution completes, and the socket connects.  The request should not
+  // time out, even after the TransportConnectJob's timeout passes.
+  host_resolver_.ResolveAllPending();
+
+  // The timer is now restarted with a value of
+  // SOCKSConnectJob::ConnectionTimeout() -
+  // TransportConnectJob::ConnectionTimeout(). Waiting until almost that much
+  // time has passed should cause no observable change in the SOCKSConnectJob's
+  // status.
+  FastForwardBy(SOCKSConnectJob::ConnectionTimeout() -
+                TransportConnectJob::ConnectionTimeout() - kTinyTime);
+  EXPECT_FALSE(test_delegate.has_result());
+
+  // Wait for exactly the SOCKSConnectJob's timeout has fully elapsed. The Job
+  // should time out.
+  FastForwardBy(kTinyTime);
+  EXPECT_FALSE(host_resolver_.has_pending_requests());
+  EXPECT_TRUE(test_delegate.has_result());
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
+}
+
+// Check initial priority is passed to the HostResolver, and priority can be
+// modified.
+TEST_F(SOCKSConnectJobTest, Priority) {
+  host_resolver_.set_ondemand_mode(true);
+  // Make resolution eventually fail, so old jobs can easily be removed from the
+  // socket pool.
+  host_resolver_.rules()->AddSimulatedFailure(kProxyHostName);
+  for (int initial_priority = MINIMUM_PRIORITY;
+       initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
+    for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
+         ++new_priority) {
+      // Don't try changing priority to itself, as APIs may not allow that.
+      if (new_priority == initial_priority)
+        continue;
+      TestConnectJobDelegate test_delegate;
+      SOCKSConnectJob socks_connect_job(
+          kSOCKS5TestHost /* group_name */,
+          static_cast<RequestPriority>(initial_priority), SocketTag(),
+          true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V4),
+          &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+      ASSERT_THAT(socks_connect_job.Connect(), test::IsError(ERR_IO_PENDING));
+      ASSERT_TRUE(host_resolver_.has_pending_requests());
+      int request_id = host_resolver_.num_resolve();
+      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
+
+      // Change priority.
+      socks_connect_job.ChangePriority(
+          static_cast<RequestPriority>(new_priority));
+      EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));
+
+      // Restore initial priority.
+      socks_connect_job.ChangePriority(
+          static_cast<RequestPriority>(initial_priority));
+      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
+
+      // Complete the resolution, which should result in emptying the
+      // TransportSocketPool.
+      host_resolver_.ResolveAllPending();
+      ASSERT_THAT(test_delegate.WaitForResult(),
+                  test::IsError(ERR_PROXY_CONNECTION_FAILED));
+    }
+  }
+}
+
+TEST_F(SOCKSConnectJobTest, ConnectTiming) {
+  host_resolver_.set_ondemand_mode(true);
+
+  MockWrite writes[] = {
+      MockWrite(ASYNC, ERR_IO_PENDING, 0),
+      MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength, 1),
+      MockWrite(SYNCHRONOUS, kSOCKS5OkRequest, kSOCKS5OkRequestLength, 3),
+  };
+
+  MockRead reads[] = {
+      MockRead(SYNCHRONOUS, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength,
+               2),
+      MockRead(SYNCHRONOUS, kSOCKS5OkResponse, kSOCKS5OkResponseLength, 4),
+  };
+
+  SequencedSocketData sequenced_socket_data(reads, writes);
+  // Host resolution is used to switch between sync and async connection
+  // behavior. The SOCKS layer can't distinguish between sync and async host
+  // resolution vs sync and async connection establishment, so just always
+  // make connection establishment synchroonous.
+  sequenced_socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+  client_socket_factory_.AddSocketDataProvider(&sequenced_socket_data);
+
+  TestConnectJobDelegate test_delegate;
+  SOCKSConnectJob socks_connect_job(
+      kSOCKS5TestHost /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, CreateSOCKSParams(SOCKSVersion::V5),
+      &transport_pool_, &host_resolver_, &test_delegate, &net_log_);
+  base::TimeTicks start = base::TimeTicks::Now();
+  socks_connect_job.Connect();
+
+  // DNS resolution completes after a short delay. The connection should be
+  // immediately established as well. The first write to the socket stalls.
+  FastForwardBy(kTinyTime);
+  host_resolver_.ResolveAllPending();
+  RunUntilIdle();
+
+  // After another short delay, data is received from the server.
+  FastForwardBy(kTinyTime);
+  sequenced_socket_data.Resume();
+
+  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
+  // Proxy name resolution is not considered resolving the host name for
+  // ConnectionInfo. For SOCKS4, where the host name is also looked up via DNS,
+  // the resolution time is not currently reported.
+  EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().dns_start);
+  EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().dns_end);
+
+  // The "connect" time for socks proxies includes DNS resolution time.
+  EXPECT_EQ(start, socks_connect_job.connect_timing().connect_start);
+  EXPECT_EQ(start + 2 * kTinyTime,
+            socks_connect_job.connect_timing().connect_end);
+
+  // Since SSL was not negotiated, SSL times are null.
+  EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_start);
+  EXPECT_EQ(base::TimeTicks(), socks_connect_job.connect_timing().ssl_end);
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 1489d58c..190b096 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -25,6 +25,7 @@
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/socks_client_socket_pool.h"
+#include "net/socket/socks_connect_job.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/socket/transport_connect_job.h"
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 739a0c4..07fa615 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -33,6 +33,7 @@
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
+#include "net/socket/socks_connect_job.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/socket/transport_connect_job.h"
 #include "net/spdy/spdy_session.h"
diff --git a/net/third_party/quic/core/http/quic_spdy_stream.cc b/net/third_party/quic/core/http/quic_spdy_stream.cc
index 9fcf9b8..d8f6a78 100644
--- a/net/third_party/quic/core/http/quic_spdy_stream.cc
+++ b/net/third_party/quic/core/http/quic_spdy_stream.cc
@@ -176,19 +176,23 @@
     QuicStringPiece data,
     bool fin,
     QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
-  if (spdy_session_->connection()->transport_version() == QUIC_VERSION_99 &&
-      data.length() > 0) {
-    std::unique_ptr<char[]> buffer;
-    QuicByteCount header_length =
-        encoder_.SerializeDataFrameHeader(data.length(), &buffer);
-    WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false,
-                      nullptr);
-    QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length "
-                    << header_length;
-    total_header_bytes_written_ += header_length;
+  if (spdy_session_->connection()->transport_version() != QUIC_VERSION_99 ||
+      data.length() == 0) {
+    WriteOrBufferData(data, fin, std::move(ack_listener));
+    return;
   }
+  QuicConnection::ScopedPacketFlusher flusher(
+      spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount header_length =
+      encoder_.SerializeDataFrameHeader(data.length(), &buffer);
+  WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false,
+                    nullptr);
+  QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length "
+                  << header_length;
+  total_header_bytes_written_ += header_length;
   WriteOrBufferData(data, fin, std::move(ack_listener));
-  QUIC_DLOG(INFO) << "Stream" << id() << " is writing body of length "
+  QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length "
                   << data.length();
 }
 
@@ -254,6 +258,8 @@
     return {0, false};
   }
 
+  QuicConnection::ScopedPacketFlusher flusher(
+      spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
   struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length};
   QuicMemSliceStorage storage(
       &header_iov, 1,
diff --git a/net/third_party/quic/tools/quic_simple_server_stream_test.cc b/net/third_party/quic/tools/quic_simple_server_stream_test.cc
index 65a343e..727cf94 100644
--- a/net/third_party/quic/tools/quic_simple_server_stream_test.cc
+++ b/net/third_party/quic/tools/quic_simple_server_stream_test.cc
@@ -200,6 +200,7 @@
         quic_response_(new QuicBackendResponse),
         body_("hello world"),
         is_verion_99_(connection_->transport_version() == QUIC_VERSION_99) {
+    connection_->set_visitor(&session_);
     header_list_.OnHeaderBlockStart();
     header_list_.OnHeader(":authority", "www.google.com");
     header_list_.OnHeader(":path", "/");
diff --git a/net/third_party/spdy/core/array_output_buffer.h b/net/third_party/spdy/core/array_output_buffer.h
index c361846..206a2813 100644
--- a/net/third_party/spdy/core/array_output_buffer.h
+++ b/net/third_party/spdy/core/array_output_buffer.h
@@ -37,7 +37,7 @@
  private:
   char* current_ = nullptr;
   char* begin_ = nullptr;
-  int64_t capacity_ = 0;
+  uint64_t capacity_ = 0;
 };
 
 }  // namespace spdy
diff --git a/net/third_party/spdy/core/hpack/hpack_constants.h b/net/third_party/spdy/core/hpack/hpack_constants.h
index aaba7a2f..860bc608 100644
--- a/net/third_party/spdy/core/hpack/hpack_constants.h
+++ b/net/third_party/spdy/core/hpack/hpack_constants.h
@@ -7,7 +7,6 @@
 
 #include <cstddef>
 #include <cstdint>
-
 #include <vector>
 
 #include "net/third_party/spdy/platform/api/spdy_export.h"
diff --git a/net/third_party/spdy/core/hpack/hpack_encoder_test.cc b/net/third_party/spdy/core/hpack/hpack_encoder_test.cc
index afe0e64..a8c6915 100644
--- a/net/third_party/spdy/core/hpack/hpack_encoder_test.cc
+++ b/net/third_party/spdy/core/hpack/hpack_encoder_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/spdy/core/hpack/hpack_encoder.h"
 
+#include <cstdint>
 #include <map>
 
 #include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
diff --git a/net/third_party/spdy/core/hpack/hpack_entry.h b/net/third_party/spdy/core/hpack/hpack_entry.h
index ce11434..9829b91d 100644
--- a/net/third_party/spdy/core/hpack/hpack_entry.h
+++ b/net/third_party/spdy/core/hpack/hpack_entry.h
@@ -6,6 +6,7 @@
 #define NET_THIRD_PARTY_SPDY_CORE_HPACK_HPACK_ENTRY_H_
 
 #include <cstddef>
+#include <cstdint>
 
 #include "base/macros.h"
 #include "net/third_party/spdy/platform/api/spdy_export.h"
diff --git a/net/third_party/spdy/core/hpack/hpack_header_table.h b/net/third_party/spdy/core/hpack/hpack_header_table.h
index e97a81b..57c10760 100644
--- a/net/third_party/spdy/core/hpack/hpack_header_table.h
+++ b/net/third_party/spdy/core/hpack/hpack_header_table.h
@@ -6,9 +6,9 @@
 #define NET_THIRD_PARTY_SPDY_CORE_HPACK_HPACK_HEADER_TABLE_H_
 
 #include <cstddef>
+#include <cstdint>
 #include <deque>
 #include <memory>
-#include <utility>
 
 #include "base/macros.h"
 #include "net/third_party/spdy/core/hpack/hpack_entry.h"
diff --git a/net/third_party/spdy/core/hpack/hpack_header_table_test.cc b/net/third_party/spdy/core/hpack/hpack_header_table_test.cc
index d7cb23f..8cb2e4e 100644
--- a/net/third_party/spdy/core/hpack/hpack_header_table_test.cc
+++ b/net/third_party/spdy/core/hpack/hpack_header_table_test.cc
@@ -5,6 +5,7 @@
 #include "net/third_party/spdy/core/hpack/hpack_header_table.h"
 
 #include <algorithm>
+#include <cstdint>
 #include <set>
 #include <vector>
 
diff --git a/net/third_party/spdy/core/hpack/hpack_huffman_table.cc b/net/third_party/spdy/core/hpack/hpack_huffman_table.cc
index cba01ba0..81afac0b 100644
--- a/net/third_party/spdy/core/hpack/hpack_huffman_table.cc
+++ b/net/third_party/spdy/core/hpack/hpack_huffman_table.cc
@@ -6,10 +6,10 @@
 
 #include <algorithm>
 #include <cmath>
+#include <limits>
 #include <memory>
 
 #include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
 #include "net/third_party/spdy/core/hpack/hpack_output_stream.h"
 #include "net/third_party/spdy/platform/api/spdy_estimate_memory_usage.h"
 
@@ -37,7 +37,7 @@
 bool HpackHuffmanTable::Initialize(const HpackHuffmanSymbol* input_symbols,
                                    size_t symbol_count) {
   CHECK(!IsInitialized());
-  DCHECK(base::IsValueInRangeForNumericType<uint16_t>(symbol_count));
+  DCHECK_LE(symbol_count, std::numeric_limits<uint16_t>::max());
 
   std::vector<Symbol> symbols(symbol_count);
   // Validate symbol id sequence, and copy into |symbols|.
diff --git a/net/third_party/spdy/core/hpack/hpack_huffman_table.h b/net/third_party/spdy/core/hpack/hpack_huffman_table.h
index c88f377..d0ce178 100644
--- a/net/third_party/spdy/core/hpack/hpack_huffman_table.h
+++ b/net/third_party/spdy/core/hpack/hpack_huffman_table.h
@@ -5,7 +5,6 @@
 #ifndef NET_THIRD_PARTY_SPDY_CORE_HPACK_HPACK_HUFFMAN_TABLE_H_
 #define NET_THIRD_PARTY_SPDY_CORE_HPACK_HPACK_HUFFMAN_TABLE_H_
 
-
 #include <cstddef>
 #include <cstdint>
 #include <vector>
diff --git a/net/third_party/spdy/core/hpack/hpack_output_stream.cc b/net/third_party/spdy/core/hpack/hpack_output_stream.cc
index 0d2b5634..d578cc9 100644
--- a/net/third_party/spdy/core/hpack/hpack_output_stream.cc
+++ b/net/third_party/spdy/core/hpack/hpack_output_stream.cc
@@ -27,11 +27,11 @@
   } else if (new_bit_offset <= 8) {
     // Buffer does not end on a byte boundary but the given bits fit
     // in the remainder of the last byte.
-    *buffer_.rbegin() |= bits << (8 - new_bit_offset);
+    buffer_.back() |= bits << (8 - new_bit_offset);
   } else {
     // Buffer does not end on a byte boundary and the given bits do
     // not fit in the remainder of the last byte.
-    *buffer_.rbegin() |= bits >> (new_bit_offset - 8);
+    buffer_.back() |= bits >> (new_bit_offset - 8);
     buffer_.append(1, bits << (16 - new_bit_offset));
   }
   bit_offset_ = new_bit_offset % 8;
diff --git a/net/third_party/spdy/core/hpack/hpack_output_stream.h b/net/third_party/spdy/core/hpack/hpack_output_stream.h
index f6f0751..309da2b 100644
--- a/net/third_party/spdy/core/hpack/hpack_output_stream.h
+++ b/net/third_party/spdy/core/hpack/hpack_output_stream.h
@@ -6,7 +6,6 @@
 #define NET_THIRD_PARTY_SPDY_CORE_HPACK_HPACK_OUTPUT_STREAM_H_
 
 #include <cstdint>
-
 #include <map>
 
 #include "base/macros.h"
diff --git a/net/third_party/spdy/core/http2_frame_decoder_adapter.cc b/net/third_party/spdy/core/http2_frame_decoder_adapter.cc
index 63e1d02..f039ee1 100644
--- a/net/third_party/spdy/core/http2_frame_decoder_adapter.cc
+++ b/net/third_party/spdy/core/http2_frame_decoder_adapter.cc
@@ -16,7 +16,6 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "build/build_config.h"
 #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
 #include "net/third_party/quiche/src/http2/decoder/decode_status.h"
 #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h"
@@ -28,7 +27,6 @@
 #include "net/third_party/spdy/core/hpack/hpack_header_table.h"
 #include "net/third_party/spdy/core/spdy_alt_svc_wire_format.h"
 #include "net/third_party/spdy/core/spdy_bug_tracker.h"
-#include "net/third_party/spdy/core/spdy_frame_builder.h"
 #include "net/third_party/spdy/core/spdy_header_block.h"
 #include "net/third_party/spdy/core/spdy_headers_handler_interface.h"
 #include "net/third_party/spdy/core/spdy_protocol.h"
@@ -57,9 +55,6 @@
 namespace http2 {
 namespace {
 
-using SpdyFramerError = Http2DecoderAdapter::SpdyFramerError;
-using SpdyState = Http2DecoderAdapter::SpdyState;
-
 const bool kHasPriorityFields = true;
 const bool kNotHasPriorityFields = false;
 
@@ -75,7 +70,7 @@
 uint64_t ToSpdyPingId(const Http2PingFields& ping) {
   uint64_t v;
   std::memcpy(&v, ping.opaque_bytes, Http2PingFields::EncodedSize());
-  return base::NetToHost64(v);
+  return spdy::SpdyNetToHost64(v);
 }
 
 // Overwrites the fields of the header with invalid values, for the purpose
diff --git a/net/third_party/spdy/core/http2_frame_decoder_adapter.h b/net/third_party/spdy/core/http2_frame_decoder_adapter.h
index 10a5a4e..47db5d39 100644
--- a/net/third_party/spdy/core/http2_frame_decoder_adapter.h
+++ b/net/third_party/spdy/core/http2_frame_decoder_adapter.h
@@ -10,8 +10,8 @@
 #include <cstdint>
 #include <memory>
 
-#include "base/optional.h"
 #include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_optional.h"
 #include "net/third_party/spdy/core/hpack/hpack_decoder_adapter.h"
 #include "net/third_party/spdy/core/hpack/hpack_header_table.h"
 #include "net/third_party/spdy/core/spdy_alt_svc_wire_format.h"
@@ -245,7 +245,7 @@
 
   // Amount of trailing padding. Currently used just as an indicator of whether
   // OnPadLength has been called.
-  base::Optional<size_t> opt_pad_length_;
+  Http2Optional<size_t> opt_pad_length_;
 
   // Temporary buffers for the AltSvc fields.
   Http2String alt_svc_origin_;
diff --git a/net/third_party/spdy/core/spdy_alt_svc_wire_format_test.cc b/net/third_party/spdy/core/spdy_alt_svc_wire_format_test.cc
index d2c54e8..d1c1585 100644
--- a/net/third_party/spdy/core/spdy_alt_svc_wire_format_test.cc
+++ b/net/third_party/spdy/core/spdy_alt_svc_wire_format_test.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "testing/platform_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
 
diff --git a/net/third_party/spdy/core/spdy_deframer_visitor.cc b/net/third_party/spdy/core/spdy_deframer_visitor.cc
index 2545b3b..4d4602e 100644
--- a/net/third_party/spdy/core/spdy_deframer_visitor.cc
+++ b/net/third_party/spdy/core/spdy_deframer_visitor.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <limits>
+#include <memory>
 
 #include "base/logging.h"
 #include "net/third_party/spdy/core/hpack/hpack_constants.h"
@@ -19,6 +20,7 @@
 #include "net/third_party/spdy/platform/api/spdy_flags.h"
 #include "net/third_party/spdy/platform/api/spdy_ptr_util.h"
 #include "net/third_party/spdy/platform/api/spdy_string_piece.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 using ::testing::AssertionFailure;
 using ::testing::AssertionResult;
@@ -32,8 +34,7 @@
 enum class HeaderDirection { REQUEST, RESPONSE };
 
 // Types of HTTP/2 frames, per RFC 7540.
-// TODO(jamessynge): Switch to using
-// //net/third_party/quiche/src/http2/http2_constants.h when ready.
+// TODO(jamessynge): Switch to using http2/http2_constants.h when ready.
 enum Http2FrameType {
   DATA = 0,
   HEADERS = 1,
@@ -52,8 +53,7 @@
   UNKNOWN = -2,
 };
 
-// TODO(jamessynge): Switch to using
-// //net/third_party/quiche/src/http2/http2_constants.h when ready.
+// TODO(jamessynge): Switch to using http2/http2_constants.h when ready.
 const char* Http2FrameTypeToString(Http2FrameType v) {
   switch (v) {
     case DATA:
@@ -87,8 +87,7 @@
   }
 }
 
-// TODO(jamessynge): Switch to using
-// //net/third_party/quiche/src/http2/http2_constants.h when ready.
+// TODO(jamessynge): Switch to using http2/http2_constants.h when ready.
 inline std::ostream& operator<<(std::ostream& out, Http2FrameType v) {
   return out << Http2FrameTypeToString(v);
 }
@@ -97,8 +96,7 @@
 // (see https://httpwg.github.io/specs/rfc7540.html#FrameHeader for details on
 // the fixed 9-octet header structure shared by all frames).
 // Flag bits are only valid for specified frame types.
-// TODO(jamessynge): Switch to using
-// //net/third_party/quiche/src/http2/http2_constants.h when ready.
+// TODO(jamessynge): Switch to using http2/http2_constants.h when ready.
 enum Http2HeaderFlag {
   NO_FLAGS = 0,
 
@@ -110,8 +108,7 @@
 };
 
 // Returns name of frame type.
-// TODO(jamessynge): Switch to using
-// //net/third_party/quiche/src/http2/http2_constants.h when ready.
+// TODO(jamessynge): Switch to using http2/http2_constants.h when ready.
 const char* Http2FrameTypeToString(Http2FrameType v);
 
 void SpdyDeframerVisitorInterface::OnPingAck(
@@ -773,7 +770,7 @@
 void SpdyTestDeframerImpl::OnHeaderBlockEnd(
     size_t /* header_bytes_parsed */,
     size_t /* compressed_header_bytes_parsed */) {
-  CHECK(headers_);
+  CHECK(headers_ != nullptr);
   CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION ||
         frame_type_ == PUSH_PROMISE)
       << "   frame_type_=" << Http2FrameTypeToString(frame_type_);
@@ -901,18 +898,18 @@
   return *this;
 }
 
-::testing::AssertionResult CollectedFrame::VerifyHasHeaders(
+AssertionResult CollectedFrame::VerifyHasHeaders(
     const StringPairVector& expected_headers) const {
   VERIFY_NE(headers.get(), nullptr);
   VERIFY_THAT(*headers, ::testing::ContainerEq(expected_headers));
-  return ::testing::AssertionSuccess();
+  return AssertionSuccess();
 }
 
-::testing::AssertionResult CollectedFrame::VerifyHasSettings(
+AssertionResult CollectedFrame::VerifyHasSettings(
     const SettingVector& expected_settings) const {
   VERIFY_NE(settings.get(), nullptr);
   VERIFY_THAT(*settings, testing::ContainerEq(expected_settings));
-  return ::testing::AssertionSuccess();
+  return AssertionSuccess();
 }
 
 DeframerCallbackCollector::DeframerCallbackCollector(
diff --git a/net/third_party/spdy/core/spdy_frame_builder.h b/net/third_party/spdy/core/spdy_frame_builder.h
index 63096aad..3d296587 100644
--- a/net/third_party/spdy/core/spdy_frame_builder.h
+++ b/net/third_party/spdy/core/spdy_frame_builder.h
@@ -5,9 +5,8 @@
 #ifndef NET_THIRD_PARTY_SPDY_CORE_SPDY_FRAME_BUILDER_H_
 #define NET_THIRD_PARTY_SPDY_CORE_SPDY_FRAME_BUILDER_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
+#include <cstddef>
+#include <cstdint>
 #include <memory>
 
 #include "base/gtest_prod_util.h"
diff --git a/net/third_party/spdy/core/spdy_frame_builder_test.cc b/net/third_party/spdy/core/spdy_frame_builder_test.cc
index dc7fa9a..f353e56b 100644
--- a/net/third_party/spdy/core/spdy_frame_builder_test.cc
+++ b/net/third_party/spdy/core/spdy_frame_builder_test.cc
@@ -4,10 +4,12 @@
 
 #include "net/third_party/spdy/core/spdy_frame_builder.h"
 
+#include <memory>
+
 #include "net/third_party/spdy/core/array_output_buffer.h"
 #include "net/third_party/spdy/core/spdy_framer.h"
 #include "net/third_party/spdy/core/spdy_protocol.h"
-#include "testing/platform_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace spdy {
 
diff --git a/net/third_party/spdy/core/spdy_frame_reader.cc b/net/third_party/spdy/core/spdy_frame_reader.cc
index c2b70f5..e98bb2f5 100644
--- a/net/third_party/spdy/core/spdy_frame_reader.cc
+++ b/net/third_party/spdy/core/spdy_frame_reader.cc
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <limits>
-
 #include "net/third_party/spdy/core/spdy_frame_reader.h"
+
 #include "net/third_party/spdy/core/spdy_protocol.h"
 #include "net/third_party/spdy/platform/api/spdy_endianness_util.h"
 
diff --git a/net/third_party/spdy/core/spdy_frame_reader_test.cc b/net/third_party/spdy/core/spdy_frame_reader_test.cc
index f9e0364c..ca557ac 100644
--- a/net/third_party/spdy/core/spdy_frame_reader_test.cc
+++ b/net/third_party/spdy/core/spdy_frame_reader_test.cc
@@ -4,13 +4,11 @@
 
 #include "net/third_party/spdy/core/spdy_frame_reader.h"
 
-#include <algorithm>
-#include <iostream>
-#include <memory>
+#include <cstdint>
 
 #include "net/third_party/spdy/platform/api/spdy_arraysize.h"
 #include "net/third_party/spdy/platform/api/spdy_endianness_util.h"
-#include "testing/platform_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace spdy {
 
diff --git a/net/third_party/spdy/core/spdy_framer.cc b/net/third_party/spdy/core/spdy_framer.cc
index 9b84b79..8ca5503 100644
--- a/net/third_party/spdy/core/spdy_framer.cc
+++ b/net/third_party/spdy/core/spdy_framer.cc
@@ -5,16 +5,12 @@
 #include "net/third_party/spdy/core/spdy_framer.h"
 
 #include <algorithm>
-#include <cctype>
-#include <ios>
+#include <cstdint>
 #include <iterator>
 #include <list>
 #include <new>
 
-#include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_util.h"
 #include "net/third_party/spdy/core/hpack/hpack_constants.h"
 #include "net/third_party/spdy/core/spdy_bitmasks.h"
 #include "net/third_party/spdy/core/spdy_bug_tracker.h"
@@ -1265,7 +1261,7 @@
 }
 
 HpackEncoder* SpdyFramer::GetHpackEncoder() {
-  if (hpack_encoder_.get() == nullptr) {
+  if (hpack_encoder_ == nullptr) {
     hpack_encoder_ = SpdyMakeUnique<HpackEncoder>(ObtainHpackHuffmanTable());
     if (!compression_enabled()) {
       hpack_encoder_->DisableCompression();
diff --git a/net/third_party/spdy/core/spdy_framer.h b/net/third_party/spdy/core/spdy_framer.h
index 79d5bb8..4ea74dd 100644
--- a/net/third_party/spdy/core/spdy_framer.h
+++ b/net/third_party/spdy/core/spdy_framer.h
@@ -12,7 +12,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/sys_byteorder.h"
 #include "net/third_party/spdy/core/hpack/hpack_encoder.h"
 #include "net/third_party/spdy/core/spdy_alt_svc_wire_format.h"
 #include "net/third_party/spdy/core/spdy_header_block.h"
diff --git a/net/third_party/spdy/core/spdy_framer_test.cc b/net/third_party/spdy/core/spdy_framer_test.cc
index 196936c..0a7b8d44 100644
--- a/net/third_party/spdy/core/spdy_framer_test.cc
+++ b/net/third_party/spdy/core/spdy_framer_test.cc
@@ -7,14 +7,13 @@
 #include <stdlib.h>
 
 #include <algorithm>
+#include <cstdint>
 #include <limits>
 #include <tuple>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/third_party/spdy/core/array_output_buffer.h"
 #include "net/third_party/spdy/core/hpack/hpack_constants.h"
 #include "net/third_party/spdy/core/mock_spdy_framer_visitor.h"
@@ -30,7 +29,6 @@
 #include "net/third_party/spdy/platform/api/spdy_string_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
 
 using ::http2::Http2DecoderAdapter;
 using ::testing::_;
@@ -1595,7 +1593,6 @@
 
   {
     const char kDescription[] = "RST_STREAM frame with max stream ID";
-    // clang-format off
     const unsigned char kH2FrameData[] = {
         0x00, 0x00, 0x04,        // Length: 4
         0x03,                    //   Type: RST_STREAM
@@ -3819,7 +3816,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
 
@@ -3875,7 +3872,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     deframer_.set_visitor(&visitor);
@@ -3904,7 +3901,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     deframer_.set_visitor(&visitor);
@@ -3950,7 +3947,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
 
@@ -3981,7 +3978,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION);
@@ -4041,7 +4038,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     deframer_.set_visitor(&visitor);
@@ -4064,7 +4061,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
 
@@ -4091,7 +4088,7 @@
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     testing::StrictMock<test::MockDebugVisitor> debug_visitor;
@@ -4137,7 +4134,7 @@
       output_.Reset();
     }
     SCOPED_TRACE(testing::Message()
-                 << "Flags " << flags << std::hex << static_cast<int>(flags));
+                 << "Flags " << std::hex << static_cast<int>(flags));
 
     testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
     testing::StrictMock<test::MockDebugVisitor> debug_visitor;
diff --git a/net/third_party/spdy/core/spdy_header_block.h b/net/third_party/spdy/core/spdy_header_block.h
index 50d1ca1..cd76f5d7 100644
--- a/net/third_party/spdy/core/spdy_header_block.h
+++ b/net/third_party/spdy/core/spdy_header_block.h
@@ -8,7 +8,6 @@
 #include <stddef.h>
 
 #include <list>
-#include <map>
 #include <memory>
 #include <utility>
 #include <vector>
diff --git a/net/third_party/spdy/core/spdy_header_block_test.cc b/net/third_party/spdy/core/spdy_header_block_test.cc
index f54a8ca..c3cfdbda 100644
--- a/net/third_party/spdy/core/spdy_header_block_test.cc
+++ b/net/third_party/spdy/core/spdy_header_block_test.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/values.h"
 #include "net/third_party/spdy/core/spdy_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/net/third_party/spdy/core/spdy_pinnable_buffer_piece_test.cc b/net/third_party/spdy/core/spdy_pinnable_buffer_piece_test.cc
index b8ba7cad..8051d26 100644
--- a/net/third_party/spdy/core/spdy_pinnable_buffer_piece_test.cc
+++ b/net/third_party/spdy/core/spdy_pinnable_buffer_piece_test.cc
@@ -6,6 +6,7 @@
 
 #include "net/third_party/spdy/core/spdy_prefixed_buffer_reader.h"
 #include "net/third_party/spdy/platform/api/spdy_string.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace spdy {
diff --git a/net/third_party/spdy/core/spdy_protocol.h b/net/third_party/spdy/core/spdy_protocol.h
index 569c6f9..41a24ac 100644
--- a/net/third_party/spdy/core/spdy_protocol.h
+++ b/net/third_party/spdy/core/spdy_protocol.h
@@ -9,8 +9,8 @@
 #ifndef NET_THIRD_PARTY_SPDY_CORE_SPDY_PROTOCOL_H_
 #define NET_THIRD_PARTY_SPDY_CORE_SPDY_PROTOCOL_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
 
 #include <iosfwd>
 #include <limits>
@@ -19,10 +19,8 @@
 #include <new>
 #include <utility>
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/sys_byteorder.h"
 #include "net/third_party/spdy/core/spdy_alt_svc_wire_format.h"
 #include "net/third_party/spdy/core/spdy_bitmasks.h"
 #include "net/third_party/spdy/core/spdy_bug_tracker.h"
diff --git a/net/third_party/spdy/core/spdy_test_utils.cc b/net/third_party/spdy/core/spdy_test_utils.cc
index 602e664..e1bf36c 100644
--- a/net/third_party/spdy/core/spdy_test_utils.cc
+++ b/net/third_party/spdy/core/spdy_test_utils.cc
@@ -33,7 +33,7 @@
   }
 
   SpdyString hex;
-  for (const unsigned char *row = data; length > 0;
+  for (const unsigned char* row = data; length > 0;
        row += kColumns, length -= kColumns) {
     for (const unsigned char* p = row; p < row + 4; ++p) {
       if (p < row + length) {
diff --git a/net/third_party/spdy/core/spdy_test_utils.h b/net/third_party/spdy/core/spdy_test_utils.h
index d0b2346..7d5e704 100644
--- a/net/third_party/spdy/core/spdy_test_utils.h
+++ b/net/third_party/spdy/core/spdy_test_utils.h
@@ -5,9 +5,8 @@
 #ifndef NET_THIRD_PARTY_SPDY_CORE_SPDY_TEST_UTILS_H_
 #define NET_THIRD_PARTY_SPDY_CORE_SPDY_TEST_UTILS_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
+#include <cstddef>
+#include <cstdint>
 #include <map>
 #include <memory>
 
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index 0bdc246..4ae32548 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -17,7 +17,6 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "printing/page_size_margins.h"
 #include "printing/print_job_constants.h"
 #include "printing/print_settings.h"
 #include "printing/units.h"
@@ -26,20 +25,21 @@
 
 namespace {
 
-void GetCustomMarginsFromJobSettings(const base::DictionaryValue& settings,
-                                     PageSizeMargins* page_size_margins) {
-  const base::DictionaryValue* custom_margins;
-  if (!settings.GetDictionary(kSettingMarginsCustom, &custom_margins) ||
-      !custom_margins->GetDouble(kSettingMarginTop,
-                                 &page_size_margins->margin_top) ||
-      !custom_margins->GetDouble(kSettingMarginBottom,
-                                 &page_size_margins->margin_bottom) ||
-      !custom_margins->GetDouble(kSettingMarginLeft,
-                                 &page_size_margins->margin_left) ||
-      !custom_margins->GetDouble(kSettingMarginRight,
-                                 &page_size_margins->margin_right)) {
+void GetCustomMarginsFromJobSettings(const base::Value& settings,
+                                     PageMargins* page_size_margins) {
+  const base::Value* custom_margins = settings.FindKey(kSettingMarginsCustom);
+  if (!custom_margins) {
     NOTREACHED();
+    return;
   }
+  page_size_margins->top =
+      custom_margins->FindKey(kSettingMarginTop)->GetDouble();
+  page_size_margins->bottom =
+      custom_margins->FindKey(kSettingMarginBottom)->GetDouble();
+  page_size_margins->left =
+      custom_margins->FindKey(kSettingMarginLeft)->GetDouble();
+  page_size_margins->right =
+      custom_margins->FindKey(kSettingMarginRight)->GetDouble();
 }
 
 void SetMarginsToJobSettings(const std::string& json_path,
@@ -75,150 +75,139 @@
 
 }  // namespace
 
-bool PrintSettingsFromJobSettings(const base::DictionaryValue& job_settings,
+bool PrintSettingsFromJobSettings(const base::Value& job_settings,
                                   PrintSettings* settings) {
-  bool display_header_footer = false;
-  if (!job_settings.GetBoolean(kSettingHeaderFooterEnabled,
-                               &display_header_footer)) {
+  base::Optional<bool> display_header_footer =
+      job_settings.FindBoolKey(kSettingHeaderFooterEnabled);
+  if (!display_header_footer.has_value())
     return false;
-  }
-  settings->set_display_header_footer(display_header_footer);
 
+  settings->set_display_header_footer(display_header_footer.value());
   if (settings->display_header_footer()) {
-    base::string16 title;
-    base::string16 url;
-    if (!job_settings.GetString(kSettingHeaderFooterTitle, &title) ||
-        !job_settings.GetString(kSettingHeaderFooterURL, &url)) {
+    const std::string* title =
+        job_settings.FindStringKey(kSettingHeaderFooterTitle);
+    const std::string* url =
+        job_settings.FindStringKey(kSettingHeaderFooterURL);
+    if (!title || !url)
       return false;
-    }
-    settings->set_title(title);
-    settings->set_url(url);
+
+    settings->set_title(base::UTF8ToUTF16(*title));
+    settings->set_url(base::UTF8ToUTF16(*url));
   }
 
-  bool backgrounds = false;
-  bool selection_only = false;
-  if (!job_settings.GetBoolean(kSettingShouldPrintBackgrounds, &backgrounds) ||
-      !job_settings.GetBoolean(kSettingShouldPrintSelectionOnly,
-                               &selection_only)) {
+  base::Optional<bool> backgrounds =
+      job_settings.FindBoolKey(kSettingShouldPrintBackgrounds);
+  base::Optional<bool> selection_only =
+      job_settings.FindBoolKey(kSettingShouldPrintSelectionOnly);
+  if (!backgrounds.has_value() || !selection_only.has_value())
     return false;
-  }
-  settings->set_should_print_backgrounds(backgrounds);
-  settings->set_selection_only(selection_only);
+
+  settings->set_should_print_backgrounds(backgrounds.value());
+  settings->set_selection_only(selection_only.value());
 
   PrintSettings::RequestedMedia requested_media;
-  const base::DictionaryValue* media_size_value = NULL;
-  if (job_settings.GetDictionary(kSettingMediaSize, &media_size_value)) {
-    int width_microns = 0;
-    int height_microns = 0;
-    if (media_size_value->GetInteger(kSettingMediaSizeWidthMicrons,
-                                     &width_microns) &&
-        media_size_value->GetInteger(kSettingMediaSizeHeightMicrons,
-                                     &height_microns)) {
-      requested_media.size_microns = gfx::Size(width_microns, height_microns);
+  const base::Value* media_size_value = job_settings.FindKeyOfType(
+      kSettingMediaSize, base::Value::Type::DICTIONARY);
+  if (media_size_value) {
+    base::Optional<int> width_microns =
+        media_size_value->FindIntKey(kSettingMediaSizeWidthMicrons);
+    base::Optional<int> height_microns =
+        media_size_value->FindIntKey(kSettingMediaSizeHeightMicrons);
+    if (width_microns.has_value() && height_microns.has_value()) {
+      requested_media.size_microns =
+          gfx::Size(width_microns.value(), height_microns.value());
     }
-    std::string vendor_id;
-    if (media_size_value->GetString(kSettingMediaSizeVendorId, &vendor_id) &&
-        !vendor_id.empty()) {
-      requested_media.vendor_id = vendor_id;
-    }
+
+    const std::string* vendor_id =
+        media_size_value->FindStringKey(kSettingMediaSizeVendorId);
+    if (vendor_id && !vendor_id->empty())
+      requested_media.vendor_id = *vendor_id;
   }
   settings->set_requested_media(requested_media);
 
-  int margin_type = DEFAULT_MARGINS;
-  if (!job_settings.GetInteger(kSettingMarginsType, &margin_type) ||
-      (margin_type != DEFAULT_MARGINS &&
-       margin_type != NO_MARGINS &&
-       margin_type != CUSTOM_MARGINS &&
-       margin_type != PRINTABLE_AREA_MARGINS)) {
+  int margin_type =
+      job_settings.FindIntKey(kSettingMarginsType).value_or(DEFAULT_MARGINS);
+  if (margin_type != DEFAULT_MARGINS && margin_type != NO_MARGINS &&
+      margin_type != CUSTOM_MARGINS && margin_type != PRINTABLE_AREA_MARGINS) {
     margin_type = DEFAULT_MARGINS;
   }
   settings->set_margin_type(static_cast<MarginType>(margin_type));
 
   if (margin_type == CUSTOM_MARGINS) {
-    PageSizeMargins page_size_margins;
-    GetCustomMarginsFromJobSettings(job_settings, &page_size_margins);
-
     PageMargins margins_in_points;
     margins_in_points.Clear();
-    margins_in_points.top = page_size_margins.margin_top;
-    margins_in_points.bottom = page_size_margins.margin_bottom;
-    margins_in_points.left = page_size_margins.margin_left;
-    margins_in_points.right = page_size_margins.margin_right;
-
+    GetCustomMarginsFromJobSettings(job_settings, &margins_in_points);
     settings->SetCustomMargins(margins_in_points);
   }
 
   PageRanges new_ranges;
-  const base::ListValue* page_range_array = NULL;
-  if (job_settings.GetList(kSettingPageRange, &page_range_array)) {
-    for (size_t index = 0; index < page_range_array->GetSize(); ++index) {
-      const base::DictionaryValue* dict;
-      if (!page_range_array->GetDictionary(index, &dict))
+  const base::Value* page_range_array =
+      job_settings.FindKeyOfType(kSettingPageRange, base::Value::Type::LIST);
+  if (page_range_array) {
+    for (const base::Value& value : page_range_array->GetList()) {
+      if (!value.is_dict())
         continue;
 
-      PageRange range;
-      if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) ||
-          !dict->GetInteger(kSettingPageRangeTo, &range.to)) {
+      base::Optional<int> from = value.FindIntKey(kSettingPageRangeFrom);
+      base::Optional<int> to = value.FindIntKey(kSettingPageRangeTo);
+      if (!from.has_value() || !to.has_value())
         continue;
-      }
 
       // Page numbers are 1-based in the dictionary.
       // Page numbers are 0-based for the printing context.
-      range.from--;
-      range.to--;
-      new_ranges.push_back(range);
+      new_ranges.push_back(PageRange{from.value() - 1, to.value() - 1});
     }
   }
   settings->set_ranges(new_ranges);
 
-  int color = 0;
-  bool landscape = false;
-  int duplex_mode = 0;
-  base::string16 device_name;
-  bool collate = false;
-  int copies = 1;
-  int scale_factor = 100;
-  bool rasterize_pdf = false;
-  int pages_per_sheet = 1;
+  base::Optional<bool> collate = job_settings.FindBoolKey(kSettingCollate);
+  base::Optional<int> copies = job_settings.FindIntKey(kSettingCopies);
+  base::Optional<int> color = job_settings.FindIntKey(kSettingColor);
+  base::Optional<int> duplex_mode = job_settings.FindIntKey(kSettingDuplexMode);
+  base::Optional<bool> landscape = job_settings.FindBoolKey(kSettingLandscape);
+  const std::string* device_name =
+      job_settings.FindStringKey(kSettingDeviceName);
+  base::Optional<int> scale_factor =
+      job_settings.FindIntKey(kSettingScaleFactor);
+  base::Optional<bool> rasterize_pdf =
+      job_settings.FindBoolKey(kSettingRasterizePdf);
+  base::Optional<int> pages_per_sheet =
+      job_settings.FindIntKey(kSettingPagesPerSheet);
 
-  if (!job_settings.GetBoolean(kSettingCollate, &collate) ||
-      !job_settings.GetInteger(kSettingCopies, &copies) ||
-      !job_settings.GetInteger(kSettingColor, &color) ||
-      !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
-      !job_settings.GetBoolean(kSettingLandscape, &landscape) ||
-      !job_settings.GetString(kSettingDeviceName, &device_name) ||
-      !job_settings.GetInteger(kSettingScaleFactor, &scale_factor) ||
-      !job_settings.GetBoolean(kSettingRasterizePdf, &rasterize_pdf) ||
-      !job_settings.GetInteger(kSettingPagesPerSheet, &pages_per_sheet)) {
+  if (!collate.has_value() || !copies.has_value() || !color.has_value() ||
+      !duplex_mode.has_value() || !landscape.has_value() || !device_name ||
+      !scale_factor.has_value() || !rasterize_pdf.has_value() ||
+      !pages_per_sheet.has_value()) {
     return false;
   }
 #if defined(OS_WIN)
-  int dpi_horizontal = 0;
-  int dpi_vertical = 0;
-  if (!job_settings.GetInteger(kSettingDpiHorizontal, &dpi_horizontal) ||
-      !job_settings.GetInteger(kSettingDpiVertical, &dpi_vertical)) {
+  base::Optional<int> dpi_horizontal =
+      job_settings.FindIntKey(kSettingDpiHorizontal);
+  base::Optional<int> dpi_vertical =
+      job_settings.FindIntKey(kSettingDpiVertical);
+  if (!dpi_horizontal.has_value() || !dpi_vertical.has_value())
     return false;
-  }
-  settings->set_dpi_xy(dpi_horizontal, dpi_vertical);
+
+  settings->set_dpi_xy(dpi_horizontal.value(), dpi_vertical.value());
 #endif
 
-  settings->set_collate(collate);
-  settings->set_copies(copies);
-  settings->SetOrientation(landscape);
-  settings->set_device_name(device_name);
-  settings->set_duplex_mode(static_cast<DuplexMode>(duplex_mode));
-  settings->set_color(static_cast<ColorModel>(color));
-  settings->set_scale_factor(static_cast<double>(scale_factor) / 100.0);
-  settings->set_rasterize_pdf(rasterize_pdf);
-  settings->set_pages_per_sheet(pages_per_sheet);
-  bool is_modifiable = false;
-  if (job_settings.GetBoolean(kSettingPreviewModifiable, &is_modifiable)) {
-    settings->set_is_modifiable(is_modifiable);
+  settings->set_collate(collate.value());
+  settings->set_copies(copies.value());
+  settings->SetOrientation(landscape.value());
+  settings->set_device_name(base::UTF8ToUTF16(*device_name));
+  settings->set_duplex_mode(static_cast<DuplexMode>(duplex_mode.value()));
+  settings->set_color(static_cast<ColorModel>(color.value()));
+  settings->set_scale_factor(static_cast<double>(scale_factor.value()) / 100.0);
+  settings->set_rasterize_pdf(rasterize_pdf.value());
+  settings->set_pages_per_sheet(pages_per_sheet.value());
+  base::Optional<bool> is_modifiable =
+      job_settings.FindBoolKey(kSettingPreviewModifiable);
+  if (is_modifiable.has_value()) {
+    settings->set_is_modifiable(is_modifiable.value());
 #if defined(OS_WIN)
-    settings->set_print_text_with_gdi(is_modifiable);
+    settings->set_print_text_with_gdi(is_modifiable.value());
 #endif
   }
-
   return true;
 }
 
diff --git a/printing/print_settings_conversion.h b/printing/print_settings_conversion.h
index 283c0ff..07b6b03 100644
--- a/printing/print_settings_conversion.h
+++ b/printing/print_settings_conversion.h
@@ -11,6 +11,7 @@
 
 namespace base {
 class DictionaryValue;
+class Value;
 }
 
 namespace printing {
@@ -18,7 +19,7 @@
 class PrintSettings;
 
 PRINTING_EXPORT bool PrintSettingsFromJobSettings(
-    const base::DictionaryValue& job_settings,
+    const base::Value& job_settings,
     PrintSettings* print_settings);
 
 // Use for debug only, because output is not completely consistent with format
diff --git a/printing/printing_context.cc b/printing/printing_context.cc
index 289b44b..1939a761 100644
--- a/printing/printing_context.cc
+++ b/printing/printing_context.cc
@@ -56,32 +56,31 @@
 }
 
 PrintingContext::Result PrintingContext::UsePdfSettings() {
-  std::unique_ptr<base::DictionaryValue> pdf_settings(
-      new base::DictionaryValue);
-  pdf_settings->SetBoolean(kSettingHeaderFooterEnabled, false);
-  pdf_settings->SetBoolean(kSettingShouldPrintBackgrounds, false);
-  pdf_settings->SetBoolean(kSettingShouldPrintSelectionOnly, false);
-  pdf_settings->SetInteger(kSettingMarginsType, printing::NO_MARGINS);
-  pdf_settings->SetBoolean(kSettingCollate, true);
-  pdf_settings->SetInteger(kSettingCopies, 1);
-  pdf_settings->SetInteger(kSettingColor, printing::COLOR);
-  pdf_settings->SetInteger(kSettingDpiHorizontal, kPointsPerInch);
-  pdf_settings->SetInteger(kSettingDpiVertical, kPointsPerInch);
-  pdf_settings->SetInteger(kSettingDuplexMode, printing::SIMPLEX);
-  pdf_settings->SetBoolean(kSettingLandscape, false);
-  pdf_settings->SetString(kSettingDeviceName, "");
-  pdf_settings->SetBoolean(kSettingPrintToPDF, true);
-  pdf_settings->SetBoolean(kSettingCloudPrintDialog, false);
-  pdf_settings->SetBoolean(kSettingPrintWithPrivet, false);
-  pdf_settings->SetBoolean(kSettingPrintWithExtension, false);
-  pdf_settings->SetInteger(kSettingScaleFactor, 100);
-  pdf_settings->SetBoolean(kSettingRasterizePdf, false);
-  pdf_settings->SetInteger(kSettingPagesPerSheet, 1);
-  return UpdatePrintSettings(*pdf_settings);
+  base::Value pdf_settings(base::Value::Type::DICTIONARY);
+  pdf_settings.SetKey(kSettingHeaderFooterEnabled, base::Value(false));
+  pdf_settings.SetKey(kSettingShouldPrintBackgrounds, base::Value(false));
+  pdf_settings.SetKey(kSettingShouldPrintSelectionOnly, base::Value(false));
+  pdf_settings.SetKey(kSettingMarginsType, base::Value(printing::NO_MARGINS));
+  pdf_settings.SetKey(kSettingCollate, base::Value(true));
+  pdf_settings.SetKey(kSettingCopies, base::Value(1));
+  pdf_settings.SetKey(kSettingColor, base::Value(printing::COLOR));
+  pdf_settings.SetKey(kSettingDpiHorizontal, base::Value(kPointsPerInch));
+  pdf_settings.SetKey(kSettingDpiVertical, base::Value(kPointsPerInch));
+  pdf_settings.SetKey(kSettingDuplexMode, base::Value(printing::SIMPLEX));
+  pdf_settings.SetKey(kSettingLandscape, base::Value(false));
+  pdf_settings.SetKey(kSettingDeviceName, base::Value(""));
+  pdf_settings.SetKey(kSettingPrintToPDF, base::Value(true));
+  pdf_settings.SetKey(kSettingCloudPrintDialog, base::Value(false));
+  pdf_settings.SetKey(kSettingPrintWithPrivet, base::Value(false));
+  pdf_settings.SetKey(kSettingPrintWithExtension, base::Value(false));
+  pdf_settings.SetKey(kSettingScaleFactor, base::Value(100));
+  pdf_settings.SetKey(kSettingRasterizePdf, base::Value(false));
+  pdf_settings.SetKey(kSettingPagesPerSheet, base::Value(1));
+  return UpdatePrintSettings(std::move(pdf_settings));
 }
 
 PrintingContext::Result PrintingContext::UpdatePrintSettings(
-    const base::DictionaryValue& job_settings) {
+    base::Value job_settings) {
   ResetSettings();
 
   if (!PrintSettingsFromJobSettings(job_settings, &settings_)) {
@@ -89,23 +88,29 @@
     return OnError();
   }
 
-  bool print_to_pdf = false;
-  bool is_cloud_dialog = false;
-  bool print_with_privet = false;
-  bool print_with_extension = false;
+  base::Optional<bool> print_to_pdf_opt =
+      job_settings.FindBoolKey(kSettingPrintToPDF);
+  base::Optional<bool> is_cloud_dialog_opt =
+      job_settings.FindBoolKey(kSettingCloudPrintDialog);
+  base::Optional<bool> print_with_privet_opt =
+      job_settings.FindBoolKey(kSettingPrintWithPrivet);
+  base::Optional<bool> print_with_extension_opt =
+      job_settings.FindBoolKey(kSettingPrintWithExtension);
 
-  if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
-      !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog) ||
-      !job_settings.GetBoolean(kSettingPrintWithPrivet, &print_with_privet) ||
-      !job_settings.GetBoolean(kSettingPrintWithExtension,
-                               &print_with_extension)) {
+  if (!print_to_pdf_opt || !is_cloud_dialog_opt || !print_with_privet_opt ||
+      !print_with_extension_opt) {
     NOTREACHED();
     return OnError();
   }
 
-  bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
+  bool print_to_pdf = print_to_pdf_opt.value();
+  bool is_cloud_dialog = is_cloud_dialog_opt.value();
+  bool print_with_privet = print_with_privet_opt.value();
+  bool print_with_extension = print_with_extension_opt.value();
+
+  bool print_to_cloud = job_settings.FindKey(kSettingCloudPrintId) != nullptr;
   bool open_in_external_preview =
-      job_settings.HasKey(kSettingOpenPDFInPreview);
+      job_settings.FindKey(kSettingOpenPDFInPreview) != nullptr;
 
   if (!open_in_external_preview &&
       (print_to_pdf || print_to_cloud || is_cloud_dialog || print_with_privet ||
@@ -131,14 +136,10 @@
     return OK;
   }
 
-  bool show_system_dialog = false;
-  job_settings.GetBoolean(kSettingShowSystemDialog, &show_system_dialog);
-
-  int page_count = 0;
-  job_settings.GetInteger(kSettingPreviewPageCount, &page_count);
-
-  return UpdatePrinterSettings(open_in_external_preview, show_system_dialog,
-                               page_count);
+  return UpdatePrinterSettings(
+      open_in_external_preview,
+      job_settings.FindBoolKey(kSettingShowSystemDialog).value_or(false),
+      job_settings.FindIntKey(kSettingPreviewPageCount).value_or(0));
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/printing/printing_context.h b/printing/printing_context.h
index 88593790..1704d5a 100644
--- a/printing/printing_context.h
+++ b/printing/printing_context.h
@@ -11,14 +11,11 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/values.h"
 #include "printing/native_drawing_context.h"
 #include "printing/print_settings.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 // An abstraction of a printer context, implemented by objects that describe the
@@ -83,8 +80,8 @@
                                        int page_count) = 0;
 
   // Updates Print Settings. |job_settings| contains all print job
-  // settings information. |ranges| has the new page range settings.
-  Result UpdatePrintSettings(const base::DictionaryValue& job_settings);
+  // settings information.
+  Result UpdatePrintSettings(base::Value job_settings);
 
 #if defined(OS_CHROMEOS)
   // Updates Print Settings.
diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h
index 21b79729..3747a37 100644
--- a/remoting/host/chromoting_messages.h
+++ b/remoting/host/chromoting_messages.h
@@ -228,6 +228,9 @@
 
 IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_CaptureFrame)
 
+IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_SelectSource,
+                    int /* desktop_display_id */)
+
 // Carries a clipboard event from the client to the desktop session agent.
 // |serialized_event| is a serialized protocol::ClipboardEvent.
 IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index f53c161..4e921d36 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "remoting/base/capabilities.h"
@@ -237,6 +238,18 @@
   }
 }
 
+void ClientSession::SelectDesktopDisplay(
+    const protocol::SelectDesktopDisplayRequest& select_display) {
+  int id = webrtc::kFullDesktopScreenId;
+  if (select_display.id() != "all") {
+    if (!base::StringToInt(select_display.id().c_str(), &id)) {
+      // Default to fullscreen if unable to parse id.
+      id = webrtc::kFullDesktopScreenId;
+    }
+  }
+  video_stream_->SelectSource(id);
+}
+
 void ClientSession::OnConnectionAuthenticating() {
   event_handler_->OnSessionAuthenticating(this);
 }
diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h
index 7e42bdd..283bb463 100644
--- a/remoting/host/client_session.h
+++ b/remoting/host/client_session.h
@@ -113,6 +113,8 @@
   void RequestPairing(
       const remoting::protocol::PairingRequest& pairing_request) override;
   void DeliverClientMessage(const protocol::ExtensionMessage& message) override;
+  void SelectDesktopDisplay(
+      const protocol::SelectDesktopDisplayRequest& select_display) override;
 
   // protocol::ConnectionToClient::EventHandler interface.
   void OnConnectionAuthenticating() override;
diff --git a/remoting/host/desktop_capturer_proxy.cc b/remoting/host/desktop_capturer_proxy.cc
index 5600b29e..af68571a 100644
--- a/remoting/host/desktop_capturer_proxy.cc
+++ b/remoting/host/desktop_capturer_proxy.cc
@@ -42,6 +42,7 @@
   void Start();
   void SetSharedMemoryFactory(
       std::unique_ptr<webrtc::SharedMemoryFactory> shared_memory_factory);
+  void SelectSource(SourceId id);
   void CaptureFrame();
 
  private:
@@ -96,6 +97,13 @@
   }
 }
 
+void DesktopCapturerProxy::Core::SelectSource(SourceId id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (capturer_) {
+    capturer_->SelectSource(id);
+  }
+}
+
 void DesktopCapturerProxy::Core::CaptureFrame() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (capturer_) {
@@ -175,7 +183,10 @@
 }
 
 bool DesktopCapturerProxy::SelectSource(SourceId id) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+  capture_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Core::SelectSource, base::Unretained(core_.get()), id));
   return false;
 }
 
diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc
index a9dd0b98a..9ea3c1ef 100644
--- a/remoting/host/desktop_session_agent.cc
+++ b/remoting/host/desktop_session_agent.cc
@@ -181,6 +181,8 @@
     IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame,
                           OnCaptureFrame)
+      IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SelectSource,
+                          OnSelectSource)
       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
                           OnInjectClipboardEvent)
       IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent,
@@ -518,6 +520,11 @@
   video_capturer_->CaptureFrame();
 }
 
+void DesktopSessionAgent::OnSelectSource(int id) {
+  DCHECK(caller_task_runner_->BelongsToCurrentThread());
+  video_capturer_->SelectSource(id);
+}
+
 void DesktopSessionAgent::OnInjectClipboardEvent(
     const std::string& serialized_event) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h
index bbee154..81aac89 100644
--- a/remoting/host/desktop_session_agent.h
+++ b/remoting/host/desktop_session_agent.h
@@ -140,6 +140,9 @@
   // Handles CaptureFrame requests from the client.
   void OnCaptureFrame();
 
+  // Handles desktop display selection requests from the client.
+  void OnSelectSource(int id);
+
   // Handles event executor requests from the client.
   void OnInjectClipboardEvent(const std::string& serialized_event);
   void OnInjectKeyEvent(const std::string& serialized_event);
diff --git a/remoting/host/desktop_session_proxy.cc b/remoting/host/desktop_session_proxy.cc
index 8eace611..3fd812e 100644
--- a/remoting/host/desktop_session_proxy.cc
+++ b/remoting/host/desktop_session_proxy.cc
@@ -293,6 +293,12 @@
   }
 }
 
+bool DesktopSessionProxy::SelectSource(webrtc::DesktopCapturer::SourceId id) {
+  DCHECK(caller_task_runner_->BelongsToCurrentThread());
+  SendToDesktop(new ChromotingNetworkDesktopMsg_SelectSource(id));
+  return true;
+}
+
 void DesktopSessionProxy::SetVideoCapturer(
     const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
diff --git a/remoting/host/desktop_session_proxy.h b/remoting/host/desktop_session_proxy.h
index 2eeafb2f..ccaa367 100644
--- a/remoting/host/desktop_session_proxy.h
+++ b/remoting/host/desktop_session_proxy.h
@@ -115,6 +115,7 @@
   // APIs used to implement the webrtc::DesktopCapturer interface. These must be
   // called on the |video_capture_task_runner_| thread.
   void CaptureFrame();
+  bool SelectSource(webrtc::DesktopCapturer::SourceId id);
 
   // Stores |video_capturer| to be used to post captured video frames. Called on
   // the |video_capture_task_runner_| thread.
diff --git a/remoting/host/ipc_video_frame_capturer.cc b/remoting/host/ipc_video_frame_capturer.cc
index c3f9435..a6044f6b 100644
--- a/remoting/host/ipc_video_frame_capturer.cc
+++ b/remoting/host/ipc_video_frame_capturer.cc
@@ -47,8 +47,8 @@
 }
 
 bool IpcVideoFrameCapturer::SelectSource(SourceId id) {
-  NOTIMPLEMENTED();
-  return false;
+  desktop_session_proxy_->SelectSource(id);
+  return true;
 }
 
 }  // namespace remoting
diff --git a/remoting/proto/control.proto b/remoting/proto/control.proto
index 4b50cfa..1829b30 100644
--- a/remoting/proto/control.proto
+++ b/remoting/proto/control.proto
@@ -106,3 +106,9 @@
   // Layout for each video track.
   repeated VideoTrackLayout video_track = 1;
 }
+
+message SelectDesktopDisplayRequest {
+  // Identifier for display to select. Valid strings are "0", "1", ...
+  // The "all" string is used to select the entire desktop.
+  optional string id = 1;
+}
diff --git a/remoting/proto/internal.proto b/remoting/proto/internal.proto
index a02f551..e6f9c81 100644
--- a/remoting/proto/internal.proto
+++ b/remoting/proto/internal.proto
@@ -26,6 +26,7 @@
   optional PairingResponse pairing_response = 8;
   optional ExtensionMessage extension_message = 9;
   optional VideoLayout video_layout = 10;
+  optional SelectDesktopDisplayRequest select_display = 11;
 }
 
 // Defines an event message on the event channel.
diff --git a/remoting/protocol/client_control_dispatcher.cc b/remoting/protocol/client_control_dispatcher.cc
index c7837ec..ffc6beb 100644
--- a/remoting/protocol/client_control_dispatcher.cc
+++ b/remoting/protocol/client_control_dispatcher.cc
@@ -111,6 +111,13 @@
   message_pipe()->Send(&control_message, base::Closure());
 }
 
+void ClientControlDispatcher::SelectDesktopDisplay(
+    const SelectDesktopDisplayRequest& select_display) {
+  ControlMessage message;
+  message.mutable_select_display()->CopyFrom(select_display);
+  message_pipe()->Send(&message, base::Closure());
+}
+
 void ClientControlDispatcher::OnIncomingMessage(
     std::unique_ptr<CompoundBuffer> buffer) {
   DCHECK(client_stub_);
diff --git a/remoting/protocol/client_control_dispatcher.h b/remoting/protocol/client_control_dispatcher.h
index 2fd8456..c915ad9 100644
--- a/remoting/protocol/client_control_dispatcher.h
+++ b/remoting/protocol/client_control_dispatcher.h
@@ -37,6 +37,8 @@
   void SetCapabilities(const Capabilities& capabilities) override;
   void RequestPairing(const PairingRequest& pairing_request) override;
   void DeliverClientMessage(const ExtensionMessage& message) override;
+  void SelectDesktopDisplay(
+      const SelectDesktopDisplayRequest& select_display) override;
 
   // Sets the ClientStub that will be called for each incoming control
   // message. |client_stub| must outlive this object.
diff --git a/remoting/protocol/fake_connection_to_client.cc b/remoting/protocol/fake_connection_to_client.cc
index 910c18a..69edc4fd 100644
--- a/remoting/protocol/fake_connection_to_client.cc
+++ b/remoting/protocol/fake_connection_to_client.cc
@@ -32,6 +32,8 @@
   observer_ = observer;
 }
 
+void FakeVideoStream::SelectSource(int id) {};
+
 base::WeakPtr<FakeVideoStream> FakeVideoStream::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/remoting/protocol/fake_connection_to_client.h b/remoting/protocol/fake_connection_to_client.h
index 39440af..5078cb14 100644
--- a/remoting/protocol/fake_connection_to_client.h
+++ b/remoting/protocol/fake_connection_to_client.h
@@ -32,6 +32,7 @@
   void SetLosslessEncode(bool want_lossless) override;
   void SetLosslessColor(bool want_lossless) override;
   void SetObserver(Observer* observer) override;
+  void SelectSource(int id) override;
 
   Observer* observer() { return observer_; }
 
diff --git a/remoting/protocol/host_control_dispatcher.cc b/remoting/protocol/host_control_dispatcher.cc
index a4142c6..2b86a9f 100644
--- a/remoting/protocol/host_control_dispatcher.cc
+++ b/remoting/protocol/host_control_dispatcher.cc
@@ -93,6 +93,8 @@
     host_stub_->RequestPairing(message->pairing_request());
   } else if (message->has_extension_message()) {
     host_stub_->DeliverClientMessage(message->extension_message());
+  } else if (message->has_select_display()) {
+    host_stub_->SelectDesktopDisplay(message->select_display());
   } else {
     LOG(WARNING) << "Unknown control message received.";
   }
diff --git a/remoting/protocol/host_stub.h b/remoting/protocol/host_stub.h
index f312ab5..cf75777 100644
--- a/remoting/protocol/host_stub.h
+++ b/remoting/protocol/host_stub.h
@@ -19,6 +19,7 @@
 class ClientResolution;
 class ExtensionMessage;
 class PairingRequest;
+class SelectDesktopDisplayRequest;
 class VideoControl;
 
 class HostStub {
@@ -46,6 +47,10 @@
   // Deliver an extension message from the client to the host.
   virtual void DeliverClientMessage(const ExtensionMessage& message) = 0;
 
+  // Select the specified host display.
+  virtual void SelectDesktopDisplay(
+      const SelectDesktopDisplayRequest& select_display) = 0;
+
  protected:
   virtual ~HostStub() {}
 
diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h
index ffea02d..a9c0f16 100644
--- a/remoting/protocol/protocol_mock_objects.h
+++ b/remoting/protocol/protocol_mock_objects.h
@@ -133,6 +133,8 @@
   MOCK_METHOD1(SetCapabilities, void(const Capabilities& capabilities));
   MOCK_METHOD1(RequestPairing, void(const PairingRequest& pairing_request));
   MOCK_METHOD1(DeliverClientMessage, void(const ExtensionMessage& message));
+  MOCK_METHOD1(SelectDesktopDisplay,
+               void(const SelectDesktopDisplayRequest& message));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockHostStub);
diff --git a/remoting/protocol/video_frame_pump.cc b/remoting/protocol/video_frame_pump.cc
index 6ff904a8..91a812c8 100644
--- a/remoting/protocol/video_frame_pump.cc
+++ b/remoting/protocol/video_frame_pump.cc
@@ -102,6 +102,8 @@
   observer_ = observer;
 }
 
+void VideoFramePump::SelectSource(int id) {}
+
 void VideoFramePump::OnCaptureResult(
     webrtc::DesktopCapturer::Result result,
     std::unique_ptr<webrtc::DesktopFrame> frame) {
diff --git a/remoting/protocol/video_frame_pump.h b/remoting/protocol/video_frame_pump.h
index 01c644e7b..91a1906 100644
--- a/remoting/protocol/video_frame_pump.h
+++ b/remoting/protocol/video_frame_pump.h
@@ -86,6 +86,7 @@
   void SetLosslessEncode(bool want_lossless) override;
   void SetLosslessColor(bool want_lossless) override;
   void SetObserver(Observer* observer) override;
+  void SelectSource(int id) override;
 
   protocol::VideoFeedbackStub* video_feedback_stub() {
     return &capture_scheduler_;
diff --git a/remoting/protocol/video_stream.h b/remoting/protocol/video_stream.h
index e97bf2364..263c0fa 100644
--- a/remoting/protocol/video_stream.h
+++ b/remoting/protocol/video_stream.h
@@ -49,6 +49,9 @@
 
   // Sets stream observer.
   virtual void SetObserver(Observer* observer) = 0;
+
+  // Selects the current desktop display (if multiple displays).
+  virtual void SelectSource(int id) = 0;
 };
 
 }  // namespace protocol
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index 51ef9d7e..b26fdb24 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -160,6 +160,10 @@
                                this);
 }
 
+void WebrtcVideoStream::SelectSource(int id) {
+  capturer_->SelectSource(id);
+}
+
 void WebrtcVideoStream::SetEventTimestampsSource(
     scoped_refptr<InputEventTimestampsSource> event_timestamps_source) {
   event_timestamps_source_ = event_timestamps_source;
diff --git a/remoting/protocol/webrtc_video_stream.h b/remoting/protocol/webrtc_video_stream.h
index 796a9d9..736b645 100644
--- a/remoting/protocol/webrtc_video_stream.h
+++ b/remoting/protocol/webrtc_video_stream.h
@@ -54,6 +54,7 @@
   void SetLosslessEncode(bool want_lossless) override;
   void SetLosslessColor(bool want_lossless) override;
   void SetObserver(Observer* observer) override;
+  void SelectSource(int id) override;
 
  private:
   struct FrameStats;
diff --git a/remoting/test/fake_connection_event_logger.cc b/remoting/test/fake_connection_event_logger.cc
index f77a1a0..7ee1b37 100644
--- a/remoting/test/fake_connection_event_logger.cc
+++ b/remoting/test/fake_connection_event_logger.cc
@@ -229,6 +229,8 @@
   void RequestPairing(
       const protocol::PairingRequest& pairing_request) override {}
   void SetCapabilities(const protocol::Capabilities& capabilities) override {}
+  void SelectDesktopDisplay(
+      const protocol::SelectDesktopDisplayRequest& select_display) override{};
 };
 
 FakeConnectionEventLogger::CounterHostStub::CounterHostStub()
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index ee0fa017..542567f 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -37,6 +37,7 @@
 namespace {
 
 const int kExitSuccess = 0;
+const int kExitFailure = 1;
 
 #if defined(__clang__)
 // Disable sanitizers that rely on TLS and may write to non-stack memory.
@@ -281,7 +282,7 @@
     // unshare() requires the effective uid and gid to have a mapping in the
     // parent namespace.
     if (!SetGidAndUidMaps(gid, uid))
-      _exit(1);
+      _exit(kExitFailure);
 
     // Make sure we drop CAP_SYS_ADMIN.
     CHECK(sandbox::Credentials::DropAllCapabilities());
@@ -290,7 +291,7 @@
     // Jessie explicitly forbids this case.  See:
     // add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch
     if (sys_unshare(CLONE_NEWUSER))
-      _exit(1);
+      _exit(kExitFailure);
 
     _exit(kExitSuccess);
   }
@@ -299,6 +300,9 @@
   int status = -1;
   PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
 
+  DCHECK(WIFEXITED(status) && (WEXITSTATUS(status) == kExitSuccess ||
+                               WEXITSTATUS(status) == kExitFailure));
+
   // clone(2) succeeded.  Now return true only if the system grants
   // unprivileged use of CLONE_NEWUSER as well.
   return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
diff --git a/services/BUILD.gn b/services/BUILD.gn
index bf1012a..6733c55 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -28,6 +28,7 @@
       "//services/audio:tests",
       "//services/data_decoder:tests",
       "//services/device:tests",
+      "//services/image_annotation:tests",
       "//services/image_annotation/public/cpp:tests",
       "//services/media_session:tests",
       "//services/media_session/public/cpp:tests",
diff --git a/services/image_annotation/BUILD.gn b/services/image_annotation/BUILD.gn
new file mode 100644
index 0000000..dee579a
--- /dev/null
+++ b/services/image_annotation/BUILD.gn
@@ -0,0 +1,43 @@
+# 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.
+
+source_set("lib") {
+  sources = [
+    "annotator.cc",
+    "annotator.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/public/cpp/bindings",
+    "//net",
+    "//services/image_annotation/public/cpp",
+    "//services/image_annotation/public/mojom",
+    "//services/network/public/cpp",
+    "//url",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+
+  sources = [
+    "annotator_unittest.cc",
+  ]
+
+  deps = [
+    ":lib",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/public/cpp/bindings",
+    "//net",
+    "//services/image_annotation/public/cpp",
+    "//services/image_annotation/public/mojom",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/services/image_annotation/DEPS b/services/image_annotation/DEPS
index b7df0bc..6a2652f 100644
--- a/services/image_annotation/DEPS
+++ b/services/image_annotation/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
+  "+net",
+  "+services/network",
   "+third_party/skia",
   "+ui/gfx",
+  "+url",
 ]
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
new file mode 100644
index 0000000..78cfbf20
--- /dev/null
+++ b/services/image_annotation/annotator.cc
@@ -0,0 +1,312 @@
+// 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 "services/image_annotation/annotator.h"
+
+#include <utility>
+
+#include "base/base64.h"
+#include "base/feature_list.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "net/base/load_flags.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "url/gurl.h"
+
+namespace image_annotation {
+
+namespace {
+
+constexpr size_t kMaxResponseSize = 1024 * 1024;  // 1MB.
+
+// The minimum confidence value needed to return an OCR result.
+// TODO(crbug.com/916420): tune this value.
+constexpr double kMinOcrConfidence = 0.7;
+
+// Constructs and returns a JSON string representing an OCR request for the
+// given image bytes.
+std::string FormatJsonOcrRequest(const std::string& source_id,
+                                 const std::vector<uint8_t>& image_bytes) {
+  // Re-encode image bytes into base64, which can be represented in JSON.
+  std::string base64_data;
+  Base64Encode(
+      base::StringPiece(reinterpret_cast<const char*>(image_bytes.data()),
+                        image_bytes.size()),
+      &base64_data);
+
+  base::Value image_request(base::Value::Type::DICTIONARY);
+  image_request.SetKey("image_id", base::Value(source_id));
+  image_request.SetKey("image_bytes", base::Value(std::move(base64_data)));
+
+  // TODO(crbug.com/916420): batch multiple images into one request.
+  base::Value image_request_list(base::Value::Type::LIST);
+  image_request_list.GetList().push_back(std::move(image_request));
+
+  // TODO(crbug.com/916420): accept and propagate page language info to improve
+  //                         OCR accuracy.
+  base::Value feature_request(base::Value::Type::DICTIONARY);
+  feature_request.SetKey("ocr_feature",
+                         base::Value(base::Value::Type::DICTIONARY));
+
+  base::Value request(base::Value::Type::DICTIONARY);
+  request.SetKey("image_requests", base::Value(std::move(image_request_list)));
+  request.SetKey("feature_request", std::move(feature_request));
+
+  std::string json_request;
+  base::JSONWriter::Write(request, &json_request);
+
+  return json_request;
+}
+
+// Creates a URL loader that calls the image annotation server with an OCR
+// request for the given image bytes.
+std::unique_ptr<network::SimpleURLLoader> MakeOcrRequestLoader(
+    const GURL& server_url,
+    const std::string& source_id,
+    const std::vector<uint8_t>& image_bytes) {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "POST";
+
+  // TODO(crbug.com/916420): accept and pass API key when the server is
+  //                         configured to require it.
+  resource_request->url = server_url;
+
+  resource_request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES |
+                                 net::LOAD_DO_NOT_SEND_COOKIES |
+                                 net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+  // TODO(crbug.com/916420): update this annotation to be more general and to
+  //                         reflect specfics of the UI when it is implemented.
+  const net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("image_annotation", R"(
+        semantics {
+          sender: "Image Annotation"
+          description:
+            "Chrome can identify text inside images and provide this text to "
+            "screen readers (for visually-impaired users) by sending images to "
+            "Google's servers. If image text extraction is enabled for a page, "
+            "Chrome will send the URLs and pixels of all images on the "
+            "page to Google's servers, which will return any textual content "
+            "identified inside the images. This content is made accessible to "
+            "screen reading software."
+          trigger: "A page containing images is loaded for a user who has "
+                   "automatic image text extraction enabled."
+          data: "Image pixels and URLs. No user identifier is sent along with "
+                "the data."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "You can enable or disable this feature via the context menu "
+            "for images, or via 'Image Labeling' in Chrome's settings under "
+            "Accessibility. This feature is disabled by default."
+          policy_exception_justification: "Policy to come; feature not yet "
+                                          "complete."
+        })");
+
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+
+  url_loader->AttachStringForUpload(
+      FormatJsonOcrRequest(source_id, image_bytes), "application/json");
+
+  return url_loader;
+}
+
+// Attempts to extract OCR results from the server response, returning true and
+// setting |out| to these results if successful.
+base::Optional<std::string> ParseJsonOcrResponse(
+    const std::string* const json_response) {
+  if (!json_response)
+    return base::nullopt;
+
+  const std::unique_ptr<base::Value> response =
+      base::JSONReader::Read(*json_response);
+  if (!response || !response->is_dict())
+    return base::nullopt;
+
+  const base::Value* const results = response->FindKey("results");
+  if (!results || !results->is_list() || results->GetList().size() != 1 ||
+      !results->GetList()[0].is_dict())
+    return base::nullopt;
+
+  const base::Value* const annotations =
+      results->GetList()[0].FindKey("annotations");
+  if (!annotations || !annotations->is_dict())
+    return base::nullopt;
+
+  const base::Value* const ocr_list = annotations->FindKey("ocr");
+  if (!ocr_list || !ocr_list->is_list())
+    return base::nullopt;
+
+  // The server returns separate OCR results for each region of the image; we
+  // naively concatenate these into one response string.
+  std::string out;
+  for (const base::Value& ocr : ocr_list->GetList()) {
+    if (!ocr.is_dict())
+      return base::nullopt;
+
+    const base::Value* const detected_text = ocr.FindKey("detected_text");
+    if (!detected_text || !detected_text->is_string())
+      return base::nullopt;
+
+    const base::Value* const confidence = ocr.FindKey("confidence_score");
+    if (!confidence || !confidence->is_double())
+      return base::nullopt;
+
+    if (confidence->GetDouble() < kMinOcrConfidence)
+      continue;
+
+    const std::string& detected_text_str = detected_text->GetString();
+
+    if (!out.empty() && !detected_text_str.empty())
+      out += "\n";
+    out += detected_text_str;
+  }
+
+  return out;
+}
+
+}  // namespace
+
+Annotator::Annotator(
+    GURL server_url,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(std::move(url_loader_factory)),
+      server_url_(std::move(server_url)) {}
+
+Annotator::~Annotator() {}
+
+void Annotator::BindRequest(mojom::AnnotatorRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void Annotator::AnnotateImage(const std::string& source_id,
+                              mojom::ImageProcessorPtr image_processor,
+                              AnnotateImageCallback callback) {
+  // Return cached results if they exist.
+  const auto cache_lookup = cached_results_.find(source_id);
+  if (cache_lookup != cached_results_.end()) {
+    std::move(callback).Run(
+        mojom::AnnotateImageResult::NewOcrText(cache_lookup->second));
+    return;
+  }
+
+  // Register the ImageProcessor and callback to be used for this request.
+  RequestInfoList& request_info_list = request_infos_[source_id];
+  request_info_list.push_back(
+      {std::move(image_processor), std::move(callback)});
+
+  // If the image processor dies: automatically delete the request info and
+  // reassign local processing (for other interested clients) if the dead image
+  // processor was responsible for some ongoing work.
+  request_info_list.back().first.set_connection_error_handler(base::BindOnce(
+      &Annotator::RemoveRequestInfo, base::Unretained(this), source_id,
+      --request_info_list.end(), mojom::AnnotateImageError::kCanceled));
+
+  // Don't start local work if it would duplicate some ongoing or already-
+  // completed work.
+  if (base::ContainsKey(local_processors_, source_id) ||
+      base::ContainsKey(url_loaders_, source_id))
+    return;
+
+  local_processors_.insert(
+      std::make_pair(source_id, &request_info_list.back().first));
+
+  // TODO(crbug.com/916420): first query the public result cache by URL to
+  // improve latency.
+
+  request_info_list.back().first->GetJpgImageData(
+      base::BindOnce(&Annotator::OnJpgImageDataReceived, base::Unretained(this),
+                     source_id, --request_info_list.end()));
+}
+
+void Annotator::OnJpgImageDataReceived(
+    const std::string& source_id,
+    const RequestInfoList::iterator request_info_it,
+    const std::vector<uint8_t>& image_bytes) {
+  // Failed to retrieve bytes from local processor; remove dead processor and
+  // reschedule processing.
+  if (image_bytes.empty()) {
+    RemoveRequestInfo(source_id, request_info_it,
+                      mojom::AnnotateImageError::kFailure);
+    return;
+  }
+
+  // Local processing is no longer ongoing.
+  local_processors_.erase(source_id);
+
+  // Kick off server communication.
+  // TODO(crbug.com/916420): add request to a queue here and batch them up
+  //                         to limit number of concurrent HTTP requests made.
+  std::unique_ptr<network::SimpleURLLoader> url_loader =
+      MakeOcrRequestLoader(server_url_, source_id, image_bytes);
+  const auto url_loader_it =
+      url_loaders_.insert({source_id, std::move(url_loader)}).first;
+  url_loader_it->second->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&Annotator::OnServerResponseReceived,
+                     base::Unretained(this), source_id, url_loader_it),
+      kMaxResponseSize);
+}
+
+void Annotator::OnServerResponseReceived(
+    const std::string& source_id,
+    const URLLoaderMap::iterator url_loader_it,
+    const std::unique_ptr<std::string> json_response) {
+  url_loaders_.erase(url_loader_it);
+
+  // Extract OCR results into a string.
+  const base::Optional<std::string> ocr_text =
+      ParseJsonOcrResponse(json_response.get());
+
+  if (ocr_text.has_value())
+    cached_results_.insert({source_id, *ocr_text});
+
+  const auto request_info_it = request_infos_.find(source_id);
+  if (request_info_it == request_infos_.end())
+    return;
+
+  // Notify clients of success or failure.
+  // TODO(crbug.com/916420): explore server retry strategies.
+  for (auto& info : request_info_it->second) {
+    auto result = ocr_text.has_value()
+                      ? mojom::AnnotateImageResult::NewOcrText(*ocr_text)
+                      : mojom::AnnotateImageResult::NewErrorCode(
+                            mojom::AnnotateImageError::kFailure);
+    std::move(info.second).Run(std::move(result));
+  }
+  request_infos_.erase(request_info_it);
+}
+
+void Annotator::RemoveRequestInfo(
+    const std::string& source_id,
+    const RequestInfoList::iterator request_info_it,
+    const mojom::AnnotateImageError error) {
+  // Check whether we are deleting the ImageProcessor responsible for current
+  // local processing.
+  auto lookup = local_processors_.find(source_id);
+  const bool should_reassign = lookup != local_processors_.end() &&
+                               lookup->second == &request_info_it->first;
+
+  // Notify client of cancellation / failure.
+  std::move(request_info_it->second)
+      .Run(mojom::AnnotateImageResult::NewErrorCode(error));
+
+  // Delete the specified ImageProcessor.
+  RequestInfoList& request_info_list = request_infos_[source_id];
+  request_info_list.erase(request_info_it);
+
+  // If necessary, reassign local processing.
+  if (!request_info_list.empty() && should_reassign) {
+    lookup->second = &request_info_list.front().first;
+
+    request_info_list.front().first->GetJpgImageData(base::BindOnce(
+        &Annotator::OnJpgImageDataReceived, base::Unretained(this), source_id,
+        request_info_list.begin()));
+  }
+}
+
+}  // namespace image_annotation
diff --git a/services/image_annotation/annotator.h b/services/image_annotation/annotator.h
new file mode 100644
index 0000000..1efb6088
--- /dev/null
+++ b/services/image_annotation/annotator.h
@@ -0,0 +1,107 @@
+// 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.
+
+#ifndef SERVICES_IMAGE_ANNOTATION_ANNOTATOR_H_
+#define SERVICES_IMAGE_ANNOTATION_ANNOTATOR_H_
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/image_annotation/public/mojom/image_annotation.mojom.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace image_annotation {
+
+// The annotator communicates with the external image annotation server to
+// perform image labeling at the behest of clients.
+//
+// Clients make requests of the annotator by providing an image "source ID"
+// (which is either an image URL or hash of an image data URI) and an associated
+// ImageProcessor (the interface through which the annotator can obtain image
+// pixels if necessary).
+//
+// The annotator maintains a cache of previously-computed results, and will
+// compute new results either by sending image URLs (for publicly-crawled
+// images) or image pixels to the external server.
+class Annotator : public mojom::Annotator {
+ public:
+  Annotator(GURL server_url,
+            scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  ~Annotator() override;
+
+  // Start providing behavior for the given Mojo request.
+  void BindRequest(mojom::AnnotatorRequest request);
+
+  // mojom::Annotator:
+  void AnnotateImage(const std::string& source_id,
+                     mojom::ImageProcessorPtr image,
+                     AnnotateImageCallback callback) override;
+
+ private:
+  // A list of the information associated with each request made by a client
+  // (i.e. a Chrome component) to this service. Each entry contains:
+  //  1) the ImageProcessor to use for local processing, and
+  //  2) the callback to execute when request processing has finished.
+  using RequestInfoList =
+      std::list<std::pair<mojom::ImageProcessorPtr, AnnotateImageCallback>>;
+
+  // A map from source ID to URL loader.
+  using URLLoaderMap =
+      std::map<std::string, std::unique_ptr<network::SimpleURLLoader>>;
+
+  // Removes the given request, reassigning local processing if its associated
+  // image processor had some ongoing.
+  void RemoveRequestInfo(const std::string& source_id,
+                         RequestInfoList::iterator request_info_it,
+                         mojom::AnnotateImageError error);
+
+  // Called when a local handler returns compressed image data for the given
+  // source ID.
+  void OnJpgImageDataReceived(const std::string& source_id,
+                              RequestInfoList::iterator request_info_it,
+                              const std::vector<uint8_t>& image_bytes);
+
+  // Called when the image annotation server responds with annotations for the
+  // given source ID.
+  void OnServerResponseReceived(const std::string& source_id,
+                                URLLoaderMap::iterator url_loader_it,
+                                std::unique_ptr<std::string> json_response);
+
+  // Maps from source ID to previously-obtained OCR result.
+  // TODO(crbug.com/916420): periodically clear entries from this cache.
+  std::map<std::string, std::string> cached_results_;
+
+  // Maps from source ID to the list of request info (i.e. info of clients that
+  // have made requests) for that source.
+  std::map<std::string, RequestInfoList> request_infos_;
+
+  // Maps from the source IDs of images currently being locally processed to the
+  // ImageProcessors responsible for their processing.
+  //
+  // The value is a weak pointer to an entry in the RequestInfoList for the
+  // given source.
+  std::map<std::string, mojom::ImageProcessorPtr*> local_processors_;
+
+  // A map from source ID to the currently-ongoing HTTP request to the image
+  // annotation server (if any) for that source.
+  URLLoaderMap url_loaders_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  mojo::BindingSet<mojom::Annotator> bindings_;
+
+  const GURL server_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(Annotator);
+};
+
+}  // namespace image_annotation
+
+#endif  // SERVICES_IMAGE_ANNOTATION_ANNOTATOR_H_
diff --git a/services/image_annotation/annotator_unittest.cc b/services/image_annotation/annotator_unittest.cc
new file mode 100644
index 0000000..e741cb72
--- /dev/null
+++ b/services/image_annotation/annotator_unittest.cc
@@ -0,0 +1,617 @@
+// 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 "services/image_annotation/annotator.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/optional.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "net/http/http_status_code.h"
+#include "services/image_annotation/public/mojom/image_annotation.mojom.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace image_annotation {
+
+namespace {
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::IsEmpty;
+using testing::SizeIs;
+
+constexpr char kTestServerUrl[] = "https://test_ia_server.com/v1:ocr";
+
+// Example server requests / responses.
+
+constexpr char kRequestTemplate[] = R"(
+{
+  "image_requests": [
+    {
+      "image_id": "%s",
+      "image_bytes": "%s"
+    }
+  ],
+
+  "feature_request": {
+    "ocr_feature": {}
+  }
+}
+)";
+
+constexpr char kSuccessResponseTemplate[] = R"(
+  {
+    "results": [
+      {
+        "image_id": "%s",
+        "annotations": {
+          "ocr": [%s]
+        }
+      }
+    ]
+  }
+)";
+
+constexpr char kErrorResponseTemplate[] = R"(
+  {
+    "results": [
+      {
+        "image_id": "%s",
+        "status": {
+          "code": 8,
+          "message": "Resource exhaused"
+        }
+      }
+    ]
+  }
+)";
+
+// Example image URLs.
+
+constexpr char kImage1Url[] = "https://www.example.com/image1.jpg";
+constexpr char kImage2Url[] = "https://www.example.com/image2.jpg";
+
+// An image processor that holds and exposes the callbacks it is passed.
+class TestImageProcessor : public mojom::ImageProcessor {
+ public:
+  mojom::ImageProcessorPtr GetPtr() {
+    mojom::ImageProcessorPtr ptr;
+    bindings_.AddBinding(this, mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  void Reset() {
+    bindings_.CloseAllBindings();
+    callbacks_.clear();
+  }
+
+  void GetJpgImageData(GetJpgImageDataCallback callback) override {
+    callbacks_.push_back(std::move(callback));
+  }
+
+  std::vector<GetJpgImageDataCallback>& callbacks() { return callbacks_; }
+
+ private:
+  std::vector<GetJpgImageDataCallback> callbacks_;
+
+  mojo::BindingSet<mojom::ImageProcessor> bindings_;
+};
+
+// A class that supports test URL loading for the "server" use case: where
+// all request URLs have the same prefix and differ only in suffix and body
+// content.
+class TestServerURLLoaderFactory {
+ public:
+  TestServerURLLoaderFactory(const std::string& server_url_prefix)
+      : server_url_prefix_(server_url_prefix),
+        shared_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &loader_factory_)) {}
+
+  // Expects that the earliest received request has the given URL and body, and
+  // replies with the given response.
+  //
+  // Consumes the earliest received request (i.e. a subsequent call will apply
+  // to the second-earliest received request and so on).
+  void ExpectRequestAndSimulateResponse(
+      const std::string& expected_url_suffix,
+      const std::string& expected_body,
+      const std::string& response,
+      const net::HttpStatusCode response_code) {
+    const std::string expected_url = server_url_prefix_ + expected_url_suffix;
+
+    const std::vector<network::TestURLLoaderFactory::PendingRequest>&
+        pending_requests = *loader_factory_.pending_requests();
+
+    CHECK(!pending_requests.empty());
+    const network::ResourceRequest& request = pending_requests.front().request;
+
+    // Assert that the earliest request is for the given URL.
+    CHECK_EQ(request.url, GURL(expected_url));
+
+    // Extract request body.
+    std::string actual_body;
+    if (request.request_body) {
+      const std::vector<network::DataElement>* const elements =
+          request.request_body->elements();
+
+      // We only support the simplest body structure.
+      CHECK(elements && elements->size() == 1 &&
+            (*elements)[0].type() == network::mojom::DataElementType::kBytes);
+
+      actual_body =
+          std::string((*elements)[0].bytes(), (*elements)[0].length());
+    }
+
+    EXPECT_THAT(actual_body, Eq(expected_body));
+
+    // Guaranteed to match the first request based on URL.
+    loader_factory_.SimulateResponseForPendingRequest(expected_url, response,
+                                                      response_code);
+  }
+
+  scoped_refptr<network::SharedURLLoaderFactory> AsSharedURLLoaderFactory() {
+    return shared_loader_factory_;
+  }
+
+ private:
+  const std::string server_url_prefix_;
+  network::TestURLLoaderFactory loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> shared_loader_factory_;
+};
+
+// Returns a "canonically" formatted version of a JSON string by parsing and
+// then rewriting it.
+std::string ReformatJson(const std::string& in) {
+  const std::unique_ptr<base::Value> json = base::JSONReader::Read(in);
+  CHECK(json);
+
+  std::string out;
+  base::JSONWriter::Write(*json, &out);
+
+  return out;
+}
+
+// Receives the result of an annotation request and writes the result data into
+// the given variables.
+void ReportResult(base::Optional<mojom::AnnotateImageError>* const error,
+                  base::Optional<std::string>* const ocr_text,
+                  mojom::AnnotateImageResultPtr result) {
+  if (result->which() == mojom::AnnotateImageResult::Tag::ERROR_CODE) {
+    *error = result->get_error_code();
+  } else {
+    *ocr_text = std::move(result->get_ocr_text());
+  }
+}
+
+}  // namespace
+
+// Test that OCR works for one client, and that the cache is populated.
+TEST(AnnotatorTest, SuccessAndCache) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+  TestImageProcessor processor;
+
+  // First call performs original image annotation.
+  {
+    base::Optional<mojom::AnnotateImageError> error;
+    base::Optional<std::string> ocr_text;
+
+    annotator.AnnotateImage(kImage1Url, processor.GetPtr(),
+                            base::BindOnce(&ReportResult, &error, &ocr_text));
+    test_task_env.RunUntilIdle();
+
+    // Annotator should have asked processor for pixels.
+    ASSERT_THAT(processor.callbacks(), SizeIs(1));
+
+    // Send back image data.
+    std::move(processor.callbacks()[0]).Run({1, 2, 3});
+    processor.callbacks().pop_back();
+    test_task_env.RunUntilIdle();
+
+    // HTTP request should have been made.
+    test_url_factory.ExpectRequestAndSimulateResponse(
+        "ocr",
+        ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+        base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                           R"({
+                                "detected_text": "Text 1",
+                                "confidence_score": 1.0
+                              },
+                              {
+                                "detected_text": "Text 2",
+                                "confidence_score": 1.0
+                              })"),
+        net::HTTP_OK);
+    test_task_env.RunUntilIdle();
+
+    // HTTP response should have completed and callback should have been called.
+    ASSERT_THAT(error, Eq(base::nullopt));
+    EXPECT_THAT(ocr_text, Eq("Text 1\nText 2"));
+  }
+
+  // Second call uses cached results.
+  {
+    base::Optional<mojom::AnnotateImageError> error;
+    base::Optional<std::string> ocr_text;
+
+    annotator.AnnotateImage(kImage1Url, processor.GetPtr(),
+                            base::BindOnce(&ReportResult, &error, &ocr_text));
+    test_task_env.RunUntilIdle();
+
+    // Pixels shouldn't be requested.
+    ASSERT_THAT(processor.callbacks(), IsEmpty());
+
+    // Results should have been directly returned without any server call.
+    ASSERT_THAT(error, Eq(base::nullopt));
+    EXPECT_THAT(ocr_text, Eq("Text 1\nText 2"));
+  }
+}
+
+// Test that HTTP failure is gracefully handled.
+TEST(AnnotatorTest, HttpError) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor;
+  base::Optional<mojom::AnnotateImageError> error;
+  base::Optional<std::string> ocr_text;
+
+  annotator.AnnotateImage(kImage1Url, processor.GetPtr(),
+                          base::BindOnce(&ReportResult, &error, &ocr_text));
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor for pixels.
+  ASSERT_THAT(processor.callbacks(), SizeIs(1));
+
+  // Send back image data.
+  std::move(processor.callbacks()[0]).Run({1, 2, 3});
+  processor.callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request should have been made.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      "", net::HTTP_INTERNAL_SERVER_ERROR);
+  test_task_env.RunUntilIdle();
+
+  // HTTP response should have completed and callback should have been called.
+  EXPECT_THAT(error, Eq(mojom::AnnotateImageError::kFailure));
+  EXPECT_THAT(ocr_text, Eq(base::nullopt));
+}
+
+// Test that backend failure is gracefully handled.
+TEST(AnnotatorTest, BackendError) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor;
+  base::Optional<mojom::AnnotateImageError> error;
+  base::Optional<std::string> ocr_text;
+
+  annotator.AnnotateImage(kImage1Url, processor.GetPtr(),
+                          base::BindOnce(&ReportResult, &error, &ocr_text));
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor for pixels.
+  ASSERT_THAT(processor.callbacks(), SizeIs(1));
+
+  // Send back image data.
+  std::move(processor.callbacks()[0]).Run({1, 2, 3});
+  processor.callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request should have been made.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      base::StringPrintf(kErrorResponseTemplate, kImage1Url), net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // HTTP response should have completed and callback should have been called
+  // with an error status.
+  EXPECT_THAT(error, Eq(mojom::AnnotateImageError::kFailure));
+  EXPECT_THAT(ocr_text, Eq(base::nullopt));
+}
+
+// Test that server failure (i.e. nonsense response) is gracefully handled.
+TEST(AnnotatorTest, ServerError) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor;
+  base::Optional<mojom::AnnotateImageError> error;
+  base::Optional<std::string> ocr_text;
+
+  annotator.AnnotateImage(kImage1Url, processor.GetPtr(),
+                          base::BindOnce(&ReportResult, &error, &ocr_text));
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor for pixels.
+  ASSERT_THAT(processor.callbacks(), SizeIs(1));
+
+  // Send back image data.
+  std::move(processor.callbacks()[0]).Run({1, 2, 3});
+  processor.callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request should have been made; respond with nonsense string.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      "Hello, world!", net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // HTTP response should have completed and callback should have been called
+  // with an error status.
+  EXPECT_THAT(error, Eq(mojom::AnnotateImageError::kFailure));
+  EXPECT_THAT(ocr_text, Eq(base::nullopt));
+}
+
+// Test that work is reassigned if a processor fails.
+TEST(AnnotatorTest, ProcessorFails) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor[3];
+  base::Optional<mojom::AnnotateImageError> error[3];
+  base::Optional<std::string> ocr_text[3];
+
+  for (int i = 0; i < 3; ++i) {
+    annotator.AnnotateImage(
+        kImage1Url, processor[i].GetPtr(),
+        base::BindOnce(&ReportResult, &error[i], &ocr_text[i]));
+  }
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 1 for image 1's pixels.
+  ASSERT_THAT(processor[0].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[1].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Make processor 1 fail by returning empty bytes.
+  std::move(processor[0].callbacks()[0]).Run({});
+  processor[0].callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 2 for image 1's pixels.
+  ASSERT_THAT(processor[0].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[1].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Send back image data.
+  std::move(processor[1].callbacks()[0]).Run({1, 2, 3});
+  processor[1].callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request for image 1 should have been made.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                         R"({
+                              "detected_text": "Some text",
+                              "confidence_score": 1.0
+                            })"),
+      net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have called all callbacks, but request 1 received an error
+  // when we returned empty bytes.
+  ASSERT_THAT(error, ElementsAre(mojom::AnnotateImageError::kFailure,
+                                 base::nullopt, base::nullopt));
+  EXPECT_THAT(ocr_text, ElementsAre(base::nullopt, "Some text", "Some text"));
+}
+
+// Test that work is reassigned if processor dies.
+TEST(AnnotatorTest, ProcessorDies) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor[3];
+  base::Optional<mojom::AnnotateImageError> error[3];
+  base::Optional<std::string> ocr_text[3];
+
+  for (int i = 0; i < 3; ++i) {
+    annotator.AnnotateImage(
+        kImage1Url, processor[i].GetPtr(),
+        base::BindOnce(&ReportResult, &error[i], &ocr_text[i]));
+  }
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 1 for image 1's pixels.
+  ASSERT_THAT(processor[0].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[1].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Kill processor 1.
+  processor[0].Reset();
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 2 for image 1's pixels.
+  ASSERT_THAT(processor[0].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[1].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Send back image data.
+  std::move(processor[1].callbacks()[0]).Run({1, 2, 3});
+  processor[1].callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request for image 1 should have been made.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                         R"({
+                              "detected_text": "Some text",
+                              "confidence_score": 1.0
+                            })"),
+      net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have called all callbacks, but request 1 was canceled when
+  // we reset processor 1.
+  ASSERT_THAT(error, ElementsAre(mojom::AnnotateImageError::kCanceled,
+                                 base::nullopt, base::nullopt));
+  EXPECT_THAT(ocr_text, ElementsAre(base::nullopt, "Some text", "Some text"));
+}
+
+// Test that multiple concurrent requests are handled.
+TEST(AnnotatorTest, Concurrent) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor[2];
+  base::Optional<mojom::AnnotateImageError> error[2];
+  base::Optional<std::string> ocr_text[2];
+
+  // Requests for images 1 and 2.
+  annotator.AnnotateImage(
+      kImage1Url, processor[0].GetPtr(),
+      base::BindOnce(&ReportResult, &error[0], &ocr_text[0]));
+  annotator.AnnotateImage(
+      kImage2Url, processor[1].GetPtr(),
+      base::BindOnce(&ReportResult, &error[1], &ocr_text[1]));
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 1 for image 1's pixels and processor
+  // 2 for image 2's pixels.
+  ASSERT_THAT(processor[0].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[1].callbacks(), SizeIs(1));
+
+  // Send back image data.
+  std::move(processor[0].callbacks()[0]).Run({1, 2, 3});
+  processor[0].callbacks().pop_back();
+  std::move(processor[1].callbacks()[0]).Run({4, 5, 6});
+  processor[1].callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // HTTP request for image 1 should have been made first, then request for
+  // image 2.
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                         R"({
+                              "detected_text": "Text 1",
+                              "confidence_score": 1.0
+                            })"),
+      net::HTTP_OK);
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage2Url, "BAUG")),
+      base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                         R"({
+                              "detected_text": "Text 2",
+                              "confidence_score": 1.0
+                            })"),
+      net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have called each callback with its corresponding text.
+  ASSERT_THAT(error, ElementsAre(base::nullopt, base::nullopt));
+  EXPECT_THAT(ocr_text, ElementsAre("Text 1", "Text 2"));
+}
+
+// Test that work is not duplicated if it is already ongoing.
+TEST(AnnotatorTest, DuplicateWork) {
+  base::test::ScopedTaskEnvironment test_task_env;
+  TestServerURLLoaderFactory test_url_factory("https://test_ia_server.com/v1:");
+
+  Annotator annotator(GURL(kTestServerUrl),
+                      test_url_factory.AsSharedURLLoaderFactory());
+
+  TestImageProcessor processor[3];
+  base::Optional<mojom::AnnotateImageError> error[3];
+  base::Optional<std::string> ocr_text[3];
+
+  // First request annotation of image 1 with processor 1.
+  annotator.AnnotateImage(
+      kImage1Url, processor[0].GetPtr(),
+      base::BindOnce(&ReportResult, &error[0], &ocr_text[0]));
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have asked processor 1 for image 1's pixels.
+  ASSERT_THAT(processor[0].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[1].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Now request annotation of image 1 with processor 2.
+  annotator.AnnotateImage(
+      kImage1Url, processor[1].GetPtr(),
+      base::BindOnce(&ReportResult, &error[1], &ocr_text[1]));
+  test_task_env.RunUntilIdle();
+
+  // Annotator *should not* have asked processor 2 for image 1's pixels (since
+  // processor 1 is already handling that).
+  ASSERT_THAT(processor[0].callbacks(), SizeIs(1));
+  ASSERT_THAT(processor[1].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // Get processor 1 to reply with bytes for image 1.
+  std::move(processor[0].callbacks()[0]).Run({1, 2, 3});
+  processor[0].callbacks().pop_back();
+  test_task_env.RunUntilIdle();
+
+  // Now request annotation of image 1 with processor 3.
+  annotator.AnnotateImage(
+      kImage1Url, processor[2].GetPtr(),
+      base::BindOnce(&ReportResult, &error[2], &ocr_text[2]));
+  test_task_env.RunUntilIdle();
+
+  // Annotator *should not* have asked processor 3 for image 1's pixels (since
+  // it has already sent image 1's pixels to the server).
+  ASSERT_THAT(processor[0].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[1].callbacks(), IsEmpty());
+  ASSERT_THAT(processor[2].callbacks(), IsEmpty());
+
+  // HTTP request for image 1 should have been made (with bytes obtained from
+  // processor 1).
+  test_url_factory.ExpectRequestAndSimulateResponse(
+      "ocr",
+      ReformatJson(base::StringPrintf(kRequestTemplate, kImage1Url, "AQID")),
+      base::StringPrintf(kSuccessResponseTemplate, kImage1Url,
+                         R"({
+                              "detected_text": "Some text",
+                              "confidence_score": 1.0
+                            })"),
+      net::HTTP_OK);
+  test_task_env.RunUntilIdle();
+
+  // Annotator should have called all callbacks with annotation results.
+  ASSERT_THAT(error, ElementsAre(base::nullopt, base::nullopt, base::nullopt));
+  EXPECT_THAT(ocr_text, ElementsAre("Some text", "Some text", "Some text"));
+}
+
+}  // namespace image_annotation
diff --git a/services/image_annotation/public/mojom/image_annotation.mojom b/services/image_annotation/public/mojom/image_annotation.mojom
index 1e5e80a..02514e5d 100644
--- a/services/image_annotation/public/mojom/image_annotation.mojom
+++ b/services/image_annotation/public/mojom/image_annotation.mojom
@@ -11,3 +11,31 @@
   //                         require more sophisticated render-side processing.
   GetJpgImageData() => (array<uint8> bytes);
 };
+
+enum AnnotateImageError {
+  kCanceled,
+  kFailure,
+};
+
+union AnnotateImageResult {
+  AnnotateImageError error_code;
+  string ocr_text;
+};
+
+interface Annotator {
+  // Requests a11y annotations (i.e. OCR) for the given image.
+  //
+  // |source_id| is either the URL for an image, or some non-URL string that
+  // uniquely identifies an image (e.g. a hash of image content for a data
+  // URI). Source IDs are used to query local and remote caches.
+  //
+  // |result| will contain either the |error_code| value specifying how
+  // annotation failed, or the text |ocr_text| extracted from the image (with
+  // an empty string denoting no image text).
+  //
+  // TODO(crbug.com/916420): expand this signature to include a request
+  //                         argument when we support more than one type of
+  //                         annotation.
+  AnnotateImage(string source_id, ImageProcessor image_processor)
+    => (AnnotateImageResult result);
+};
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index 77520ec..e6b952f 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -142,19 +142,28 @@
     controller_->BindToInterface(std::move(request));
   }
 
-  void Suspend(bool transient) {
+  void Suspend(const EnforcementState& state) {
     DCHECK(!session_info_->force_duck);
-    transient_suspend_ = transient_suspend_ || transient;
-    session_->Suspend(mojom::MediaSession::SuspendType::kSystem);
+
+    // In most cases if we stop or suspend we should call the ::Suspend method
+    // on the media session. The only exception is if the session has the
+    // |prefer_stop_for_gain_focus_loss| bit set in which case we should use
+    // ::Stop and ::Suspend respectively.
+    if (state.should_stop && session_info_->prefer_stop_for_gain_focus_loss) {
+      session_->Stop(mojom::MediaSession::SuspendType::kSystem);
+    } else {
+      was_suspended_ = was_suspended_ || state.should_suspend;
+      session_->Suspend(mojom::MediaSession::SuspendType::kSystem);
+    }
   }
 
   void MaybeResume() {
     DCHECK(!session_info_->force_duck);
 
-    if (!transient_suspend_)
+    if (!was_suspended_)
       return;
 
-    transient_suspend_ = false;
+    was_suspended_ = false;
     session_->Resume(mojom::MediaSession::SuspendType::kSystem);
   }
 
@@ -192,7 +201,7 @@
 
   AudioFocusManagerMetricsHelper metrics_helper_;
   bool encountered_error_ = false;
-  bool transient_suspend_ = false;
+  bool was_suspended_ = false;
 
   std::unique_ptr<MediaController> controller_;
 
@@ -425,10 +434,10 @@
     // Update the flags based on the audio focus type of this session.
     switch (session->audio_focus_type()) {
       case mojom::AudioFocusType::kGain:
-        state.should_suspend = true;
+        state.should_stop = true;
         break;
       case mojom::AudioFocusType::kGainTransient:
-        state.should_transient_suspend = true;
+        state.should_suspend = true;
         break;
       case mojom::AudioFocusType::kGainTransientMayDuck:
         state.should_duck = true;
@@ -500,8 +509,7 @@
 bool AudioFocusManager::ShouldSessionBeSuspended(
     const StackRow* session,
     const EnforcementState& state) const {
-  bool should_suspend_any =
-      state.should_suspend || state.should_transient_suspend;
+  bool should_suspend_any = state.should_stop || state.should_suspend;
 
   switch (enforcement_mode_) {
     case mojom::EnforcementMode::kSingleSession:
@@ -547,7 +555,7 @@
     return;
 
   if (ShouldSessionBeSuspended(session, state)) {
-    session->Suspend(state.should_transient_suspend);
+    session->Suspend(state);
   } else {
     session->MaybeResume();
   }
diff --git a/services/media_session/audio_focus_manager.h b/services/media_session/audio_focus_manager.h
index 04cbfed..50d72f6 100644
--- a/services/media_session/audio_focus_manager.h
+++ b/services/media_session/audio_focus_manager.h
@@ -98,8 +98,8 @@
 
   struct EnforcementState {
     bool should_duck = false;
+    bool should_stop = false;
     bool should_suspend = false;
-    bool should_transient_suspend = false;
   };
 
   void RequestAudioFocusInternal(std::unique_ptr<StackRow>,
diff --git a/services/media_session/audio_focus_manager_unittest.cc b/services/media_session/audio_focus_manager_unittest.cc
index 23ea8d4e..c8b9b49 100644
--- a/services/media_session/audio_focus_manager_unittest.cc
+++ b/services/media_session/audio_focus_manager_unittest.cc
@@ -1121,6 +1121,11 @@
 
   media_session_4.AbandonAudioFocusFromClient();
 
+  // TODO(https://crbug.com/916177): This should wait on a more precise
+  // condition than RunLoop idling, but it's not clear exactly what that
+  // should be.
+  base::RunLoop().RunUntilIdle();
+
   EXPECT_EQ(IsGroupingEnabled()
                 ? mojom::MediaSessionInfo::SessionState::kActive
                 : mojom::MediaSessionInfo::SessionState::kSuspended,
@@ -1191,4 +1196,42 @@
             GetState(&media_session_2));
 }
 
+TEST_P(AudioFocusManagerTest, RequestAudioFocus_PreferStop_LossToGain) {
+  test::MockMediaSession media_session_1;
+  test::MockMediaSession media_session_2;
+
+  media_session_1.SetPreferStop(true);
+
+  AudioFocusManager::RequestId request_id_1 =
+      RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
+  EXPECT_EQ(request_id_1, GetAudioFocusedSession());
+  EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
+            GetState(&media_session_1));
+
+  AudioFocusManager::RequestId request_id_2 =
+      RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
+  EXPECT_EQ(request_id_2, GetAudioFocusedSession());
+  EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kInactive),
+            GetState(&media_session_1));
+}
+
+TEST_P(AudioFocusManagerTest,
+       RequestAudioFocus_PreferStop_LossToGainTransient) {
+  test::MockMediaSession media_session_1;
+  test::MockMediaSession media_session_2;
+
+  media_session_1.SetPreferStop(true);
+
+  AudioFocusManager::RequestId request_id_1 =
+      RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
+  EXPECT_EQ(request_id_1, GetAudioFocusedSession());
+  EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
+            GetState(&media_session_1));
+
+  RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
+  EXPECT_EQ(
+      GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
+      GetState(&media_session_1));
+}
+
 }  // namespace media_session
diff --git a/services/media_session/public/cpp/test/mock_media_session.cc b/services/media_session/public/cpp/test/mock_media_session.cc
index e2f406af..12ae3ca 100644
--- a/services/media_session/public/cpp/test/mock_media_session.cc
+++ b/services/media_session/public/cpp/test/mock_media_session.cc
@@ -55,6 +55,8 @@
 void MockMediaSessionMojoObserver::MediaSessionActionsChanged(
     const std::vector<mojom::MediaSessionAction>& actions) {
   session_actions_ = actions;
+  session_actions_set_ =
+      std::set<mojom::MediaSessionAction>(actions.begin(), actions.end());
 
   if (waiting_for_actions_) {
     run_loop_->Quit();
@@ -333,6 +335,7 @@
     info->playback_state = mojom::MediaPlaybackState::kPlaying;
 
   info->is_controllable = is_controllable_;
+  info->prefer_stop_for_gain_focus_loss = prefer_stop_;
 
   return info;
 }
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h
index 8294306..904e7d1 100644
--- a/services/media_session/public/cpp/test/mock_media_session.h
+++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -60,12 +60,17 @@
     return session_actions_;
   }
 
+  const std::set<mojom::MediaSessionAction>& actions_set() const {
+    return session_actions_set_;
+  }
+
  private:
   void StartWaiting();
 
   mojom::MediaSessionInfoPtr session_info_;
   base::Optional<base::Optional<MediaMetadata>> session_metadata_;
   std::vector<mojom::MediaSessionAction> session_actions_;
+  std::set<mojom::MediaSessionAction> session_actions_set_;
 
   bool waiting_for_metadata_ = false;
   bool waiting_for_non_empty_metadata_ = false;
@@ -104,6 +109,8 @@
 
   void SetIsControllable(bool value);
 
+  void SetPreferStop(bool value) { prefer_stop_ = value; }
+
   void AbandonAudioFocusFromClient();
   base::UnguessableToken GetRequestIdFromClient();
 
@@ -144,6 +151,7 @@
   const bool force_duck_ = false;
   bool is_ducking_ = false;
   bool is_controllable_ = false;
+  bool prefer_stop_ = false;
 
   int prev_track_count_ = 0;
   int next_track_count_ = 0;
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index 6341525..7d9e4b2a 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -9,7 +9,7 @@
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojom/url.mojom";
 
-// Next MinVersion: 3
+// Next MinVersion: 4
 
 [Extensible]
 enum MediaPlaybackState {
@@ -76,6 +76,10 @@
 
   // If true then the session should be controllable by the user.
   [MinVersion=2] bool is_controllable;
+
+  // If true then we will stop this MediaSession instead of suspending when the
+  // session loses focus to a kGain focus type.
+  [MinVersion=4] bool prefer_stop_for_gain_focus_loss;
 };
 
 // Contains debugging information about a MediaSession. This will be displayed
diff --git a/services/network/host_resolver_unittest.cc b/services/network/host_resolver_unittest.cc
index a402ccbc..57cbc807 100644
--- a/services/network/host_resolver_unittest.cc
+++ b/services/network/host_resolver_unittest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/optional.h"
@@ -17,8 +18,13 @@
 #include "net/base/address_list.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/dns/dns_config.h"
+#include "net/dns/dns_test_util.h"
+#include "net/dns/host_resolver_impl.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/dns/public/dns_protocol.h"
 #include "net/dns/public/dns_query_type.h"
 #include "net/log/net_log.h"
 #include "services/network/host_resolver.h"
@@ -69,6 +75,16 @@
       run_loop_->Quit();
   }
 
+  void OnTextResults(const std::vector<std::string>& text_results) override {
+    DCHECK(!complete_);
+    result_text_ = text_results;
+  }
+
+  void OnHostnameResults(const std::vector<net::HostPortPair>& hosts) override {
+    DCHECK(!complete_);
+    result_hosts_ = hosts;
+  }
+
   bool complete() const { return complete_; }
 
   int result_error() const {
@@ -81,12 +97,24 @@
     return result_addresses_;
   }
 
+  const base::Optional<std::vector<std::string>>& result_text() const {
+    DCHECK(complete_);
+    return result_text_;
+  }
+
+  const base::Optional<std::vector<net::HostPortPair>>& result_hosts() const {
+    DCHECK(complete_);
+    return result_hosts_;
+  }
+
  private:
   mojo::Binding<mojom::ResolveHostClient> binding_;
 
   bool complete_;
   int result_error_;
   base::Optional<net::AddressList> result_addresses_;
+  base::Optional<std::vector<std::string>> result_text_;
+  base::Optional<std::vector<net::HostPortPair>> result_hosts_;
   base::RunLoop* const run_loop_;
 };
 
@@ -113,6 +141,8 @@
   EXPECT_EQ(net::OK, response_client.result_error());
   EXPECT_THAT(response_client.result_addresses().value().endpoints(),
               testing::ElementsAre(CreateExpectedEndPoint("127.0.0.1", 160)));
+  EXPECT_FALSE(response_client.result_text());
+  EXPECT_FALSE(response_client.result_hosts());
   EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
   EXPECT_EQ(net::DEFAULT_PRIORITY, inner_resolver->last_request_priority());
 }
@@ -145,6 +175,8 @@
   EXPECT_EQ(net::OK, response_client.result_error());
   EXPECT_THAT(response_client.result_addresses().value().endpoints(),
               testing::ElementsAre(CreateExpectedEndPoint("127.0.0.1", 160)));
+  EXPECT_FALSE(response_client.result_text());
+  EXPECT_FALSE(response_client.result_hosts());
   EXPECT_TRUE(control_handle_closed);
   EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
   EXPECT_EQ(net::DEFAULT_PRIORITY, inner_resolver->last_request_priority());
@@ -1027,5 +1059,94 @@
   EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
 }
 
+net::DnsConfig CreateValidDnsConfig() {
+  net::IPAddress dns_ip(192, 168, 1, 0);
+  net::DnsConfig config;
+  config.nameservers.push_back(
+      net::IPEndPoint(dns_ip, net::dns_protocol::kDefaultPort));
+  EXPECT_TRUE(config.IsValid());
+  return config;
+}
+
+TEST_F(HostResolverTest, TextResults) {
+  static const char* kTextRecords[] = {"foo", "bar", "more text"};
+  net::MockDnsClientRuleList rules;
+  rules.emplace_back(
+      "example.com", net::dns_protocol::kTypeTXT,
+      net::MockDnsClientRule::Result(net::BuildTestDnsResponse(
+          "example.com", {std::vector<std::string>(std::begin(kTextRecords),
+                                                   std::end(kTextRecords))})),
+      false /* delay */);
+  auto dns_client =
+      std::make_unique<net::MockDnsClient>(net::DnsConfig(), std::move(rules));
+
+  net::NetLog net_log;
+  std::unique_ptr<net::HostResolverImpl> inner_resolver =
+      net::HostResolver::CreateDefaultResolverImpl(&net_log);
+  inner_resolver->SetDnsClient(std::move(dns_client));
+  inner_resolver->SetBaseDnsConfigForTesting(CreateValidDnsConfig());
+
+  HostResolver resolver(inner_resolver.get(), &net_log);
+
+  base::RunLoop run_loop;
+  mojom::ResolveHostParametersPtr optional_parameters =
+      mojom::ResolveHostParameters::New();
+  optional_parameters->dns_query_type = net::DnsQueryType::TXT;
+  mojom::ResolveHostClientPtr response_client_ptr;
+  TestResolveHostClient response_client(&response_client_ptr, &run_loop);
+
+  resolver.ResolveHost(net::HostPortPair("example.com", 160),
+                       std::move(optional_parameters),
+                       std::move(response_client_ptr));
+  run_loop.Run();
+
+  EXPECT_EQ(net::OK, response_client.result_error());
+  EXPECT_FALSE(response_client.result_addresses());
+  EXPECT_THAT(response_client.result_text(),
+              testing::Optional(testing::ElementsAreArray(kTextRecords)));
+  EXPECT_FALSE(response_client.result_hosts());
+  EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
+}
+
+TEST_F(HostResolverTest, HostResults) {
+  net::MockDnsClientRuleList rules;
+  rules.emplace_back(
+      "example.com", net::dns_protocol::kTypePTR,
+      net::MockDnsClientRule::Result(net::BuildTestDnsPointerResponse(
+          "example.com", {"google.com", "chromium.org"})),
+      false /* delay */);
+  auto dns_client =
+      std::make_unique<net::MockDnsClient>(net::DnsConfig(), std::move(rules));
+
+  net::NetLog net_log;
+  std::unique_ptr<net::HostResolverImpl> inner_resolver =
+      net::HostResolver::CreateDefaultResolverImpl(&net_log);
+  inner_resolver->SetDnsClient(std::move(dns_client));
+  inner_resolver->SetBaseDnsConfigForTesting(CreateValidDnsConfig());
+
+  HostResolver resolver(inner_resolver.get(), &net_log);
+
+  base::RunLoop run_loop;
+  mojom::ResolveHostParametersPtr optional_parameters =
+      mojom::ResolveHostParameters::New();
+  optional_parameters->dns_query_type = net::DnsQueryType::PTR;
+  mojom::ResolveHostClientPtr response_client_ptr;
+  TestResolveHostClient response_client(&response_client_ptr, &run_loop);
+
+  resolver.ResolveHost(net::HostPortPair("example.com", 160),
+                       std::move(optional_parameters),
+                       std::move(response_client_ptr));
+  run_loop.Run();
+
+  EXPECT_EQ(net::OK, response_client.result_error());
+  EXPECT_FALSE(response_client.result_addresses());
+  EXPECT_FALSE(response_client.result_text());
+  EXPECT_THAT(response_client.result_hosts(),
+              testing::Optional(testing::UnorderedElementsAre(
+                  net::HostPortPair("google.com", 160),
+                  net::HostPortPair("chromium.org", 160))));
+  EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
+}
+
 }  // namespace
 }  // namespace network
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 22bb5cf..e0c9603 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -97,6 +97,7 @@
 #include "services/network/network_service.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_service_buildflags.h"
+#include "services/network/public/cpp/resolve_host_client_base.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/public/mojom/net_log.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
@@ -2559,7 +2560,7 @@
   return net::IPEndPoint(ip_address, port);
 }
 
-class TestResolveHostClient : public mojom::ResolveHostClient {
+class TestResolveHostClient : public ResolveHostClientBase {
  public:
   TestResolveHostClient(mojom::ResolveHostClientPtr* interface_ptr,
                         base::RunLoop* run_loop)
diff --git a/services/network/p2p/socket_manager.cc b/services/network/p2p/socket_manager.cc
index df3d8e6..475c27ff 100644
--- a/services/network/p2p/socket_manager.cc
+++ b/services/network/p2p/socket_manager.cc
@@ -27,8 +27,8 @@
 #include "services/network/p2p/socket.h"
 #include "services/network/proxy_resolving_client_socket_factory.h"
 #include "services/network/public/cpp/p2p_param_traits.h"
-#include "third_party/webrtc/media/base/rtputils.h"
-#include "third_party/webrtc/media/base/turnutils.h"
+#include "third_party/webrtc/media/base/rtp_utils.h"
+#include "third_party/webrtc/media/base/turn_utils.h"
 
 namespace network {
 
diff --git a/services/network/p2p/socket_tcp.cc b/services/network/p2p/socket_tcp.cc
index 5409a96..b0471d1 100644
--- a/services/network/p2p/socket_tcp.cc
+++ b/services/network/p2p/socket_tcp.cc
@@ -19,7 +19,7 @@
 #include "services/network/proxy_resolving_client_socket.h"
 #include "services/network/proxy_resolving_client_socket_factory.h"
 #include "services/network/public/cpp/p2p_param_traits.h"
-#include "third_party/webrtc/media/base/rtputils.h"
+#include "third_party/webrtc/media/base/rtp_utils.h"
 #include "url/gurl.h"
 
 namespace network {
diff --git a/services/network/p2p/socket_throttler.cc b/services/network/p2p/socket_throttler.cc
index ddcfca4..dce46f64 100644
--- a/services/network/p2p/socket_throttler.cc
+++ b/services/network/p2p/socket_throttler.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "third_party/webrtc/rtc_base/data_rate_limiter.h"
-#include "third_party/webrtc/rtc_base/timeutils.h"
+#include "third_party/webrtc/rtc_base/time_utils.h"
 
 namespace network {
 
diff --git a/services/network/p2p/socket_udp.cc b/services/network/p2p/socket_udp.cc
index 7efbc8a..fdaaf92 100644
--- a/services/network/p2p/socket_udp.cc
+++ b/services/network/p2p/socket_udp.cc
@@ -16,7 +16,7 @@
 #include "net/log/net_log_source.h"
 #include "services/network/p2p/socket_throttler.h"
 #include "services/network/public/mojom/p2p.mojom.h"
-#include "third_party/webrtc/media/base/rtputils.h"
+#include "third_party/webrtc/media/base/rtp_utils.h"
 
 namespace {
 
diff --git a/services/network/p2p/socket_udp.h b/services/network/p2p/socket_udp.h
index 39c3fb0..8362a7f 100644
--- a/services/network/p2p/socket_udp.h
+++ b/services/network/p2p/socket_udp.h
@@ -24,7 +24,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/p2p/socket.h"
 #include "services/network/public/cpp/p2p_socket_type.h"
-#include "third_party/webrtc/rtc_base/asyncpacketsocket.h"
+#include "third_party/webrtc/rtc_base/async_packet_socket.h"
 
 namespace net {
 class NetLog;
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index 80b4432..0db565a 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -38,6 +38,8 @@
     "network_quality_tracker.h",
     "network_switches.cc",
     "network_switches.h",
+    "resolve_host_client_base.cc",
+    "resolve_host_client_base.h",
     "shared_url_loader_factory.cc",
     "shared_url_loader_factory.h",
     "simple_url_loader.cc",
diff --git a/services/network/public/cpp/DEPS b/services/network/public/cpp/DEPS
index c9e8727..0cab4fa 100644
--- a/services/network/public/cpp/DEPS
+++ b/services/network/public/cpp/DEPS
@@ -2,7 +2,7 @@
   # CORS related files will be included from Blink, and should use cors.mojom-shared.h.
   # resource_request.h is an exception since it's not used by Blink.
   "-services/network/public/mojom/cors.mojom.h",
-  "+third_party/webrtc/rtc_base/asyncpacketsocket.h",
+  "+third_party/webrtc/rtc_base/async_packet_socket.h",
 ]
 
 specific_include_rules = {
diff --git a/services/network/public/cpp/data_element.cc b/services/network/public/cpp/data_element.cc
index 3fd9507..f516f82 100644
--- a/services/network/public/cpp/data_element.cc
+++ b/services/network/public/cpp/data_element.cc
@@ -16,7 +16,7 @@
 const uint64_t DataElement::kUnknownSize;
 
 DataElement::DataElement()
-    : type_(TYPE_UNKNOWN),
+    : type_(mojom::DataElementType::kUnknown),
       bytes_(NULL),
       offset_(0),
       length_(std::numeric_limits<uint64_t>::max()) {}
@@ -31,7 +31,7 @@
     uint64_t offset,
     uint64_t length,
     const base::Time& expected_modification_time) {
-  type_ = TYPE_FILE;
+  type_ = mojom::DataElementType::kFile;
   path_ = path;
   offset_ = offset;
   length_ = length;
@@ -43,7 +43,7 @@
                                  uint64_t offset,
                                  uint64_t length,
                                  const base::Time& expected_modification_time) {
-  type_ = TYPE_RAW_FILE;
+  type_ = mojom::DataElementType::kRawFile;
   file_ = std::move(file);
   path_ = path;
   offset_ = offset;
@@ -54,7 +54,7 @@
 void DataElement::SetToBlobRange(const std::string& blob_uuid,
                                  uint64_t offset,
                                  uint64_t length) {
-  type_ = TYPE_BLOB;
+  type_ = mojom::DataElementType::kBlob;
   blob_uuid_ = blob_uuid;
   offset_ = offset;
   length_ = length;
@@ -62,13 +62,13 @@
 
 void DataElement::SetToDataPipe(mojom::DataPipeGetterPtr data_pipe_getter) {
   DCHECK(data_pipe_getter);
-  type_ = TYPE_DATA_PIPE;
+  type_ = mojom::DataElementType::kDataPipe;
   data_pipe_getter_ = data_pipe_getter.PassInterface();
 }
 
 void DataElement::SetToChunkedDataPipe(
     mojom::ChunkedDataPipeGetterPtr chunked_data_pipe_getter) {
-  type_ = TYPE_CHUNKED_DATA_PIPE;
+  type_ = mojom::DataElementType::kChunkedDataPipe;
   chunked_data_pipe_getter_ = chunked_data_pipe_getter.PassInterface();
 }
 
@@ -77,13 +77,13 @@
 }
 
 mojom::DataPipeGetterPtrInfo DataElement::ReleaseDataPipeGetter() {
-  DCHECK_EQ(TYPE_DATA_PIPE, type_);
+  DCHECK_EQ(mojom::DataElementType::kDataPipe, type_);
   DCHECK(data_pipe_getter_.is_valid());
   return std::move(data_pipe_getter_);
 }
 
 mojom::DataPipeGetterPtr DataElement::CloneDataPipeGetter() const {
-  DCHECK_EQ(TYPE_DATA_PIPE, type_);
+  DCHECK_EQ(mojom::DataElementType::kDataPipe, type_);
   DCHECK(data_pipe_getter_.is_valid());
   auto* mutable_this = const_cast<DataElement*>(this);
   mojom::DataPipeGetterPtr owned(std::move(mutable_this->data_pipe_getter_));
@@ -95,7 +95,7 @@
 
 mojom::ChunkedDataPipeGetterPtrInfo
 DataElement::ReleaseChunkedDataPipeGetter() {
-  DCHECK_EQ(TYPE_CHUNKED_DATA_PIPE, type_);
+  DCHECK_EQ(mojom::DataElementType::kChunkedDataPipe, type_);
   return std::move(chunked_data_pipe_getter_);
 }
 
@@ -103,7 +103,7 @@
   const uint64_t kMaxDataPrintLength = 40;
   *os << "<DataElement>{type: ";
   switch (x.type()) {
-    case DataElement::TYPE_BYTES: {
+    case mojom::DataElementType::kBytes: {
       uint64_t length = std::min(x.length(), kMaxDataPrintLength);
       *os << "TYPE_BYTES, data: ["
           << base::HexEncode(x.bytes(), static_cast<size_t>(length));
@@ -113,24 +113,24 @@
       *os << "]";
       break;
     }
-    case DataElement::TYPE_FILE:
+    case mojom::DataElementType::kFile:
       *os << "TYPE_FILE, path: " << x.path().AsUTF8Unsafe()
           << ", expected_modification_time: " << x.expected_modification_time();
       break;
-    case DataElement::TYPE_RAW_FILE:
+    case mojom::DataElementType::kRawFile:
       *os << "TYPE_RAW_FILE, path: " << x.path().AsUTF8Unsafe()
           << ", expected_modification_time: " << x.expected_modification_time();
       break;
-    case DataElement::TYPE_BLOB:
+    case mojom::DataElementType::kBlob:
       *os << "TYPE_BLOB, uuid: " << x.blob_uuid();
       break;
-    case DataElement::TYPE_DATA_PIPE:
+    case mojom::DataElementType::kDataPipe:
       *os << "TYPE_DATA_PIPE";
       break;
-    case DataElement::TYPE_CHUNKED_DATA_PIPE:
+    case mojom::DataElementType::kChunkedDataPipe:
       *os << "TYPE_CHUNKED_DATA_PIPE";
       break;
-    case DataElement::TYPE_UNKNOWN:
+    case mojom::DataElementType::kUnknown:
       *os << "TYPE_UNKNOWN";
       break;
   }
@@ -142,21 +142,21 @@
       a.length() != b.length())
     return false;
   switch (a.type()) {
-    case DataElement::TYPE_BYTES:
+    case mojom::DataElementType::kBytes:
       return memcmp(a.bytes(), b.bytes(), b.length()) == 0;
-    case DataElement::TYPE_FILE:
+    case mojom::DataElementType::kFile:
       return a.path() == b.path() &&
              a.expected_modification_time() == b.expected_modification_time();
-    case DataElement::TYPE_RAW_FILE:
+    case mojom::DataElementType::kRawFile:
       return a.path() == b.path() &&
              a.expected_modification_time() == b.expected_modification_time();
-    case DataElement::TYPE_BLOB:
+    case mojom::DataElementType::kBlob:
       return a.blob_uuid() == b.blob_uuid();
-    case DataElement::TYPE_DATA_PIPE:
+    case mojom::DataElementType::kDataPipe:
       return false;
-    case DataElement::TYPE_CHUNKED_DATA_PIPE:
+    case mojom::DataElementType::kChunkedDataPipe:
       return false;
-    case DataElement::TYPE_UNKNOWN:
+    case mojom::DataElementType::kUnknown:
       NOTREACHED();
       return false;
   }
diff --git a/services/network/public/cpp/data_element.h b/services/network/public/cpp/data_element.h
index 5ef9c5d..f3077dc3 100644
--- a/services/network/public/cpp/data_element.h
+++ b/services/network/public/cpp/data_element.h
@@ -23,6 +23,7 @@
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
 #include "services/network/public/mojom/data_pipe_getter.mojom.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -33,22 +34,6 @@
  public:
   static const uint64_t kUnknownSize = std::numeric_limits<uint64_t>::max();
 
-  enum Type {
-    TYPE_UNKNOWN = -1,
-
-    // Only used for Upload with Network Service as of now:
-    TYPE_DATA_PIPE,
-    TYPE_CHUNKED_DATA_PIPE,
-    TYPE_RAW_FILE,
-
-    // Used for Upload when Network Service is disabled:
-    TYPE_BLOB,
-    TYPE_FILE,
-
-    // Commonly used in every case:
-    TYPE_BYTES,
-  };
-
   DataElement();
   ~DataElement();
 
@@ -57,7 +42,7 @@
   DataElement(DataElement&& other);
   DataElement& operator=(DataElement&& other);
 
-  Type type() const { return type_; }
+  mojom::DataElementType type() const { return type_; }
   const char* bytes() const { return bytes_ ? bytes_ : buf_.data(); }
   const base::FilePath& path() const { return path_; }
   const base::File& file() const { return file_; }
@@ -74,7 +59,7 @@
 
   // Sets TYPE_BYTES data. This copies the given data into the element.
   void SetToBytes(const char* bytes, int bytes_len) {
-    type_ = TYPE_BYTES;
+    type_ = mojom::DataElementType::kBytes;
     bytes_ = nullptr;
     buf_.assign(bytes, bytes + bytes_len);
     length_ = buf_.size();
@@ -82,7 +67,7 @@
 
   // Sets TYPE_BYTES data. This moves the given data vector into the element.
   void SetToBytes(std::vector<char> bytes) {
-    type_ = TYPE_BYTES;
+    type_ = mojom::DataElementType::kBytes;
     bytes_ = nullptr;
     buf_ = std::move(bytes);
     length_ = buf_.size();
@@ -91,7 +76,7 @@
   // Sets TYPE_BYTES data, and clears the internal bytes buffer.
   // For use with AppendBytes.
   void SetToEmptyBytes() {
-    type_ = TYPE_BYTES;
+    type_ = mojom::DataElementType::kBytes;
     buf_.clear();
     length_ = 0;
     bytes_ = nullptr;
@@ -100,7 +85,7 @@
   // Copies and appends the given data into the element. SetToEmptyBytes or
   // SetToBytes must be called before this method.
   void AppendBytes(const char* bytes, int bytes_len) {
-    DCHECK_EQ(type_, TYPE_BYTES);
+    DCHECK_EQ(type_, mojom::DataElementType::kBytes);
     DCHECK_NE(length_, std::numeric_limits<uint64_t>::max());
     DCHECK(!bytes_);
     buf_.insert(buf_.end(), bytes, bytes + bytes_len);
@@ -111,7 +96,7 @@
   // should make sure the data is alive when this element is accessed.
   // You cannot use AppendBytes with this method.
   void SetToSharedBytes(const char* bytes, int bytes_len) {
-    type_ = TYPE_BYTES;
+    type_ = mojom::DataElementType::kBytes;
     bytes_ = bytes;
     length_ = bytes_len;
   }
@@ -120,7 +105,7 @@
   // internal vector but does not populate it with anything.  The caller can
   // then use the bytes() method to access this buffer and populate it.
   void SetToAllocatedBytes(size_t bytes_len) {
-    type_ = TYPE_BYTES;
+    type_ = mojom::DataElementType::kBytes;
     bytes_ = nullptr;
     buf_.resize(bytes_len);
     length_ = bytes_len;
@@ -186,7 +171,7 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(BlobAsyncTransportStrategyTest, TestInvalidParams);
   friend void PrintTo(const DataElement& x, ::std::ostream* os);
-  Type type_;
+  mojom::DataElementType type_;
   // For TYPE_BYTES.
   std::vector<char> buf_;
   // For TYPE_BYTES.
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index 197314a..78a22a7 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -226,11 +226,11 @@
     case net::DnsQueryType::AAAA:
       return ResolveHostParameters::DnsQueryType::AAAA;
     case net::DnsQueryType::TXT:
+      return ResolveHostParameters::DnsQueryType::TXT;
     case net::DnsQueryType::PTR:
+      return ResolveHostParameters::DnsQueryType::PTR;
     case net::DnsQueryType::SRV:
-      // TODO(crbug.com/846423): Add Mojo support for non-address types.
-      NOTIMPLEMENTED();
-      return ResolveHostParameters::DnsQueryType::kMinValue;
+      return ResolveHostParameters::DnsQueryType::SRV;
   }
 }
 
@@ -248,6 +248,15 @@
     case ResolveHostParameters::DnsQueryType::AAAA:
       *output = net::DnsQueryType::AAAA;
       return true;
+    case ResolveHostParameters::DnsQueryType::TXT:
+      *output = net::DnsQueryType::TXT;
+      return true;
+    case ResolveHostParameters::DnsQueryType::PTR:
+      *output = net::DnsQueryType::PTR;
+      return true;
+    case ResolveHostParameters::DnsQueryType::SRV:
+      *output = net::DnsQueryType::SRV;
+      return true;
   }
 }
 
diff --git a/services/network/public/cpp/network_ipc_param_traits.cc b/services/network/public/cpp/network_ipc_param_traits.cc
index 624d2f4..eb204aa 100644
--- a/services/network/public/cpp/network_ipc_param_traits.cc
+++ b/services/network/public/cpp/network_ipc_param_traits.cc
@@ -9,6 +9,7 @@
 #include "ipc/ipc_platform_file.h"
 #include "net/http/http_util.h"
 #include "services/network/public/cpp/http_raw_request_response_info.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
 
 namespace IPC {
 
@@ -62,18 +63,18 @@
                                               const param_type& p) {
   WriteParam(m, static_cast<int>(p.type()));
   switch (p.type()) {
-    case network::DataElement::TYPE_BYTES: {
+    case network::mojom::DataElementType::kBytes: {
       m->WriteData(p.bytes(), static_cast<int>(p.length()));
       break;
     }
-    case network::DataElement::TYPE_FILE: {
+    case network::mojom::DataElementType::kFile: {
       WriteParam(m, p.path());
       WriteParam(m, p.offset());
       WriteParam(m, p.length());
       WriteParam(m, p.expected_modification_time());
       break;
     }
-    case network::DataElement::TYPE_RAW_FILE: {
+    case network::mojom::DataElementType::kRawFile: {
       WriteParam(
           m, IPC::GetPlatformFileForTransit(p.file().GetPlatformFile(),
                                             false /* close_source_handle */));
@@ -83,25 +84,25 @@
       WriteParam(m, p.expected_modification_time());
       break;
     }
-    case network::DataElement::TYPE_BLOB: {
+    case network::mojom::DataElementType::kBlob: {
       WriteParam(m, p.blob_uuid());
       WriteParam(m, p.offset());
       WriteParam(m, p.length());
       break;
     }
-    case network::DataElement::TYPE_DATA_PIPE: {
+    case network::mojom::DataElementType::kDataPipe: {
       WriteParam(
           m, p.CloneDataPipeGetter().PassInterface().PassHandle().release());
       break;
     }
-    case network::DataElement::TYPE_CHUNKED_DATA_PIPE: {
+    case network::mojom::DataElementType::kChunkedDataPipe: {
       WriteParam(m, const_cast<network::DataElement&>(p)
                         .ReleaseChunkedDataPipeGetter()
                         .PassHandle()
                         .release());
       break;
     }
-    case network::DataElement::TYPE_UNKNOWN: {
+    case network::mojom::DataElementType::kUnknown: {
       NOTREACHED();
       break;
     }
@@ -114,8 +115,8 @@
   int type;
   if (!ReadParam(m, iter, &type))
     return false;
-  switch (type) {
-    case network::DataElement::TYPE_BYTES: {
+  switch (static_cast<network::mojom::DataElementType>(type)) {
+    case network::mojom::DataElementType::kBytes: {
       const char* data;
       int len;
       if (!iter->ReadData(&data, &len))
@@ -123,7 +124,7 @@
       r->SetToBytes(data, len);
       return true;
     }
-    case network::DataElement::TYPE_FILE: {
+    case network::mojom::DataElementType::kFile: {
       base::FilePath file_path;
       uint64_t offset, length;
       base::Time expected_modification_time;
@@ -139,7 +140,7 @@
                             expected_modification_time);
       return true;
     }
-    case network::DataElement::TYPE_RAW_FILE: {
+    case network::mojom::DataElementType::kRawFile: {
       IPC::PlatformFileForTransit platform_file_for_transit;
       if (!ReadParam(m, iter, &platform_file_for_transit))
         return false;
@@ -160,7 +161,7 @@
                         expected_modification_time);
       return true;
     }
-    case network::DataElement::TYPE_BLOB: {
+    case network::mojom::DataElementType::kBlob: {
       std::string blob_uuid;
       uint64_t offset, length;
       if (!ReadParam(m, iter, &blob_uuid))
@@ -172,7 +173,7 @@
       r->SetToBlobRange(blob_uuid, offset, length);
       return true;
     }
-    case network::DataElement::TYPE_DATA_PIPE: {
+    case network::mojom::DataElementType::kDataPipe: {
       network::mojom::DataPipeGetterPtr data_pipe_getter;
       mojo::MessagePipeHandle message_pipe;
       if (!ReadParam(m, iter, &message_pipe))
@@ -182,7 +183,7 @@
       r->SetToDataPipe(std::move(data_pipe_getter));
       return true;
     }
-    case network::DataElement::TYPE_CHUNKED_DATA_PIPE: {
+    case network::mojom::DataElementType::kChunkedDataPipe: {
       network::mojom::ChunkedDataPipeGetterPtr chunked_data_pipe_getter;
       mojo::MessagePipeHandle message_pipe;
       if (!ReadParam(m, iter, &message_pipe))
@@ -193,7 +194,7 @@
       r->SetToChunkedDataPipe(std::move(chunked_data_pipe_getter));
       return true;
     }
-    case network::DataElement::TYPE_UNKNOWN: {
+    case network::mojom::DataElementType::kUnknown: {
       NOTREACHED();
       return false;
     }
@@ -232,7 +233,7 @@
   // A chunked element is only allowed if it's the only one element.
   if (elements.size() > 1) {
     for (const auto& element : elements) {
-      if (element.type() == network::DataElement::TYPE_CHUNKED_DATA_PIPE)
+      if (element.type() == network::mojom::DataElementType::kChunkedDataPipe)
         return false;
     }
   }
diff --git a/services/network/public/cpp/p2p_param_traits.h b/services/network/public/cpp/p2p_param_traits.h
index 4a511604..7d1fe868 100644
--- a/services/network/public/cpp/p2p_param_traits.h
+++ b/services/network/public/cpp/p2p_param_traits.h
@@ -17,7 +17,7 @@
 #include "net/base/network_interfaces.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/p2p_socket_type.h"
-#include "third_party/webrtc/rtc_base/asyncpacketsocket.h"
+#include "third_party/webrtc/rtc_base/async_packet_socket.h"
 
 #ifndef INTERNAL_SERVICES_NETWORK_PUBLIC_CPP_P2P_PARAM_TRAITS_H_
 #define INTERNAL_SERVICES_NETWORK_PUBLIC_CPP_P2P_PARAM_TRAITS_H_
diff --git a/services/network/public/cpp/p2p_socket_type.h b/services/network/public/cpp/p2p_socket_type.h
index 965f52c1b..e88d18d0 100644
--- a/services/network/public/cpp/p2p_socket_type.h
+++ b/services/network/public/cpp/p2p_socket_type.h
@@ -14,7 +14,7 @@
 
 #include "base/time/time.h"
 #include "net/base/ip_endpoint.h"
-#include "third_party/webrtc/rtc_base/asyncpacketsocket.h"
+#include "third_party/webrtc/rtc_base/async_packet_socket.h"
 
 namespace network {
 
diff --git a/services/network/public/cpp/resolve_host_client_base.cc b/services/network/public/cpp/resolve_host_client_base.cc
new file mode 100644
index 0000000..c78cf8cd
--- /dev/null
+++ b/services/network/public/cpp/resolve_host_client_base.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 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 "services/network/public/cpp/resolve_host_client_base.h"
+
+#include "base/logging.h"
+#include "net/base/host_port_pair.h"
+
+namespace network {
+
+void ResolveHostClientBase::OnTextResults(
+    const std::vector<std::string>& text_results) {
+  // Should be overridden if text results are expected.
+  NOTREACHED();
+}
+
+void ResolveHostClientBase::OnHostnameResults(
+    const std::vector<net::HostPortPair>& hosts) {
+  // Should be overridden if hostname results are expected.
+  NOTREACHED();
+}
+
+}  // namespace network
diff --git a/services/network/public/cpp/resolve_host_client_base.h b/services/network/public/cpp/resolve_host_client_base.h
new file mode 100644
index 0000000..4d26b20
--- /dev/null
+++ b/services/network/public/cpp/resolve_host_client_base.h
@@ -0,0 +1,33 @@
+// Copyright 2018 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 SERVICES_NETWORK_PUBLIC_CPP_RESOLVE_HOST_CLIENT_BASE_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_RESOLVE_HOST_CLIENT_BASE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "services/network/public/mojom/host_resolver.mojom.h"
+
+namespace net {
+class HostPortPair;
+}
+
+namespace network {
+
+// Partial ResolveHostClient implementation with DCHECKing implementations for
+// optional On...Result() methods.  Allows implementers to override just the
+// methods for expected result types.
+class COMPONENT_EXPORT(NETWORK_CPP) ResolveHostClientBase
+    : public mojom::ResolveHostClient {
+ public:
+  void OnTextResults(const std::vector<std::string>& text_results) override;
+
+  void OnHostnameResults(const std::vector<net::HostPortPair>& hosts) override;
+};
+
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_RESOLVE_HOST_CLIENT_BASE_H_
diff --git a/services/network/public/cpp/resource_request_body.cc b/services/network/public/cpp/resource_request_body.cc
index f777c63..923d365 100644
--- a/services/network/public/cpp/resource_request_body.cc
+++ b/services/network/public/cpp/resource_request_body.cc
@@ -20,7 +20,7 @@
 
 void ResourceRequestBody::AppendBytes(std::vector<char> bytes) {
   DCHECK(elements_.empty() ||
-         elements_.front().type() != DataElement::TYPE_CHUNKED_DATA_PIPE);
+         elements_.front().type() != mojom::DataElementType::kChunkedDataPipe);
 
   if (bytes.size() > 0) {
     elements_.push_back(DataElement());
@@ -41,7 +41,7 @@
     uint64_t length,
     const base::Time& expected_modification_time) {
   DCHECK(elements_.empty() ||
-         elements_.front().type() != DataElement::TYPE_CHUNKED_DATA_PIPE);
+         elements_.front().type() != mojom::DataElementType::kChunkedDataPipe);
 
   elements_.push_back(DataElement());
   elements_.back().SetToFilePathRange(file_path, offset, length,
@@ -55,7 +55,7 @@
     uint64_t length,
     const base::Time& expected_modification_time) {
   DCHECK(elements_.empty() ||
-         elements_.front().type() != DataElement::TYPE_CHUNKED_DATA_PIPE);
+         elements_.front().type() != mojom::DataElementType::kChunkedDataPipe);
 
   elements_.push_back(DataElement());
   elements_.back().SetToFileRange(std::move(file), file_path, offset, length,
@@ -68,7 +68,7 @@
 
 void ResourceRequestBody::AppendBlob(const std::string& uuid, uint64_t length) {
   DCHECK(elements_.empty() ||
-         elements_.front().type() != DataElement::TYPE_CHUNKED_DATA_PIPE);
+         elements_.front().type() != mojom::DataElementType::kChunkedDataPipe);
 
   elements_.push_back(DataElement());
   elements_.back().SetToBlobRange(uuid, 0 /* offset */, length);
@@ -77,7 +77,7 @@
 void ResourceRequestBody::AppendDataPipe(
     mojom::DataPipeGetterPtr data_pipe_getter) {
   DCHECK(elements_.empty() ||
-         elements_.front().type() != DataElement::TYPE_CHUNKED_DATA_PIPE);
+         elements_.front().type() != mojom::DataElementType::kChunkedDataPipe);
 
   elements_.push_back(DataElement());
   elements_.back().SetToDataPipe(std::move(data_pipe_getter));
@@ -94,7 +94,7 @@
 std::vector<base::FilePath> ResourceRequestBody::GetReferencedFiles() const {
   std::vector<base::FilePath> result;
   for (const auto& element : *elements()) {
-    if (element.type() == DataElement::TYPE_FILE)
+    if (element.type() == mojom::DataElementType::kFile)
       result.push_back(element.path());
   }
   return result;
diff --git a/services/network/public/cpp/resource_request_body.h b/services/network/public/cpp/resource_request_body.h
index 90bacae8..fc6e9ff3 100644
--- a/services/network/public/cpp/resource_request_body.h
+++ b/services/network/public/cpp/resource_request_body.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "services/network/public/cpp/data_element.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -84,7 +85,8 @@
   void set_identifier(int64_t id) { identifier_ = id; }
   int64_t identifier() const { return identifier_; }
 
-  // Returns paths referred to by |elements| of type DataElement::TYPE_FILE.
+  // Returns paths referred to by |elements| of type
+  // network::mojom::DataElementType::kFile.
   std::vector<base::FilePath> GetReferencedFiles() const;
 
   // Sets the flag which indicates whether the post data contains sensitive
@@ -96,7 +98,8 @@
 
  private:
   friend class base::RefCountedThreadSafe<ResourceRequestBody>;
-
+  friend struct mojo::StructTraits<network::mojom::URLRequestBodyDataView,
+                                   scoped_refptr<network::ResourceRequestBody>>;
   ~ResourceRequestBody();
 
   std::vector<DataElement> elements_;
diff --git a/services/network/public/cpp/simple_url_loader.cc b/services/network/public/cpp/simple_url_loader.cc
index 0b4f4b9..0c41016 100644
--- a/services/network/public/cpp/simple_url_loader.cc
+++ b/services/network/public/cpp/simple_url_loader.cc
@@ -1166,8 +1166,8 @@
       //
       // TODO(mmenke): Add a similar method for bytes, to allow streaming of
       // large byte buffers to the network process when uploading.
-      DCHECK(element.type() != DataElement::TYPE_FILE &&
-             element.type() != DataElement::TYPE_BYTES);
+      DCHECK(element.type() != mojom::DataElementType::kFile &&
+             element.type() != mojom::DataElementType::kBytes);
     }
   }
 #endif  // DCHECK_IS_ON()
@@ -1356,7 +1356,7 @@
       // pipe.
       // TODO(mmenke):  Data pipes can be Cloned(), though, so maybe update code
       // to do that?
-      DCHECK(element.type() != DataElement::TYPE_DATA_PIPE);
+      DCHECK(element.type() != mojom::DataElementType::kDataPipe);
     }
   }
 #endif  // DCHECK_IS_ON()
diff --git a/services/network/public/cpp/simple_url_loader_unittest.cc b/services/network/public/cpp/simple_url_loader_unittest.cc
index f7780cc..a71782b6 100644
--- a/services/network/public/cpp/simple_url_loader_unittest.cc
+++ b/services/network/public/cpp/simple_url_loader_unittest.cc
@@ -1730,7 +1730,7 @@
         weak_factory_for_data_pipe_callbacks_(this) {
     if (request_body && request_body->elements()->size() == 1 &&
         (*request_body->elements())[0].type() ==
-            network::DataElement::TYPE_DATA_PIPE) {
+            network::mojom::DataElementType::kDataPipe) {
       data_pipe_getter_ = (*request_body->elements())[0].CloneDataPipeGetter();
       DCHECK(data_pipe_getter_);
     }
diff --git a/services/network/public/cpp/url_request.typemap b/services/network/public/cpp/url_request.typemap
index 8a808b19..4594783 100644
--- a/services/network/public/cpp/url_request.typemap
+++ b/services/network/public/cpp/url_request.typemap
@@ -3,17 +3,23 @@
 # found in the LICENSE file.
 
 mojom = "//services/network/public/mojom/url_loader.mojom"
-public_headers = [ "//services/network/public/cpp/resource_request.h" ]
+public_headers = [
+  "//base/memory/scoped_refptr.h",
+  "//services/network/public/cpp/resource_request.h",
+  "//services/network/public/cpp/resource_request_body.h",
+]
 traits_headers = [
   "//services/network/public/cpp/network_ipc_param_traits.h",
   "//services/network/public/cpp/url_request_mojom_traits.h",
 ]
 public_deps = [
+  "//base",
   "//services/network/public/cpp:cpp_base",
 ]
 type_mappings = [
   "network.mojom.URLRequest=network::ResourceRequest",
-  "network.mojom.URLRequestBody=scoped_refptr<network::ResourceRequestBody>",
+  "network.mojom.URLRequestBody=scoped_refptr<network::ResourceRequestBody>[nullable_is_same_type,copyable_pass_by_value]",
   "network.mojom.URLRequestReferrerPolicy=net::URLRequest::ReferrerPolicy",
   "network.mojom.RequestPriority=net::RequestPriority",
+  "network.mojom.DataElement=network::DataElement[move_only]",
 ]
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 0248c6e..5aa0a0e 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -8,6 +8,8 @@
 #include "mojo/public/cpp/base/unguessable_token_mojom_traits.h"
 #include "services/network/public/cpp/http_request_headers_mojom_traits.h"
 #include "services/network/public/cpp/network_ipc_param_traits.h"
+#include "services/network/public/cpp/resource_request_body.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "url/mojom/origin_mojom_traits.h"
 #include "url/mojom/url_gurl_mojom_traits.h"
 
@@ -207,4 +209,17 @@
   return true;
 }
 
+bool StructTraits<network::mojom::URLRequestBodyDataView,
+                  scoped_refptr<network::ResourceRequestBody>>::
+    Read(network::mojom::URLRequestBodyDataView data,
+         scoped_refptr<network::ResourceRequestBody>* out) {
+  auto body = base::MakeRefCounted<network::ResourceRequestBody>();
+  if (!data.ReadElements(&(body->elements_)))
+    return false;
+  body->set_identifier(data.identifier());
+  body->set_contains_sensitive_info(data.contains_sensitive_info());
+  *out = std::move(body);
+  return true;
+}
+
 }  // namespace mojo
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index 9ae29e9..a01cf35 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -6,9 +6,12 @@
 #define SERVICES_NETWORK_PUBLIC_CPP_URL_REQUEST_MOJOM_TRAITS_H_
 
 #include "base/component_export.h"
+#include "base/memory/scoped_refptr.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
 #include "net/base/request_priority.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/mojom/url_loader.mojom-shared.h"
 
 namespace mojo {
@@ -217,6 +220,37 @@
                    network::ResourceRequest* out);
 };
 
+template <>
+struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
+    StructTraits<network::mojom::URLRequestBodyDataView,
+                 scoped_refptr<network::ResourceRequestBody>> {
+  static bool IsNull(const scoped_refptr<network::ResourceRequestBody>& r) {
+    return !r;
+  }
+
+  static void SetToNull(scoped_refptr<network::ResourceRequestBody>* output) {
+    output->reset();
+  }
+
+  static const std::vector<network::DataElement>& elements(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return *r->elements();
+  }
+
+  static uint64_t identifier(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return r->identifier_;
+  }
+
+  static bool contains_sensitive_info(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return r->contains_sensitive_info_;
+  }
+
+  static bool Read(network::mojom::URLRequestBodyDataView data,
+                   scoped_refptr<network::ResourceRequestBody>* out);
+};
+
 }  // namespace mojo
 
 #endif  // SERVICES_NETWORK_PUBLIC_CPP_URL_REQUEST_MOJOM_TRAITS_H_
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index bd67190..b3e723f1 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -97,12 +97,25 @@
 // Response interface used to receive results of NetworkContext::ResolveHost
 // requests.
 //
-// TODO(crbug.com/821021): Add additional methods to receive non-address
-// results (eg TXT DNS responses).
+// If only some result types are expected for an implementation, e.g. only
+// address types will be queried, see
+// services/network/public/cpp/resolve_host_client_base.h for a partial C++
+// implementation that allows overriding just expected result types.
 interface ResolveHostClient {
   // Called on completion of a resolve host request. Results are a network error
-  // code, and on success (network error code OK), an AddressList.
+  // code, and on success (network error code OK) and when results are in
+  // address form, an AddressList.
+  //
+  // Always called last to signal completion of the ResolveHost() request.
+  // Unless the ResolveHostClient is closed, this method will always be called
+  // exactly once, and afterwards, no more methods in the interface will be
+  // called.
   OnComplete(int32 result, AddressList? resolved_addresses);
+
+  // One or more On...Results() methods may be called when non-address results
+  // are available. Always called before OnComplete().
+  OnTextResults(array<string> text_results);
+  OnHostnameResults(array<HostPortPair> hosts);
 };
 
 // Parameter-grouping struct for additional optional parameters for
@@ -116,6 +129,9 @@
     UNSPECIFIED,
     A,
     AAAA,
+    TXT,
+    PTR,
+    SRV,
   };
 
   // Requested DNS query type. If UNSPECIFIED, resolver will pick A or AAAA (or
diff --git a/services/network/public/mojom/url_loader.mojom b/services/network/public/mojom/url_loader.mojom
index ef3f8c29..84a71cc 100644
--- a/services/network/public/mojom/url_loader.mojom
+++ b/services/network/public/mojom/url_loader.mojom
@@ -13,8 +13,22 @@
 import "url/mojom/url.mojom";
 import "url/mojom/origin.mojom";
 
+// Typemapped to network::ResourceRequestBody
+struct URLRequestBody {
+  // Store upload bodies
+  array<DataElement> elements;
+
+  // Identifies a particular upload instance, which is used by the cache to
+  // formulate a cache key.
+  uint64 identifier;
+
+  // Indicates whether the post data contains sensitive information like
+  // passwords.
+  bool contains_sensitive_info;
+};
+
 [Native]
-struct URLRequestBody;
+struct DataElement;
 
 [Native]
 struct URLResponseHead;
@@ -50,6 +64,24 @@
   kNoReferrer
 };
 
+// Used for represents the type of the internal contents of
+// network::DataElement.
+enum DataElementType {
+  kUnknown = -1,
+
+  // Only used for Upload with Network Service as of now:
+  kDataPipe,
+  kChunkedDataPipe,
+  kRawFile,
+
+  // Used for Upload when Network Service is disabled:
+  kBlob,
+  kFile,
+
+  // Commonly used in every case:
+  kBytes,
+};
+
 // Typemapped to network::ResourceRequest.
 struct URLRequest {
   // The request method: GET, POST, etc.
diff --git a/services/network/resolve_host_request.cc b/services/network/resolve_host_request.cc
index 6baf554..a999fce 100644
--- a/services/network/resolve_host_request.cc
+++ b/services/network/resolve_host_request.cc
@@ -87,6 +87,7 @@
   DCHECK(callback_);
 
   control_handle_binding_.Close();
+  SignalNonAddressResults();
   response_client_->OnComplete(error, GetAddressResults());
   response_client_ = nullptr;
 
@@ -106,4 +107,20 @@
   return internal_request_->GetAddressResults();
 }
 
+void ResolveHostRequest::SignalNonAddressResults() {
+  if (cancelled_)
+    return;
+  DCHECK(internal_request_);
+
+  if (internal_request_->GetTextResults()) {
+    response_client_->OnTextResults(
+        internal_request_->GetTextResults().value());
+  }
+
+  if (internal_request_->GetHostnameResults()) {
+    response_client_->OnHostnameResults(
+        internal_request_->GetHostnameResults().value());
+  }
+}
+
 }  // namespace network
diff --git a/services/network/resolve_host_request.h b/services/network/resolve_host_request.h
index 8ee3d2d3..7bd2bd3 100644
--- a/services/network/resolve_host_request.h
+++ b/services/network/resolve_host_request.h
@@ -44,6 +44,7 @@
  private:
   void OnComplete(int error);
   const base::Optional<net::AddressList>& GetAddressResults() const;
+  void SignalNonAddressResults();
 
   std::unique_ptr<net::HostResolver::ResolveHostRequest> internal_request_;
 
diff --git a/services/network/test/test_utils.cc b/services/network/test/test_utils.cc
index 75f902e..8f59c20b8 100644
--- a/services/network/test/test_utils.cc
+++ b/services/network/test/test_utils.cc
@@ -18,7 +18,7 @@
 
   CHECK_EQ(1u, body->elements()->size());
   const auto& element = body->elements()->at(0);
-  CHECK_EQ(network::DataElement::TYPE_BYTES, element.type());
+  CHECK_EQ(mojom::DataElementType::kBytes, element.type());
   return std::string(element.bytes(), element.length());
 }
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 3b6253d4..7e8c007 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -121,7 +121,7 @@
                      const DataElement& element)
       : net::UploadBytesElementReader(element.bytes(), element.length()),
         resource_request_body_(resource_request_body) {
-    DCHECK_EQ(DataElement::TYPE_BYTES, element.type());
+    DCHECK_EQ(network::mojom::DataElementType::kBytes, element.type());
   }
 
   ~BytesElementReader() override {}
@@ -149,7 +149,7 @@
                                      element.length(),
                                      element.expected_modification_time()),
         resource_request_body_(resource_request_body) {
-    DCHECK_EQ(DataElement::TYPE_FILE, element.type());
+    DCHECK_EQ(network::mojom::DataElementType::kFile, element.type());
   }
 
   ~FileElementReader() override {}
@@ -174,7 +174,7 @@
             element.length(),
             element.expected_modification_time()),
         resource_request_body_(resource_request_body) {
-    DCHECK_EQ(DataElement::TYPE_RAW_FILE, element.type());
+    DCHECK_EQ(network::mojom::DataElementType::kRawFile, element.type());
   }
 
   ~RawFileElementReader() override {}
@@ -193,7 +193,7 @@
   // In the case of a chunked upload, there will just be one element.
   if (body->elements()->size() == 1 &&
       body->elements()->begin()->type() ==
-          DataElement::TYPE_CHUNKED_DATA_PIPE) {
+          network::mojom::DataElementType::kChunkedDataPipe) {
     return std::make_unique<ChunkedDataPipeUploadDataStream>(
         body, mojom::ChunkedDataPipeGetterPtr(
                   const_cast<DataElement&>(body->elements()->front())
@@ -204,35 +204,35 @@
   std::vector<std::unique_ptr<net::UploadElementReader>> element_readers;
   for (const auto& element : *body->elements()) {
     switch (element.type()) {
-      case DataElement::TYPE_BYTES:
+      case network::mojom::DataElementType::kBytes:
         element_readers.push_back(
             std::make_unique<BytesElementReader>(body, element));
         break;
-      case DataElement::TYPE_FILE:
+      case network::mojom::DataElementType::kFile:
         DCHECK(opened_file != opened_files.end());
         element_readers.push_back(std::make_unique<FileElementReader>(
             body, file_task_runner, element, std::move(*opened_file++)));
         break;
-      case DataElement::TYPE_RAW_FILE:
+      case network::mojom::DataElementType::kRawFile:
         element_readers.push_back(std::make_unique<RawFileElementReader>(
             body, file_task_runner, element));
         break;
-      case DataElement::TYPE_BLOB: {
+      case network::mojom::DataElementType::kBlob: {
         CHECK(false) << "Network service always uses DATA_PIPE for blobs.";
         break;
       }
-      case DataElement::TYPE_DATA_PIPE: {
+      case network::mojom::DataElementType::kDataPipe: {
         element_readers.push_back(std::make_unique<DataPipeElementReader>(
             body, element.CloneDataPipeGetter()));
         break;
       }
-      case DataElement::TYPE_CHUNKED_DATA_PIPE: {
+      case network::mojom::DataElementType::kChunkedDataPipe: {
         // This shouldn't happen, as the traits logic should ensure that if
         // there's a chunked pipe, there's one and only one element.
         NOTREACHED();
         break;
       }
-      case DataElement::TYPE_UNKNOWN:
+      case network::mojom::DataElementType::kUnknown:
         NOTREACHED();
         break;
     }
@@ -464,7 +464,7 @@
 void URLLoader::OpenFilesForUpload(const ResourceRequest& request) {
   std::vector<base::FilePath> paths;
   for (const auto& element : *request.request_body.get()->elements()) {
-    if (element.type() == DataElement::TYPE_FILE)
+    if (element.type() == mojom::DataElementType::kFile)
       paths.push_back(element.path());
   }
   if (paths.empty()) {
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index 31d864e..c59ffd0 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -14,6 +14,14 @@
     "agent_registry.h",
     "coordinator.cc",
     "coordinator.h",
+    "perfetto/json_trace_exporter.cc",
+    "perfetto/json_trace_exporter.h",
+    "perfetto/perfetto_service.cc",
+    "perfetto/perfetto_service.h",
+    "perfetto/perfetto_tracing_coordinator.cc",
+    "perfetto/perfetto_tracing_coordinator.h",
+    "perfetto/producer_host.cc",
+    "perfetto/producer_host.h",
     "recorder.cc",
     "recorder.h",
     "tracing_service.cc",
@@ -26,26 +34,12 @@
     "//base",
     "//mojo/public/cpp/bindings",
     "//services/tracing/public/cpp",
+    "//third_party/perfetto:libperfetto",
   ]
 
-  if (is_mac || is_linux || is_android || is_win || is_fuchsia) {
-    sources += [
-      "perfetto/json_trace_exporter.cc",
-      "perfetto/json_trace_exporter.h",
-      "perfetto/perfetto_service.cc",
-      "perfetto/perfetto_service.h",
-      "perfetto/perfetto_tracing_coordinator.cc",
-      "perfetto/perfetto_tracing_coordinator.h",
-      "perfetto/producer_host.cc",
-      "perfetto/producer_host.h",
-    ]
-
-    deps = [
-      "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
-    ]
-
-    public_deps += [ "//third_party/perfetto:libperfetto" ]
-  }
+  deps = [
+    "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
+  ]
 
   if (is_chromecast) {
     defines = [ "IS_CHROMECAST" ]
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn
index b864f2d..847eff5 100644
--- a/services/tracing/public/cpp/BUILD.gn
+++ b/services/tracing/public/cpp/BUILD.gn
@@ -6,6 +6,18 @@
   sources = [
     "base_agent.cc",
     "base_agent.h",
+    "perfetto/heap_scattered_stream_delegate.cc",
+    "perfetto/heap_scattered_stream_delegate.h",
+    "perfetto/producer_client.cc",
+    "perfetto/producer_client.h",
+    "perfetto/shared_memory.cc",
+    "perfetto/shared_memory.h",
+    "perfetto/task_runner.cc",
+    "perfetto/task_runner.h",
+    "perfetto/trace_event_data_source.cc",
+    "perfetto/trace_event_data_source.h",
+    "perfetto/traced_value_proto_writer.cc",
+    "perfetto/traced_value_proto_writer.h",
     "trace_event_agent.cc",
     "trace_event_agent.h",
     "tracing_features.cc",
@@ -22,29 +34,11 @@
     "//mojo/public/cpp/bindings",
     "//services/service_manager/public/cpp",
     "//services/tracing/public/mojom",
+    "//third_party/perfetto:libperfetto",
   ]
 
-  if (is_mac || is_linux || is_android || is_win || is_fuchsia) {
-    sources += [
-      "perfetto/heap_scattered_stream_delegate.cc",
-      "perfetto/heap_scattered_stream_delegate.h",
-      "perfetto/producer_client.cc",
-      "perfetto/producer_client.h",
-      "perfetto/shared_memory.cc",
-      "perfetto/shared_memory.h",
-      "perfetto/task_runner.cc",
-      "perfetto/task_runner.h",
-      "perfetto/trace_event_data_source.cc",
-      "perfetto/trace_event_data_source.h",
-      "perfetto/traced_value_proto_writer.cc",
-      "perfetto/traced_value_proto_writer.h",
-    ]
-
-    deps = [
-      "//third_party/perfetto/include/perfetto/protozero:protozero",
-      "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
-    ]
-
-    public_deps += [ "//third_party/perfetto:libperfetto" ]
-  }
+  deps = [
+    "//third_party/perfetto/include/perfetto/protozero:protozero",
+    "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
+  ]
 }
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc
index 3ef0e51..214b90d 100644
--- a/services/tracing/public/cpp/trace_event_agent.cc
+++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -18,14 +18,9 @@
 #include "base/trace_event/trace_log.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "services/tracing/public/cpp/tracing_features.h"
-
-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) || \
-    defined(OS_WIN) || defined(OS_FUCHSIA)
-#define PERFETTO_AVAILABLE
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
-#endif
+#include "services/tracing/public/cpp/tracing_features.h"
 
 namespace {
 
@@ -47,9 +42,8 @@
                 base::trace_event::TraceLog::GetInstance()->process_id()),
       enabled_tracing_modes_(0) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-#if defined(PERFETTO_AVAILABLE)
+
   ProducerClient::Get()->AddDataSource(TraceEventDataSource::GetInstance());
-#endif
 }
 
 TraceEventAgent::~TraceEventAgent() = default;
@@ -61,10 +55,7 @@
   }
 
   BaseAgent::Connect(connector);
-
-#if defined(PERFETTO_AVAILABLE)
   ProducerClient::Get()->Connect(connector);
-#endif
 }
 
 void TraceEventAgent::GetCategories(std::set<std::string>* category_set) {
@@ -79,7 +70,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   metadata_generator_functions_.push_back(generator);
 
-#if defined(PERFETTO_AVAILABLE)
   // Instantiate and register the metadata data source on the first
   // call.
   static TraceEventMetadataSource* metadata_source = []() {
@@ -89,7 +79,6 @@
   }();
 
   metadata_source->AddGeneratorFunction(generator);
-#endif
 }
 
 void TraceEventAgent::StartTracing(const std::string& config,
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc
index 136bf136..9d1d088 100644
--- a/services/tracing/tracing_service.cc
+++ b/services/tracing/tracing_service.cc
@@ -9,12 +9,9 @@
 #include "base/timer/timer.h"
 #include "services/tracing/agent_registry.h"
 #include "services/tracing/coordinator.h"
-#include "services/tracing/public/cpp/tracing_features.h"
-
-#if defined(PERFETTO_SERVICE_AVAILABLE)
 #include "services/tracing/perfetto/perfetto_service.h"
 #include "services/tracing/perfetto/perfetto_tracing_coordinator.h"
-#endif
+#include "services/tracing/public/cpp/tracing_features.h"
 
 namespace tracing {
 
@@ -25,20 +22,15 @@
 TracingService::~TracingService() {
   task_runner_->DeleteSoon(FROM_HERE, std::move(tracing_agent_registry_));
 
-#if defined(PERFETTO_SERVICE_AVAILABLE)
   if (perfetto_tracing_coordinator_) {
     task_runner_->DeleteSoon(FROM_HERE,
                              std::move(perfetto_tracing_coordinator_));
   }
-#endif
 }
 
 void TracingService::OnStart() {
   tracing_agent_registry_ = std::make_unique<AgentRegistry>();
 
-  bool enable_legacy_tracing = true;
-
-#if defined(PERFETTO_SERVICE_AVAILABLE)
   registry_.AddInterface(base::BindRepeating(
       &tracing::PerfettoService::BindRequest,
       base::Unretained(tracing::PerfettoService::GetInstance())));
@@ -52,19 +44,7 @@
         base::BindRepeating(&PerfettoTracingCoordinator::BindCoordinatorRequest,
                             base::Unretained(perfetto_coordinator.get())));
     perfetto_tracing_coordinator_ = std::move(perfetto_coordinator);
-
-    enable_legacy_tracing = false;
-  }
-#else
-  if (TracingUsesPerfettoBackend()) {
-    LOG(ERROR) << "Perfetto is not yet available for this platform; falling "
-                  "back to using legacy TraceLog";
-  }
-#endif
-
-  // Use legacy tracing if we're on an unsupported platform or the feature flag
-  // is disabled.
-  if (enable_legacy_tracing) {
+  } else {
     auto tracing_coordinator =
         std::make_unique<Coordinator>(tracing_agent_registry_.get());
     registry_.AddInterface(
diff --git a/services/tracing/tracing_service.h b/services/tracing/tracing_service.h
index dd952a1..291c32e 100644
--- a/services/tracing/tracing_service.h
+++ b/services/tracing/tracing_service.h
@@ -19,11 +19,6 @@
 #include "services/tracing/agent_registry.h"
 #include "services/tracing/coordinator.h"
 
-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) || \
-    defined(OS_WIN) || defined(OS_FUCHSIA)
-#define PERFETTO_SERVICE_AVAILABLE
-#endif
-
 namespace tracing {
 
 class PerfettoTracingCoordinator;
@@ -48,10 +43,7 @@
   std::unique_ptr<tracing::AgentRegistry> tracing_agent_registry_;
   std::unique_ptr<Coordinator> tracing_coordinator_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-#if defined(PERFETTO_SERVICE_AVAILABLE)
   std::unique_ptr<PerfettoTracingCoordinator> perfetto_tracing_coordinator_;
-#endif
 
   // WeakPtrFactory members should always come last so WeakPtrs are destructed
   // before other members.
diff --git a/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.cc b/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.cc
index 3aad4d9..2cda379 100644
--- a/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.cc
@@ -9,18 +9,52 @@
 namespace mojo {
 
 // static
+viz::mojom::BeginFrameArgsType
+EnumTraits<viz::mojom::BeginFrameArgsType,
+           viz::BeginFrameArgs::BeginFrameArgsType>::
+    ToMojom(viz::BeginFrameArgs::BeginFrameArgsType type) {
+  switch (type) {
+    case viz::BeginFrameArgs::BeginFrameArgsType::INVALID:
+      return viz::mojom::BeginFrameArgsType::INVALID;
+    case viz::BeginFrameArgs::BeginFrameArgsType::NORMAL:
+      return viz::mojom::BeginFrameArgsType::NORMAL;
+    case viz::BeginFrameArgs::BeginFrameArgsType::MISSED:
+      return viz::mojom::BeginFrameArgsType::MISSED;
+  }
+  NOTREACHED();
+  return viz::mojom::BeginFrameArgsType::INVALID;
+}
+
+// static
+bool EnumTraits<viz::mojom::BeginFrameArgsType,
+                viz::BeginFrameArgs::BeginFrameArgsType>::
+    FromMojom(viz::mojom::BeginFrameArgsType input,
+              viz::BeginFrameArgs::BeginFrameArgsType* out) {
+  switch (input) {
+    case viz::mojom::BeginFrameArgsType::INVALID:
+      *out = viz::BeginFrameArgs::BeginFrameArgsType::INVALID;
+      return true;
+    case viz::mojom::BeginFrameArgsType::NORMAL:
+      *out = viz::BeginFrameArgs::BeginFrameArgsType::NORMAL;
+      return true;
+    case viz::mojom::BeginFrameArgsType::MISSED:
+      *out = viz::BeginFrameArgs::BeginFrameArgsType::MISSED;
+      return true;
+  }
+  return false;
+}
+
+// static
 bool StructTraits<viz::mojom::BeginFrameArgsDataView, viz::BeginFrameArgs>::
     Read(viz::mojom::BeginFrameArgsDataView data, viz::BeginFrameArgs* out) {
   if (!data.ReadFrameTime(&out->frame_time) ||
       !data.ReadDeadline(&out->deadline) ||
-      !data.ReadInterval(&out->interval)) {
+      !data.ReadInterval(&out->interval) || !data.ReadType(&out->type)) {
     return false;
   }
   out->source_id = data.source_id();
   out->sequence_number = data.sequence_number();
   out->trace_id = data.trace_id();
-  // TODO(eseckler): Use EnumTraits for |type|.
-  out->type = static_cast<viz::BeginFrameArgs::BeginFrameArgsType>(data.type());
   out->on_critical_path = data.on_critical_path();
   out->animate_only = data.animate_only();
   return true;
diff --git a/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.h b/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.h
index ef144d8..564c42f 100644
--- a/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.h
+++ b/services/viz/public/cpp/compositing/begin_frame_args_struct_traits.h
@@ -11,6 +11,16 @@
 namespace mojo {
 
 template <>
+struct EnumTraits<viz::mojom::BeginFrameArgsType,
+                  viz::BeginFrameArgs::BeginFrameArgsType> {
+  static viz::mojom::BeginFrameArgsType ToMojom(
+      viz::BeginFrameArgs::BeginFrameArgsType type);
+
+  static bool FromMojom(viz::mojom::BeginFrameArgsType input,
+                        viz::BeginFrameArgs::BeginFrameArgsType* out);
+};
+
+template <>
 struct StructTraits<viz::mojom::BeginFrameArgsDataView, viz::BeginFrameArgs> {
   static base::TimeTicks frame_time(const viz::BeginFrameArgs& args) {
     return args.frame_time;
@@ -36,8 +46,9 @@
     return args.trace_id;
   }
 
-  static viz::mojom::BeginFrameArgsType type(const viz::BeginFrameArgs& args) {
-    return static_cast<viz::mojom::BeginFrameArgsType>(args.type);
+  static viz::BeginFrameArgs::BeginFrameArgsType type(
+      const viz::BeginFrameArgs& args) {
+    return args.type;
   }
 
   static bool on_critical_path(const viz::BeginFrameArgs& args) {
diff --git a/services/viz/public/interfaces/compositing/begin_frame_args.mojom b/services/viz/public/interfaces/compositing/begin_frame_args.mojom
index 8dfadb6b..92c4d72c 100644
--- a/services/viz/public/interfaces/compositing/begin_frame_args.mojom
+++ b/services/viz/public/interfaces/compositing/begin_frame_args.mojom
@@ -9,8 +9,7 @@
 enum BeginFrameArgsType {
   INVALID,
   NORMAL,
-  MISSED,
-  BEGIN_FRAME_ARGS_TYPE_MAX
+  MISSED
 };
 
 // See components/viz/common/frame_sinks/begin_frame_args.h.
diff --git a/services/ws/drag_drop_delegate.cc b/services/ws/drag_drop_delegate.cc
index 1f19129..5c1c593 100644
--- a/services/ws/drag_drop_delegate.cc
+++ b/services/ws/drag_drop_delegate.cc
@@ -7,11 +7,11 @@
 #include "base/bind.h"
 #include "base/strings/string16.h"
 #include "mojo/public/cpp/bindings/map.h"
+#include "services/ws/window_tree.h"
 #include "ui/aura/mus/os_exchange_data_provider_mus.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 #include "ui/base/dragdrop/file_info.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/wm/core/coordinate_conversion.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "url/gurl.h"
 
 namespace ws {
@@ -46,19 +46,14 @@
   return mus_provider.GetData();
 }
 
-// Converts |location| in |window| coordinates to screen coordinates.
-gfx::Point ToScreenLocation(aura::Window* window, const gfx::Point& location) {
-  gfx::Point screen_location(location);
-  wm::ConvertPointToScreen(window, &screen_location);
-  return screen_location;
-}
-
 }  // namespace
 
-DragDropDelegate::DragDropDelegate(mojom::WindowTreeClient* window_tree_client,
+DragDropDelegate::DragDropDelegate(WindowTree* window_tree,
+                                   mojom::WindowTreeClient* window_tree_client,
                                    aura::Window* window,
                                    Id transport_window_id)
-    : tree_client_(window_tree_client),
+    : window_tree_(window_tree),
+      tree_client_(window_tree_client),
       window_(window),
       transport_window_id_(transport_window_id) {}
 
@@ -71,8 +66,8 @@
   StartDrag(event);
 
   tree_client_->OnDragEnter(
-      transport_window_id_, event.flags(),
-      ToScreenLocation(window_, event.location()), event.source_operations(),
+      transport_window_id_, event.flags(), GetRootLocation(event),
+      event.location_f(), event.source_operations(),
       base::BindOnce(&DragDropDelegate::UpdateDragOperations,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -81,8 +76,8 @@
   DCHECK(in_drag_);
 
   tree_client_->OnDragOver(
-      transport_window_id_, event.flags(),
-      ToScreenLocation(window_, event.location()), event.source_operations(),
+      transport_window_id_, event.flags(), GetRootLocation(event),
+      event.location_f(), event.source_operations(),
       base::BindOnce(&DragDropDelegate::UpdateDragOperations,
                      weak_ptr_factory_.GetWeakPtr()));
   return last_drag_operations_;
@@ -99,7 +94,7 @@
   DCHECK(in_drag_);
 
   tree_client_->OnCompleteDrop(transport_window_id_, event.flags(),
-                               ToScreenLocation(window_, event.location()),
+                               GetRootLocation(event), event.location_f(),
                                event.source_operations(), base::DoNothing());
 
   EndDrag();
@@ -132,4 +127,10 @@
   last_drag_operations_ = drag_operations;
 }
 
+gfx::PointF DragDropDelegate::GetRootLocation(
+    const ui::DropTargetEvent& event) {
+  return window_tree_->ConvertRootLocationForClient(window_,
+                                                    event.root_location_f());
+}
+
 }  // namespace ws
diff --git a/services/ws/drag_drop_delegate.h b/services/ws/drag_drop_delegate.h
index dd76c53..e8af7b25 100644
--- a/services/ws/drag_drop_delegate.h
+++ b/services/ws/drag_drop_delegate.h
@@ -12,13 +12,20 @@
 #include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 
+namespace gfx {
+class PointF;
+}
+
 namespace ws {
 
+class WindowTree;
+
 // A delegate to forward drag and drop events to a remote client window via
 // mojom::WindowTreeClient.
 class DragDropDelegate : public aura::client::DragDropDelegate {
  public:
-  DragDropDelegate(mojom::WindowTreeClient* window_tree_client,
+  DragDropDelegate(WindowTree* window_tree,
+                   mojom::WindowTreeClient* window_tree_client,
                    aura::Window* window,
                    Id transport_window_id);
   ~DragDropDelegate() override;
@@ -36,6 +43,10 @@
   // Callback invoked to update |last_drag_operations_|.
   void UpdateDragOperations(uint32_t drag_operations);
 
+  // Returns the location to use as the root_location for the client.
+  gfx::PointF GetRootLocation(const ui::DropTargetEvent& event);
+
+  WindowTree* window_tree_;
   mojom::WindowTreeClient* const tree_client_;
   aura::Window* const window_;
   const Id transport_window_id_;
diff --git a/services/ws/public/mojom/window_manager.mojom b/services/ws/public/mojom/window_manager.mojom
index 79497540..d3420714 100644
--- a/services/ws/public/mojom/window_manager.mojom
+++ b/services/ws/public/mojom/window_manager.mojom
@@ -39,8 +39,9 @@
   const string kDisplayId_InitProperty = "init:display_id";
 
   // Specifies that the system default caption and icon should not be rendered,
-  // and the client area should be equivalent to the window area. Type: bool
-  const string kRemoveStandardFrame_InitProperty = "init:remove-standard-frame";
+  // and the client area should be equivalent to the window area. This is used
+  // for frameless Chrome app windows and all Chrome browser windows. Type: bool
+  const string kClientProvidesFrame_InitProperty = "init:client-provides-frame";
 
   // The window type. This maps to aura::client::kWindowTypeKey as well as
   // Window::type(). This mapping is only done for top-level windows that are
diff --git a/services/ws/public/mojom/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom
index 3b4891f8..bfefc94b 100644
--- a/services/ws/public/mojom/window_tree.mojom
+++ b/services/ws/public/mojom/window_tree.mojom
@@ -577,29 +577,34 @@
 
   // Called when the mouse cursor enters a window that has opted into accepting
   // drags through SetAcceptsDrags(), providing a list of available mime types.
-  // Returns a bitmask of the supported operations.
-  // |screen_position| is in screen coordinates.
+  // |location_in_root| is the location relative to the embed root of |window|.
+  // |location| is the location relative to |window|.
+  // If |window| is a top-level or embed root, then |location_in_root| is the
+  // same as |location|. Callback is supplied the supported operations.
   OnDragEnter(uint64 window,
               uint32 key_state,
-              gfx.mojom.Point screen_position,
+              gfx.mojom.PointF location_in_root,
+              gfx.mojom.PointF location,
               uint32 effect_bitmask) => (uint32 supported_op_bitmask);
 
   // Called when the pointer moves over the window after the initial DragEnter.
-  // Returns a bitmask of the supported operations at this location.
-  // |screen_position| is in screen coordinates.
+  // Returns a bitmask of the supported operations at this location. See
+  // OnDragEnter() for details on coordinates.
   OnDragOver(uint64 window,
              uint32 key_state,
-             gfx.mojom.Point screen_position,
+             gfx.mojom.PointF location_in_root,
+             gfx.mojom.PointF location,
              uint32 effect_bitmask) => (uint32 supported_op_bitmask);
 
   // Called when the pointer leaves a window or if the drop is canceled.
   OnDragLeave(uint64 window);
 
-  // Called when the drop occurs on a window. Returns the action taken.
-  // |screen_position| is in screen coordinates.
+  // Called when the drop occurs on a window. Returns the action taken. See
+  // OnDragEnter() for details on coordinates.
   OnCompleteDrop(uint64 window,
                  uint32 key_state,
-                 gfx.mojom.Point screen_position,
+                 gfx.mojom.PointF location_in_root,
+                 gfx.mojom.PointF location,
                  uint32 effect_bitmask) => (uint32 action_taken);
 
   // Called on the client that requested PerformDragDrop() to return which drag
diff --git a/services/ws/test_window_tree_client.cc b/services/ws/test_window_tree_client.cc
index 472d483..ea05f2b 100644
--- a/services/ws/test_window_tree_client.cc
+++ b/services/ws/test_window_tree_client.cc
@@ -233,7 +233,8 @@
 
 void TestWindowTreeClient::OnDragEnter(Id window,
                                        uint32_t key_state,
-                                       const gfx::Point& position,
+                                       const gfx::PointF& location_in_root,
+                                       const gfx::PointF& location,
                                        uint32_t effect_bitmask,
                                        OnDragEnterCallback callback) {
   tracker_.OnDragEnter(window);
@@ -241,7 +242,8 @@
 
 void TestWindowTreeClient::OnDragOver(Id window,
                                       uint32_t key_state,
-                                      const gfx::Point& position,
+                                      const gfx::PointF& location_in_root,
+                                      const gfx::PointF& location,
                                       uint32_t effect_bitmask,
                                       OnDragOverCallback callback) {
   tracker_.OnDragOver(window);
@@ -253,7 +255,8 @@
 
 void TestWindowTreeClient::OnCompleteDrop(Id window,
                                           uint32_t key_state,
-                                          const gfx::Point& position,
+                                          const gfx::PointF& location_in_root,
+                                          const gfx::PointF& location,
                                           uint32_t effect_bitmask,
                                           OnCompleteDropCallback callback) {
   tracker_.OnCompleteDrop(window);
diff --git a/services/ws/test_window_tree_client.h b/services/ws/test_window_tree_client.h
index d1a9092..b3b7647 100644
--- a/services/ws/test_window_tree_client.h
+++ b/services/ws/test_window_tree_client.h
@@ -149,18 +149,21 @@
                            drag_data) override;
   void OnDragEnter(Id window,
                    uint32_t key_state,
-                   const gfx::Point& position,
+                   const gfx::PointF& location_in_root,
+                   const gfx::PointF& location,
                    uint32_t effect_bitmask,
                    OnDragEnterCallback callback) override;
   void OnDragOver(Id window,
                   uint32_t key_state,
-                  const gfx::Point& position,
+                  const gfx::PointF& location_in_root,
+                  const gfx::PointF& location,
                   uint32_t effect_bitmask,
                   OnDragOverCallback callback) override;
   void OnDragLeave(Id window) override;
   void OnCompleteDrop(Id window,
                       uint32_t key_state,
-                      const gfx::Point& position,
+                      const gfx::PointF& location_in_root,
+                      const gfx::PointF& location,
                       uint32_t effect_bitmask,
                       OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
diff --git a/services/ws/window_service.cc b/services/ws/window_service.cc
index df7067e..4f7df7c7 100644
--- a/services/ws/window_service.cc
+++ b/services/ws/window_service.cc
@@ -167,15 +167,17 @@
   ProxyWindow* proxy_window = ProxyWindow::GetMayBeNull(window);
   if (!proxy_window)
     return kInvalidTransportId;
-  if (!proxy_window->owning_window_tree())
+  WindowTree* tree = proxy_window->owning_window_tree()
+                         ? proxy_window->owning_window_tree()
+                         : proxy_window->embedded_window_tree();
+  if (!tree)
     return kInvalidTransportId;
   // NOTE: WindowTree::TransportIdForWindow() is the id sent to the client,
   // which has the client_id portion set to 0. This function wants to see the
   // real client_id, so it has to build it.
   return BuildTransportId(
-      proxy_window->owning_window_tree()->client_id(),
-      ClientWindowIdFromTransportId(
-          proxy_window->owning_window_tree()->TransportIdForWindow(window)));
+      tree->client_id(),
+      ClientWindowIdFromTransportId(tree->TransportIdForWindow(window)));
 }
 
 WindowService::TreeAndWindowId
diff --git a/services/ws/window_service_test_setup.cc b/services/ws/window_service_test_setup.cc
index 9f8cb7c..ac6574bf 100644
--- a/services/ws/window_service_test_setup.cc
+++ b/services/ws/window_service_test_setup.cc
@@ -29,7 +29,7 @@
   ~TestFocusRules() override = default;
 
   // wm::BaseFocusRules:
-  bool SupportsChildActivation(aura::Window* window) const override {
+  bool SupportsChildActivation(const aura::Window* window) const override {
     return window == window->GetRootWindow();
   }
 
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index f79f10b..84d2f5c5 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -198,21 +198,15 @@
 
   std::unique_ptr<ui::Event> event_to_send = ui::Event::Clone(event);
   if (event.IsLocatedEvent()) {
+    ui::LocatedEvent* located_event = event_to_send->AsLocatedEvent();
     // Translate the root location for located events. Event's root location
     // should be in the coordinate of the root window, however the root for the
     // target window in the client can be different from the one in the server,
     // thus the root location needs to be converted from the original coordinate
     // to the one used in the client. See also 'WindowTreeTest.EventLocation'
     // test case.
-    ClientRoot* client_root = FindClientRootContaining(window);
-    // The |client_root| may have been removed on shutdown.
-    if (client_root) {
-      gfx::PointF root_location =
-          event_to_send->AsLocatedEvent()->root_location_f();
-      aura::Window::ConvertPointToTarget(window->GetRootWindow(),
-                                         client_root->window(), &root_location);
-      event_to_send->AsLocatedEvent()->set_root_location_f(root_location);
-    }
+    located_event->set_root_location_f(
+        ConvertRootLocationForClient(window, located_event->root_location_f()));
   }
   DVLOG(4) << "SendEventToClient window="
            << ProxyWindow::GetMayBeNull(window)->GetIdForDebugging()
@@ -339,6 +333,19 @@
   return iter == client_roots_.end() ? nullptr : iter->get();
 }
 
+gfx::PointF WindowTree::ConvertRootLocationForClient(
+    aura::Window* window,
+    const gfx::PointF& root_location) {
+  ClientRoot* client_root = FindClientRootContaining(window);
+  // The |client_root| may have been removed on shutdown.
+  if (!client_root)
+    return root_location;
+  gfx::PointF client_root_location = root_location;
+  aura::Window::ConvertPointToTarget(
+      window->GetRootWindow(), client_root->window(), &client_root_location);
+  return client_root_location;
+}
+
 ClientRoot* WindowTree::CreateClientRoot(aura::Window* window,
                                          bool is_top_level) {
   DCHECK(window);
@@ -1657,7 +1664,7 @@
   DCHECK(proxy_window);  // Must exist because of preceding conditionals.
   if (accepts_drops && !proxy_window->HasDragDropDelegate()) {
     auto drag_drop_delegate = std::make_unique<DragDropDelegate>(
-        window_tree_client_, window, window_id);
+        this, window_tree_client_, window, window_id);
     aura::client::SetDragDropDelegate(window, drag_drop_delegate.get());
     proxy_window->SetDragDropDelegate(std::move(drag_drop_delegate));
   } else if (!accepts_drops && proxy_window->HasDragDropDelegate()) {
diff --git a/services/ws/window_tree.h b/services/ws/window_tree.h
index 5a77f0ee..a908feb 100644
--- a/services/ws/window_tree.h
+++ b/services/ws/window_tree.h
@@ -27,6 +27,10 @@
 class Window;
 }
 
+namespace gfx {
+class PointF;
+}
+
 namespace ui {
 class Event;
 }
@@ -156,6 +160,12 @@
   // recurse.
   ClientRoot* GetClientRootForWindow(aura::Window* window);
 
+  // Converts an Event's root_location as supplied to the window service to be
+  // relative to the nearest client root of |window|. The returned value should
+  // used as the root_location() for Events supplied to clients.
+  gfx::PointF ConvertRootLocationForClient(aura::Window* window,
+                                           const gfx::PointF& root_location);
+
  private:
   friend class ClientRoot;
   // TODO(sky): WindowTree should be refactored such that it is not
diff --git a/services/ws/window_tree_client_unittest.cc b/services/ws/window_tree_client_unittest.cc
index b3b5bde..10d3673 100644
--- a/services/ws/window_tree_client_unittest.cc
+++ b/services/ws/window_tree_client_unittest.cc
@@ -399,18 +399,21 @@
 
   void OnDragEnter(Id window,
                    uint32_t key_state,
-                   const gfx::Point& position,
+                   const gfx::PointF& location_in_root,
+                   const gfx::PointF& location,
                    uint32_t effect_bitmask,
                    OnDragEnterCallback callback) override {}
   void OnDragOver(Id window,
                   uint32_t key_state,
-                  const gfx::Point& position,
+                  const gfx::PointF& location_in_root,
+                  const gfx::PointF& location,
                   uint32_t effect_bitmask,
                   OnDragOverCallback callback) override {}
   void OnDragLeave(Id window) override {}
   void OnCompleteDrop(Id window,
                       uint32_t key_state,
-                      const gfx::Point& position,
+                      const gfx::PointF& location_in_root,
+                      const gfx::PointF& location,
                       uint32_t effect_bitmask,
                       OnCompleteDropCallback callback) override {}
 
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index b0ae9cd..1f92908 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -138,16 +138,16 @@
 #   define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
 #endif
 
-#ifndef SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS
-#define SK_SUPPORT_LEGACY_PAINT_FONT_FIELDS
-#endif
-
 // Remove this after we fixed all the issues related to the new SDF algorithm
 // (https://codereview.chromium.org/1643143002)
 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
 #define SK_USE_LEGACY_DISTANCE_FIELDS
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_PAINT_FLAGS
+#define SK_SUPPORT_LEGACY_PAINT_FLAGS
+#endif
+
 // Skia is enabling this feature soon. Chrome probably does
 // not want it for M64
 #ifndef SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index 9021c1d..00b8ba78 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -205,7 +205,7 @@
     val->Set("Xfermode", AsValue(paint.getBlendMode()));
   }
 
-  if (paint.getFlags()) {
+  if (paint.isAntiAlias() || paint.isDither()) {
     FlagsBuilder builder('|');
     builder.addFlag(paint.isAntiAlias(), "AntiAlias");
     builder.addFlag(paint.isDither(), "Dither");
diff --git a/storage/browser/blob/README.md b/storage/browser/blob/README.md
index a9b8cb5..9ed3fa8 100644
--- a/storage/browser/blob/README.md
+++ b/storage/browser/blob/README.md
@@ -71,7 +71,7 @@
 Reply, Data Pipe, and Files.
 * **blob description**: the inital data sychronously sent to the browser that
 describes the items (content and sizes) of the new blob. This can
-optimistically include the blob data if the size is less than the maximimum mojo
+optimistically include the blob data if the size is less than the maximum mojo
 message size.
 
 # Blob Storage Limits
@@ -152,7 +152,7 @@
 `DataElement`s, while the `RegisterFromStream` method creates a blob by reading
 data from a mojo `DataPipe`. Furthermore `Register` will call its callback as
 soon as possible after the request has been received, at which point the uuid is
-valid and known to the blob sytem. It will then asynchronously request the data
+valid and known to the blob system. It will then asynchronously request the data
 and actually create the blob. On the other hand the `RegisterFromStream` method
 won't call its callback until all the data for the blob has been received and
 the blob has been entirely completed.
diff --git a/storage/browser/blob/blob_data_builder.cc b/storage/browser/blob/blob_data_builder.cc
index 59a06d0..61609cb 100644
--- a/storage/browser/blob/blob_data_builder.cc
+++ b/storage/browser/blob/blob_data_builder.cc
@@ -18,7 +18,6 @@
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "net/disk_cache/disk_cache.h"
-#include "services/network/public/cpp/data_element.h"
 #include "storage/browser/blob/blob_entry.h"
 #include "storage/browser/blob/blob_storage_registry.h"
 #include "storage/browser/blob/shareable_blob_data_item.h"
@@ -101,36 +100,6 @@
 BlobDataBuilder::BlobDataBuilder(const std::string& uuid) : uuid_(uuid) {}
 BlobDataBuilder::~BlobDataBuilder() = default;
 
-void BlobDataBuilder::AppendIPCDataElement(
-    const network::DataElement& ipc_data,
-    const BlobStorageRegistry& blob_registry) {
-  uint64_t length = ipc_data.length();
-  switch (ipc_data.type()) {
-    case network::DataElement::TYPE_BYTES:
-      DCHECK(!ipc_data.offset());
-      AppendData(ipc_data.bytes(),
-                 base::checked_cast<size_t>(length));
-      break;
-    case network::DataElement::TYPE_FILE:
-      AppendFile(ipc_data.path(), ipc_data.offset(), length,
-                 ipc_data.expected_modification_time());
-      break;
-    case network::DataElement::TYPE_BLOB:
-      // This will be deconstructed immediately into the items the blob is made
-      // up of.
-      AppendBlob(ipc_data.blob_uuid(), ipc_data.offset(), ipc_data.length(),
-                 blob_registry);
-      break;
-    case network::DataElement::TYPE_RAW_FILE:
-    case network::DataElement::TYPE_UNKNOWN:
-    // This type can't be sent by IPC.
-    case network::DataElement::TYPE_DATA_PIPE:
-    case network::DataElement::TYPE_CHUNKED_DATA_PIPE:
-      NOTREACHED();
-      break;
-  }
-}
-
 void BlobDataBuilder::AppendData(const char* data, size_t length) {
   if (!length)
     return;
diff --git a/storage/browser/blob/blob_data_builder.h b/storage/browser/blob/blob_data_builder.h
index af3efee5..1c4b8388 100644
--- a/storage/browser/blob/blob_data_builder.h
+++ b/storage/browser/blob/blob_data_builder.h
@@ -27,10 +27,6 @@
 class Entry;
 }
 
-namespace network {
-class DataElement;
-}
-
 namespace storage {
 class BlobSliceTest;
 class BlobStorageContext;
@@ -51,15 +47,6 @@
 
   const std::string& uuid() const { return uuid_; }
 
-  // Validates the data element that was sent over IPC, and copies the data if
-  // it's a 'bytes' element. Data elements of BYTES_DESCRIPTION or
-  // DISK_CACHE_ENTRY types are not valid IPC data element types, and cannot be
-  // given to this method.
-  // |blob_registry| is needed for data elements of type BLOB.
-  void AppendIPCDataElement(
-      const network::DataElement& ipc_data,
-      const BlobStorageRegistry& blob_registry);
-
   // Copies the given data into the blob.
   void AppendData(const std::string& data) {
     AppendData(data.c_str(), data.size());
diff --git a/storage/browser/fileapi/file_writer_impl.cc b/storage/browser/fileapi/file_writer_impl.cc
index 19c2421..c93f58d2 100644
--- a/storage/browser/fileapi/file_writer_impl.cc
+++ b/storage/browser/fileapi/file_writer_impl.cc
@@ -17,7 +17,8 @@
     base::WeakPtr<BlobStorageContext> blob_context)
     : operation_runner_(std::move(operation_runner)),
       blob_context_(std::move(blob_context)),
-      url_(std::move(url)) {
+      url_(std::move(url)),
+      weak_ptr_factory_(this) {
   DCHECK(url_.is_valid());
 }
 
@@ -28,7 +29,7 @@
                            WriteCallback callback) {
   blob_context_->GetBlobDataFromBlobPtr(
       std::move(blob),
-      base::BindOnce(&FileWriterImpl::DoWrite, base::Unretained(this),
+      base::BindOnce(&FileWriterImpl::DoWrite, weak_ptr_factory_.GetWeakPtr(),
                      std::move(callback), position));
 }
 
diff --git a/storage/browser/fileapi/file_writer_impl.h b/storage/browser/fileapi/file_writer_impl.h
index c5917ca..b77ac7b1 100644
--- a/storage/browser/fileapi/file_writer_impl.h
+++ b/storage/browser/fileapi/file_writer_impl.h
@@ -6,6 +6,7 @@
 #define STORAGE_BROWSER_FILEAPI_FILE_WRITER_IMPL_H_
 
 #include "base/component_export.h"
+#include "base/memory/weak_ptr.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "third_party/blink/public/mojom/filesystem/file_writer.mojom.h"
@@ -60,6 +61,8 @@
   const std::unique_ptr<FileSystemOperationRunner> operation_runner_;
   const base::WeakPtr<BlobStorageContext> blob_context_;
   const FileSystemURL url_;
+
+  base::WeakPtrFactory<FileWriterImpl> weak_ptr_factory_;
 };
 
 }  // namespace storage
diff --git a/styleguide/web/es.md b/styleguide/web/es.md
index b0dcd23a..d4db948 100644
--- a/styleguide/web/es.md
+++ b/styleguide/web/es.md
@@ -511,6 +511,57 @@
 
 ---
 
+### Destructuring Assignment
+
+Flexible destructuring of collections or parameters.
+
+**Usage Example:**
+
+```js
+// Array
+const [a, , b] = [1, 2, 3];  // a = 1, b = 3
+
+// Object
+const {width, height} = document.body.getBoundingClientRect();
+// width = rect.width, height = rect.height
+
+// Parameters
+function f([name, val]) {
+  console.log(name, val);  // 'bar', 42
+}
+f(['bar', 42, 'extra 1', 'extra 2']);  // 'extra 1' and 'extra 2' are ignored.
+
+function g({name: n, val: v}) {
+  console.log(n, v);  // 'foo', 7
+}
+g({name: 'foo', val:  7});
+
+function h({name, val}) {
+  console.log(name, val);  // 'bar', 42
+}
+h({name: 'bar', val: 42});
+
+```
+**Mixing with [Rest Parameters](#rest-parameters)**
+
+Using rest parameters while destructuring objects is not supported by iOS 10 and requires setting the closure arg `language_in` to `ECMASCRIPT_2018`.
+
+```js
+const {one, ...rest} = {one: 1, two: 2, three: 3};
+```
+
+Using rest parameters while destructuring arrays, on the other hand, is supported by iOS 10 and `ECMASCRIPT_2017`.
+
+```js
+const [one, ...rest] = [1, 2, 3];
+```
+
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-assignment)
+
+**Discussion Notes / Link to Thread:** [link](https://groups.google.com/a/chromium.org/d/topic/chromium-dev/mwFnj7MTzgU)
+
+---
+
 ## Banned Features
 
 The following features are banned for Chromium development.
@@ -680,44 +731,6 @@
 
 ---
 
-### Destructuring Assignment
-
-Flexible destructuring of collections or parameters.
-
-**Usage Example:**
-
-```js
-// Array
-const [a, , b] = [1, 2, 3];  // a = 1, b = 3
-
-// Object
-const {width, height} = document.body.getBoundingClientRect();
-// width = rect.width, height = rect.height
-
-// Parameters
-function f([name, val]) {
-  console.log(name, val);  // 'bar', 42
-}
-f(['bar', 42, 'extra 1', 'extra 2']);  // 'extra 1' and 'extra 2' are ignored.
-
-function g({name: n, val: v}) {
-  console.log(n, v);  // 'foo', 7
-}
-g({name: 'foo', val:  7});
-
-function h({name, val}) {
-  console.log(name, val);  // 'bar', 42
-}
-h({name: 'bar', val: 42});
-
-```
-
-**Documentation:** [link](https://tc39.github.io/ecma262/#sec-destructuring-assignment)
-
-**Discussion Notes / Link to Thread:**
-
----
-
 ### Modules
 
 Support for exporting/importing values from/to modules without global
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 3c5319e..ab7b204 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -23737,7 +23737,7 @@
               "temp_band": "<30"
             }
           ],
-          "hard_timeout": 960,
+          "hard_timeout": 1800,
           "idempotent": false,
           "shards": 15
         }
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 4bf233b..9a05dae 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -3926,6 +3926,12 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
+        "test": "castrunner_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
         "test": "castrunner_integration_tests"
       },
       {
@@ -4075,6 +4081,17 @@
             }
           ]
         },
+        "test": "castrunner_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1"
+            }
+          ]
+        },
         "test": "castrunner_integration_tests"
       },
       {
@@ -4314,6 +4331,17 @@
             }
           ]
         },
+        "test": "castrunner_browsertests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1"
+            }
+          ]
+        },
         "test": "castrunner_integration_tests"
       },
       {
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
index f6a55dc..5beb44da 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
@@ -33,9 +33,6 @@
 -SwitchAccessAutoScanManagerTest.RestartIfRunningWhenOff
 -SwitchAccessAutoScanManagerTest.EnableAndDisable
 -SwitchAccessAutoScanManagerTest.SetScanTime
--SwitchAccessContextMenuManagerTest.EnterMenu
--SwitchAccessContextMenuManagerTest.NavigationInMenu
--SwitchAccessContextMenuManagerTest.ExitMenu
 -SwitchAccessNavigationManagerTest.MoveForward
 -SwitchAccessNavigationManagerTest.MoveBackward
 -SwitchAccessNavigationManagerTest.MoveBackAndForth
@@ -334,10 +331,6 @@
 # Use of Shell. https://crbug.com/899869
 -AutotestPrivateApiTest.AutotestPrivate
 
-# Pinning by setting the window property doesn't work in Mash.
-# https://crbug.com/912191
--ImmersiveModeBrowserViewTest.LockedFullscreenDisablesImmersive/*
-
 # https://crbug.com/918876
 -SwitchAccessPredicateTest.IsActionableFocusableElements
 
diff --git a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
index 72324fe..f4a098e 100644
--- a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
@@ -9,9 +9,6 @@
 # Need to convert MergeSessionResourceThrottle to a URLLoaderThrottle.
 -MergeSessionTest.XHRThrottle
 
-# Flaky with error: `Check failed: (sequence_checker_).CalledOnValidSequence()`.
--DevToolsSanityTest.DisposeEmptyBrowserContext
-
 # Crashing on Mash. https://crbug.com/919108
 -ChromeBrowserMainBrowserTest.VariationsServiceStartsRequestOnNetworkChange
 
diff --git a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
index caa8233..509a543 100644
--- a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
@@ -27,9 +27,6 @@
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsTransferredToEmbedded
 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsTransferredToFullscreen
 
-# https://crbug.com/893564
--org.chromium.android_webview.test.AwContentsClientGetDefaultVideoPosterTest.testGetDefaultVideoPoster
-
 # https://crbug.com/893566
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testBaseUrlOfLoadDataSentInRefererHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledForExistingFiles
@@ -37,10 +34,7 @@
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledWithCorrectRefererHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCanInterceptMainFrame
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testContentIdIframe
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testContentIdImage
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testDoesNotCrashOnEmptyStream
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testDoesNotCrashOnSlowStream
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testDoesNotCrashOnThrowingStream
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testHttpResponseClientViaHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testHttpResponseHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testHttpStatusCodeAndText
@@ -51,10 +45,7 @@
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNotCalledForExistingContentUrl
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNotCalledForExistingResource
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNotCalledForHttpRedirect
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNullInputStreamCausesErrorForMainFrame
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNullHttpResponseHeaders
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testDoesNotChangeReportedUrl
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNoOnReceivedErrorCallback
 
 # https://crbug.com/893568
 -org.chromium.android_webview.test.AwContentsTest.testCanInjectHeaders
@@ -72,11 +63,6 @@
 -org.chromium.android_webview.test.AwSettingsTest.testCacheModeWithBlockedNetworkLoads
 -org.chromium.android_webview.test.AwSettingsTest.testContentUrlAccessWithTwoViews
 -org.chromium.android_webview.test.AwSettingsTest.testContentUrlFromFileWithTwoViews
--org.chromium.android_webview.test.AwSettingsTest.testDatabaseDisabled
--org.chromium.android_webview.test.AwSettingsTest.testDatabaseEnabled
--org.chromium.android_webview.test.AwSettingsTest.testDatabaseInitialValue
--org.chromium.android_webview.test.AwSettingsTest.testDefaultVideoPosterURL
--org.chromium.android_webview.test.AwSettingsTest.testDomStorageEnabledWithTwoViews
 -org.chromium.android_webview.test.AwSettingsTest.testFileAccessFromFilesIframeWithTwoViews
 -org.chromium.android_webview.test.AwSettingsTest.testFileAccessFromFilesImage
 -org.chromium.android_webview.test.AwSettingsTest.testFileAccessFromFilesXhrWithTwoViews
@@ -84,7 +70,6 @@
 -org.chromium.android_webview.test.AwSettingsTest.testFileUrlAccessToggleDoesNotBlockResourceUrls
 -org.chromium.android_webview.test.AwSettingsTest.testFileUrlAccessWithTwoViews
 -org.chromium.android_webview.test.AwSettingsTest.testResourceUrl
--org.chromium.android_webview.test.AwSettingsTest.testUniversalAccessFromFilesWithTwoViews
 
 # https://crbug.com/893572
 -org.chromium.android_webview.test.ClientOnPageStartedTest.testBrowserInitiatedRedirectHangingNavigation
@@ -118,12 +103,6 @@
 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testXhrForCustomSchemeUrl
 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testXhrForHttpSchemeUrl
 
-# https://crbug.com/893581
--org.chromium.android_webview.test.LoadUrlTest.testLoadUrlWithExtraHeaders
--org.chromium.android_webview.test.LoadUrlTest.testRedirectAndReloadWithExtraHeaders
--org.chromium.android_webview.test.LoadUrlTest.testReloadWithExtraHeaders
--org.chromium.android_webview.test.LoadUrlTest.testRendererNavigationAndGoBackWithExtraHeaders
-
 # https://crbug.com/893582
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame
 -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingHardcodedMalwareUrl
@@ -140,3 +119,11 @@
 # https://crbug.com/893585
 -org.chromium.android_webview.test.WebViewWebVrTest.testWebVrNotFunctional
 
+
+# https://crbug.com/902658
+-org.chromium.android_webview.test.AwProxyControllerTest.testProxyOverride
+-org.chromium.android_webview.test.AwProxyControllerTest.testProxyOverrideLocalhost
+-org.chromium.android_webview.test.AwProxyControllerTest.testCallbacks
+-org.chromium.android_webview.test.AwProxyControllerTest.testValidInput
+-org.chromium.android_webview.test.AwProxyControllerTest.testInvalidProxyUrls
+-org.chromium.android_webview.test.AwProxyControllerTest.testInvalidBypassRules
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index f6a0572b3..596ced9 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -411,6 +411,10 @@
     "label": "//media/capture:capture_unittests",
     "type": "windowed_test_launcher",
   },
+  "castrunner_browsertests": {
+    "label": "//webrunner:castrunner_browsertests",
+    "type": "console_test_launcher",
+  },
   "castrunner_integration_tests": {
     "label": "//webrunner:castrunner_integration_tests",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 1c273bc..0dea8e1 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -259,6 +259,14 @@
       },
     },
   },
+  'castrunner_browsertests': {
+    # TODO(crbug.com/910799): Enable this once we're confident that it
+    # passes.
+    'remove_from': [
+      # chromium.linux
+      'Fuchsia x64',
+    ],
+  },
   'castrunner_integration_tests': {
     'remove_from': [
       # chromium.linux
@@ -1099,6 +1107,7 @@
               'temp_band': '<30',
             },
           ],
+          'hard_timeout': 1800,
           'shards': 15,
         },
       },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2ac8668..7b024aa 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2718,6 +2718,7 @@
 
     'fuchsia_gtests': {
       'base_unittests': {},
+      'castrunner_browsertests': {},
       'castrunner_integration_tests': {},
       'castrunner_unittests': {},
       'content_unittests': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 3d2a8782..6b8ba6a1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -849,6 +849,7 @@
     "AutofillUpstreamEditableCardholderName": [
         {
             "platforms": [
+                "android",
                 "chromeos",
                 "linux",
                 "mac",
@@ -1306,21 +1307,6 @@
             ]
         }
     ],
-    "Crostini": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Crostini",
-                    "enable_features": [
-                        "Crostini"
-                    ]
-                }
-            ]
-        }
-    ],
     "DataCompressionProxyLoFi": [
         {
             "platforms": [
@@ -4635,27 +4621,6 @@
             ]
         }
     ],
-    "TranslateUserEvents": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SyncUserLanguageDetectionEvents",
-                        "SyncUserTranslationEvents"
-                    ]
-                }
-            ]
-        }
-    ],
     "TriggeredResetFieldTrial": [
         {
             "platforms": [
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 7b200a8a..99748c1f 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -536,7 +536,7 @@
 }
 
 android_aar_prebuilt("com_google_ar_core_java") {
-  aar_path = "libs/com_google_ar_core/core-1.5.0.aar"
+  aar_path = "libs/com_google_ar_core/core-1.6.0.aar"
   info_path = "libs/com_google_ar_core/com_google_ar_core.info"
   extract_native_libraries = true
 }
diff --git a/third_party/android_deps/libs/com_google_ar_core/README.chromium b/third_party/android_deps/libs/com_google_ar_core/README.chromium
index c71710d..19c09d0 100644
--- a/third_party/android_deps/libs/com_google_ar_core/README.chromium
+++ b/third_party/android_deps/libs/com_google_ar_core/README.chromium
@@ -1,7 +1,7 @@
 Name: 
 Short Name: core
 URL: https://github.com/google-ar/arcore-android-sdk
-Version: 1.5.0
+Version: 1.6.0
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/android_deps/libs/com_google_ar_core/cipd.yaml b/third_party/android_deps/libs/com_google_ar_core/cipd.yaml
index 754918bc..2a888d1 100644
--- a/third_party/android_deps/libs/com_google_ar_core/cipd.yaml
+++ b/third_party/android_deps/libs/com_google_ar_core/cipd.yaml
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:1.5.0-cr0
+# cipd create --pkg-def cipd.yaml -tag version:1.6.0-cr0
 package: chromium/third_party/android_deps/libs/com_google_ar_core
 description: ""
 data:
-- file: core-1.5.0.aar
+- file: core-1.6.0.aar
diff --git a/third_party/android_deps/libs/com_google_ar_core/com_google_ar_core.info b/third_party/android_deps/libs/com_google_ar_core/com_google_ar_core.info
index 3f5e833..c5073f5 100644
--- a/third_party/android_deps/libs/com_google_ar_core/com_google_ar_core.info
+++ b/third_party/android_deps/libs/com_google_ar_core/com_google_ar_core.info
@@ -9,6 +9,6 @@
 has_r_text_file = true
 is_manifest_empty = false
 native_libraries = [ "jni/arm64-v8a/libarcore_sdk_c.so", "jni/arm64-v8a/libarcore_sdk_jni.so", "jni/armeabi-v7a/libarcore_sdk_c.so", "jni/armeabi-v7a/libarcore_sdk_jni.so", "jni/x86/libarcore_sdk_c.so", "jni/x86/libarcore_sdk_jni.so" ]
-resources = [ "res/layout/__arcore_education.xml", "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar-rEG/values.xml", "res/values-ar-rSA/values.xml", "res/values-ar-rXB/values.xml", "res/values-az/values.xml", "res/values-b+es+419/values.xml", "res/values-b+sr+Latn/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de-rAT/values.xml", "res/values-de-rCH/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rAU/values.xml", "res/values-en-rCA/values.xml", "res/values-en-rGB/values.xml", "res/values-en-rIE/values.xml", "res/values-en-rSG/values.xml", "res/values-en-rXA/values.xml", "res/values-en-rXC/values.xml", "res/values-en-rZA/values.xml", "res/values-es-rAR/values.xml", "res/values-es-rBO/values.xml", "res/values-es-rCL/values.xml", "res/values-es-rCO/values.xml", "res/values-es-rCR/values.xml", "res/values-es-rDO/values.xml", "res/values-es-rEC/values.xml", "res/values-es-rGT/values.xml", "res/values-es-rHN/values.xml", "res/values-es-rMX/values.xml", "res/values-es-rNI/values.xml", "res/values-es-rPA/values.xml", "res/values-es-rPE/values.xml", "res/values-es-rPR/values.xml", "res/values-es-rPY/values.xml", "res/values-es-rSV/values.xml", "res/values-es-rUS/values.xml", "res/values-es-rUY/values.xml", "res/values-es-rVE/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fil/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr-rCH/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gsw/values.xml", "res/values-gu/values.xml", "res/values-he/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-id/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mo/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-no/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-pt/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zh/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ]
+resources = [ "res/layout/__arcore_education.xml", "res/raw/keep.xml", "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar-rEG/values.xml", "res/values-ar-rSA/values.xml", "res/values-ar-rXB/values.xml", "res/values-az/values.xml", "res/values-b+es+419/values.xml", "res/values-b+sr+Latn/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de-rAT/values.xml", "res/values-de-rCH/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rAU/values.xml", "res/values-en-rCA/values.xml", "res/values-en-rGB/values.xml", "res/values-en-rIE/values.xml", "res/values-en-rSG/values.xml", "res/values-en-rXA/values.xml", "res/values-en-rXC/values.xml", "res/values-en-rZA/values.xml", "res/values-es-rAR/values.xml", "res/values-es-rBO/values.xml", "res/values-es-rCL/values.xml", "res/values-es-rCO/values.xml", "res/values-es-rCR/values.xml", "res/values-es-rDO/values.xml", "res/values-es-rEC/values.xml", "res/values-es-rGT/values.xml", "res/values-es-rHN/values.xml", "res/values-es-rMX/values.xml", "res/values-es-rNI/values.xml", "res/values-es-rPA/values.xml", "res/values-es-rPE/values.xml", "res/values-es-rPR/values.xml", "res/values-es-rPY/values.xml", "res/values-es-rSV/values.xml", "res/values-es-rUS/values.xml", "res/values-es-rUY/values.xml", "res/values-es-rVE/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fil/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr-rCH/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gsw/values.xml", "res/values-gu/values.xml", "res/values-he/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-id/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mo/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-no/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-pt/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zh/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ]
 subjar_tuples = [  ]
 subjars = [  ]
diff --git a/third_party/android_deps/libs/com_squareup_javapoet/README.chromium b/third_party/android_deps/libs/com_squareup_javapoet/README.chromium
index efcc7ffe..95697e8b 100644
--- a/third_party/android_deps/libs/com_squareup_javapoet/README.chromium
+++ b/third_party/android_deps/libs/com_squareup_javapoet/README.chromium
@@ -3,8 +3,8 @@
 URL: http://github.com/square/javapoet/
 Version: 1.11.0
 License: Apache 2.0
-License File: LICENSE
-Security Critical: yes
+License File: NOT_SHIPPED
+Security Critical: no
 
 Description:
 Use beautiful Java code to generate beautiful Java code.
diff --git a/third_party/blink/API_OWNERS b/third_party/blink/API_OWNERS
index 6d9b329..30b7d7d0 100644
--- a/third_party/blink/API_OWNERS
+++ b/third_party/blink/API_OWNERS
@@ -12,4 +12,4 @@
 rbyers@chromium.org
 slightlyoff@chromium.org
 tkent@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
diff --git a/third_party/blink/OWNERS b/third_party/blink/OWNERS
index f68c8219..5b809e1 100644
--- a/third_party/blink/OWNERS
+++ b/third_party/blink/OWNERS
@@ -8,7 +8,7 @@
 mkwst@chromium.org
 rbyers@chromium.org
 tkent@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
 
 # For *.gn* changes only.
 dpranke@chromium.org
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 78dc912..7cc97b3 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -42,6 +42,8 @@
     "loader/url_loader_factory_bundle_mojom_traits.cc",
     "manifest/manifest.cc",
     "manifest/manifest_icon_selector.cc",
+    "mediastream/media_devices.cc",
+    "mediastream/media_devices_mojom_traits.cc",
     "mediastream/media_stream_controls.cc",
     "mediastream/media_stream_mojom_traits.cc",
     "mediastream/media_stream_request.cc",
@@ -121,6 +123,7 @@
     "frame/user_activation_state_unittest.cc",
     "indexeddb/indexeddb_key_unittest.cc",
     "manifest/manifest_icon_selector_unittest.cc",
+    "mediastream/media_devices_unittest.cc",
     "mime_util/mime_util_unittest.cc",
     "notifications/notification_struct_traits_unittest.cc",
     "origin_policy/origin_policy_unittest.cc",
diff --git a/third_party/blink/common/mediastream/media_devices.cc b/third_party/blink/common/mediastream/media_devices.cc
new file mode 100644
index 0000000..3661b4b
--- /dev/null
+++ b/third_party/blink/common/mediastream/media_devices.cc
@@ -0,0 +1,50 @@
+// 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.
+
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "media/capture/video/video_capture_device_descriptor.h"
+
+namespace blink {
+
+WebMediaDeviceInfo::WebMediaDeviceInfo()
+    : video_facing(media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {}
+
+WebMediaDeviceInfo::WebMediaDeviceInfo(const WebMediaDeviceInfo& other) =
+    default;
+
+WebMediaDeviceInfo::WebMediaDeviceInfo(WebMediaDeviceInfo&& other) = default;
+
+WebMediaDeviceInfo::WebMediaDeviceInfo(const std::string& device_id,
+                                       const std::string& label,
+                                       const std::string& group_id,
+                                       media::VideoFacingMode video_facing)
+    : device_id(device_id),
+      label(label),
+      group_id(group_id),
+      video_facing(video_facing) {}
+
+WebMediaDeviceInfo::WebMediaDeviceInfo(
+    const media::VideoCaptureDeviceDescriptor& descriptor)
+    : device_id(descriptor.device_id),
+      label(descriptor.GetNameAndModel()),
+      video_facing(descriptor.facing) {}
+
+WebMediaDeviceInfo::~WebMediaDeviceInfo() = default;
+
+WebMediaDeviceInfo& WebMediaDeviceInfo::operator=(
+    const WebMediaDeviceInfo& other) = default;
+
+WebMediaDeviceInfo& WebMediaDeviceInfo::operator=(WebMediaDeviceInfo&& other) =
+    default;
+
+bool operator==(const WebMediaDeviceInfo& first,
+                const WebMediaDeviceInfo& second) {
+  // Do not use the |group_id| and |video_facing| fields for equality comparison
+  // since they are currently not fully supported by the video-capture layer.
+  // The modification of those fields by heuristics in upper layers does not
+  // result in a different device.
+  return first.device_id == second.device_id && first.label == second.label;
+}
+
+}  // namespace blink
diff --git a/content/common/media/media_devices_mojom_traits.cc b/third_party/blink/common/mediastream/media_devices_mojom_traits.cc
similarity index 71%
rename from content/common/media/media_devices_mojom_traits.cc
rename to third_party/blink/common/mediastream/media_devices_mojom_traits.cc
index ff6b20531..494c0307 100644
--- a/content/common/media/media_devices_mojom_traits.cc
+++ b/third_party/blink/common/mediastream/media_devices_mojom_traits.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 "content/common/media/media_devices_mojom_traits.h"
+#include "third_party/blink/public/common/mediastream/media_devices_mojom_traits.h"
 
 #include "base/logging.h"
 
@@ -10,14 +10,14 @@
 
 // static
 blink::mojom::MediaDeviceType
-EnumTraits<blink::mojom::MediaDeviceType, content::MediaDeviceType>::ToMojom(
-    content::MediaDeviceType type) {
+EnumTraits<blink::mojom::MediaDeviceType, blink::MediaDeviceType>::ToMojom(
+    blink::MediaDeviceType type) {
   switch (type) {
-    case content::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
+    case blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
       return blink::mojom::MediaDeviceType::MEDIA_AUDIO_INPUT;
-    case content::MediaDeviceType::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
+    case blink::MediaDeviceType::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       return blink::mojom::MediaDeviceType::MEDIA_VIDEO_INPUT;
-    case content::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
+    case blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
       return blink::mojom::MediaDeviceType::MEDIA_AUDIO_OUTPUT;
     default:
       break;
@@ -27,18 +27,18 @@
 }
 
 // static
-bool EnumTraits<blink::mojom::MediaDeviceType, content::MediaDeviceType>::
+bool EnumTraits<blink::mojom::MediaDeviceType, blink::MediaDeviceType>::
     FromMojom(blink::mojom::MediaDeviceType input,
-              content::MediaDeviceType* out) {
+              blink::MediaDeviceType* out) {
   switch (input) {
     case blink::mojom::MediaDeviceType::MEDIA_AUDIO_INPUT:
-      *out = content::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
+      *out = blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
       return true;
     case blink::mojom::MediaDeviceType::MEDIA_VIDEO_INPUT:
-      *out = content::MediaDeviceType::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
+      *out = blink::MediaDeviceType::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
       return true;
     case blink::mojom::MediaDeviceType::MEDIA_AUDIO_OUTPUT:
-      *out = content::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
+      *out = blink::MediaDeviceType::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
       return true;
     default:
       break;
@@ -87,10 +87,10 @@
 }
 
 // static
-bool StructTraits<
-    blink::mojom::MediaDeviceInfoDataView,
-    content::MediaDeviceInfo>::Read(blink::mojom::MediaDeviceInfoDataView input,
-                                    content::MediaDeviceInfo* out) {
+bool StructTraits<blink::mojom::MediaDeviceInfoDataView,
+                  blink::WebMediaDeviceInfo>::
+    Read(blink::mojom::MediaDeviceInfoDataView input,
+         blink::WebMediaDeviceInfo* out) {
   if (!input.ReadDeviceId(&out->device_id))
     return false;
   if (!input.ReadLabel(&out->label))
diff --git a/third_party/blink/common/mediastream/media_devices_unittest.cc b/third_party/blink/common/mediastream/media_devices_unittest.cc
new file mode 100644
index 0000000..143ca9d4
--- /dev/null
+++ b/third_party/blink/common/mediastream/media_devices_unittest.cc
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "media/audio/audio_device_description.h"
+#include "media/capture/video/video_capture_device_descriptor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(MediaDevicesTest, MediaDeviceInfoFromVideoDescriptor) {
+  media::VideoCaptureDeviceDescriptor descriptor(
+      "display_name", "device_id", "model_id", media::VideoCaptureApi::UNKNOWN);
+
+  // TODO(guidou): Add test for group ID when supported. See crbug.com/627793.
+  WebMediaDeviceInfo device_info(descriptor);
+  EXPECT_EQ(descriptor.device_id, device_info.device_id);
+  EXPECT_EQ(descriptor.GetNameAndModel(), device_info.label);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/common/mediastream/media_stream_request.cc b/third_party/blink/common/mediastream/media_stream_request.cc
index 9bf086a..5583e49 100644
--- a/third_party/blink/common/mediastream/media_stream_request.cc
+++ b/third_party/blink/common/mediastream/media_stream_request.cc
@@ -26,9 +26,18 @@
   return IsDesktopCaptureMediaType(type) || IsTabCaptureMediaType(type);
 }
 
+bool IsVideoScreenCaptureMediaType(MediaStreamType type) {
+  return IsVideoDesktopCaptureMediaType(type) ||
+         type == MEDIA_GUM_TAB_VIDEO_CAPTURE;
+}
+
 bool IsDesktopCaptureMediaType(MediaStreamType type) {
+  return (type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
+          IsVideoDesktopCaptureMediaType(type));
+}
+
+bool IsVideoDesktopCaptureMediaType(MediaStreamType type) {
   return (type == MEDIA_DISPLAY_VIDEO_CAPTURE ||
-          type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
           type == MEDIA_GUM_DESKTOP_VIDEO_CAPTURE);
 }
 
diff --git a/third_party/blink/perf_tests/accessibility/line-breaks.html b/third_party/blink/perf_tests/accessibility/line-breaks.html
new file mode 100644
index 0000000..f8d3adf
--- /dev/null
+++ b/third_party/blink/perf_tests/accessibility/line-breaks.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/runner.js"></script>
+
+<p id="testElement" spellcheck=false style="height:90vh"></p>
+
+<script>
+var isDone = false;
+var startTime;
+
+function randomString() {
+    return '' + Math.floor(1000000 * Math.random());
+}
+
+function runTest() {
+    if (startTime) {
+        PerfTestRunner.measureValueAsync(PerfTestRunner.now() - startTime);
+        PerfTestRunner.addRunTestEndMarker();
+    }
+    if (!isDone) {
+        PerfTestRunner.addRunTestStartMarker();
+        startTime = PerfTestRunner.now();
+
+        // Fill the paragraph with 1000 lines of text with <br> elements
+        // in-between.
+        var testElement = document.getElementById('testElement');
+        var html = '';
+        for (var i = 0; i < 1000; i++) {
+            html += randomString() + ' ' + randomString() + '<br>';
+        }
+        testElement.innerHTML = html;
+
+        // Wait to allow the asynchronous accessibility code that's
+        // covered by traceEventsToMeasure to have a chance to run.
+        setTimeout(runTest, 1500);
+    }
+}
+
+PerfTestRunner.startMeasureValuesAsync({
+    description: 'Test accessibility performance of many line breaks in one paragraph.',
+    unit: 'ms',
+    done: function () {
+        isDone = true;
+    },
+    run: function() {
+        runTest();
+    },
+    iterationCount: 6,
+    tracingCategories: 'accessibility',
+    traceEventsToMeasure: [
+        'RenderAccessibilityImpl::SendPendingAccessibilityEvents',
+    ]
+});
+</script>
+
+</html>
diff --git a/third_party/blink/perf_tests/layout/fit-content-change-available-size-blocks.html b/third_party/blink/perf_tests/layout/fit-content-change-available-size-blocks.html
new file mode 100644
index 0000000..878ba3d
--- /dev/null
+++ b/third_party/blink/perf_tests/layout/fit-content-change-available-size-blocks.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<div id="container" style="width:500px;">
+  <div id="stf" style="width:fit-content;"></div>
+</div>
+<script src="../resources/runner.js"></script>
+<script>
+  var stf = document.getElementById("stf");
+
+  for (var i = 0; i < 10000; i++) {
+      var child = document.createElement("div");
+      child.style.width = "100px";
+      child.style.height = "1px";
+      stf.appendChild(child);
+  }
+
+  var container = document.getElementById("container");
+  var style = container.style;
+
+  function test() {
+      style.width = "400px";
+      PerfTestRunner.forceLayout();
+      style.width = "500px";
+      PerfTestRunner.forceLayout();
+  }
+
+  PerfTestRunner.measureRunsPerSecond({
+      description: "Measures performance of relayout of a container with many auto fit-content children.",
+      run: test
+});
+</script>
+</html>
diff --git a/third_party/blink/perf_tests/layout/fit-content-change-available-size-text.html b/third_party/blink/perf_tests/layout/fit-content-change-available-size-text.html
new file mode 100644
index 0000000..ad7f9a9
--- /dev/null
+++ b/third_party/blink/perf_tests/layout/fit-content-change-available-size-text.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<div id="container" style="width:500px;">
+  <div id="stf" style="width:fit-content;"></div>
+</div>
+<script src="../resources/runner.js"></script>
+<script>
+  const LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+  var stf = document.getElementById("stf");
+
+  for (var i = 0; i < 1000; i++) {
+      var child = document.createElement("span");
+      stf.appendChild(child);
+      child.textContent = LOREM_IPSUM.substr(
+          Math.floor(Math.random()*100), Math.floor(Math.random()*200)+150);
+  }
+
+  var container = document.getElementById("container");
+  var style = container.style;
+
+  function test() {
+      style.width = "400px";
+      PerfTestRunner.forceLayout();
+      style.width = "500px";
+      PerfTestRunner.forceLayout();
+  }
+
+  PerfTestRunner.measureRunsPerSecond({
+      description: "Measures performance of relayout of a container with many auto fit-content children.",
+      run: test
+});
+</script>
+</html>
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 46472e9..9b529536 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -659,7 +659,6 @@
     "web/devtools_frontend.mojom",
     "web/selection_menu_behavior.mojom",
     "web/window_features.mojom",
-    "web/worker_content_settings_proxy.mojom",
   ]
   public_deps = [
     ":android_mojo_bindings",
@@ -736,22 +735,6 @@
   export_header_blink = "third_party/blink/public/platform/web_common.h"
 }
 
-mojom("media_devices_mojo_bindings") {
-  sources = [
-    "platform/modules/mediastream/media_devices.mojom",
-  ]
-
-  public_deps = [
-    "//media/capture/mojom:video_capture",
-    "//media/mojo/interfaces",
-    "//ui/gfx/geometry/mojo",
-  ]
-
-  export_class_attribute = "CONTENT_EXPORT"
-  export_define = "CONTENT_IMPLEMENTATION=1"
-  export_header = "content/common/content_export.h"
-}
-
 # The embedded_frame_sink_mojo_bindings is separated from the rest of mojom
 # files because its deps contain too many files in Chromium that would pollute
 # the include paths in generated mojom-blink files for other services.
@@ -818,8 +801,6 @@
     ":core_mojo_bindings_blink_headers",
     ":core_mojo_bindings_headers",
     ":embedded_frame_sink_mojo_bindings_blink_headers",
-    ":media_devices_mojo_bindings_blink_headers",
-    ":media_devices_mojo_bindings_headers",
     ":mojo_bindings_blink_headers",
     ":mojo_bindings_headers",
   ]
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index b2e7eac..289a604b 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -50,8 +50,8 @@
 
       <!-- Layered API scripts. Should be consistent with kLayeredAPIResources
            in renderer/core/script/layered_api.cc -->
-      <include name="IDR_LAYERED_API_ASYNC_LOCAL_STORAGE_INDEX_JS" file="../renderer/core/script/resources/layered_api/async-local-storage/index.js" type="BINDATA" skip_minify="true"/>
-      <include name="IDR_LAYERED_API_ASYNC_LOCAL_STORAGE_IDB_UTILS_JS" file="../renderer/core/script/resources/layered_api/async-local-storage/idb_utils.js" type="BINDATA" skip_minify="true"/>
+      <include name="IDR_LAYERED_API_KV_STORAGE_INDEX_JS" file="../renderer/core/script/resources/layered_api/kv-storage/index.js" type="BINDATA" skip_minify="true"/>
+      <include name="IDR_LAYERED_API_KV_STORAGE_IDB_UTILS_JS" file="../renderer/core/script/resources/layered_api/kv-storage/idb_utils.js" type="BINDATA" skip_minify="true"/>
       <include name="IDR_LAYERED_API_BLANK_INDEX_JS" file="../renderer/core/script/resources/layered_api/blank/index.js" type="BINDATA" skip_minify="true"/>
       <include name="IDR_LAYERED_API_VIRTUAL_SCROLLER_INDEX_JS" file="../renderer/core/script/resources/layered_api/virtual-scroller/index.js" type="BINDATA" skip_minify="true"/>
       <include name="IDR_LAYERED_API_VIRTUAL_SCROLLER_ITEM_SOURCE_JS" file="../renderer/core/script/resources/layered_api/virtual-scroller/item-source.js" type="BINDATA" skip_minify="true"/>
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index ec576c4..466f90a 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -65,6 +65,8 @@
     "manifest/manifest.h",
     "manifest/manifest_icon_selector.h",
     "manifest/web_display_mode.h",
+    "mediastream/media_devices.h",
+    "mediastream/media_devices_mojom_traits.h",
     "mediastream/media_stream_controls.h",
     "mediastream/media_stream_mojom_traits.h",
     "mediastream/media_stream_request.h",
diff --git a/third_party/blink/public/common/mediastream/media_devices.h b/third_party/blink/public/common/mediastream/media_devices.h
new file mode 100644
index 0000000..d87affeb
--- /dev/null
+++ b/third_party/blink/public/common/mediastream/media_devices.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_H_
+
+#include <string>
+#include <vector>
+
+#include "media/base/video_facing.h"
+#include "third_party/blink/public/common/common_export.h"
+
+namespace media {
+struct VideoCaptureDeviceDescriptor;
+}  // namespace media
+
+namespace blink {
+
+enum MediaDeviceType {
+  MEDIA_DEVICE_TYPE_AUDIO_INPUT,
+  MEDIA_DEVICE_TYPE_VIDEO_INPUT,
+  MEDIA_DEVICE_TYPE_AUDIO_OUTPUT,
+  NUM_MEDIA_DEVICE_TYPES,
+};
+
+struct BLINK_COMMON_EXPORT WebMediaDeviceInfo {
+  WebMediaDeviceInfo();
+  WebMediaDeviceInfo(const WebMediaDeviceInfo& other);
+  WebMediaDeviceInfo(WebMediaDeviceInfo&& other);
+  WebMediaDeviceInfo(
+      const std::string& device_id,
+      const std::string& label,
+      const std::string& group_id,
+      media::VideoFacingMode video_facing = media::MEDIA_VIDEO_FACING_NONE);
+  explicit WebMediaDeviceInfo(
+      const media::VideoCaptureDeviceDescriptor& descriptor);
+  ~WebMediaDeviceInfo();
+  WebMediaDeviceInfo& operator=(const WebMediaDeviceInfo& other);
+  WebMediaDeviceInfo& operator=(WebMediaDeviceInfo&& other);
+
+  std::string device_id;
+  std::string label;
+  std::string group_id;
+  media::VideoFacingMode video_facing;
+};
+
+using WebMediaDeviceInfoArray = std::vector<WebMediaDeviceInfo>;
+
+BLINK_COMMON_EXPORT bool operator==(const WebMediaDeviceInfo& first,
+                                    const WebMediaDeviceInfo& second);
+
+inline bool IsValidMediaDeviceType(MediaDeviceType type) {
+  return type >= 0 && type < NUM_MEDIA_DEVICE_TYPES;
+}
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_H_
diff --git a/third_party/blink/public/common/mediastream/media_devices.typemap b/third_party/blink/public/common/mediastream/media_devices.typemap
new file mode 100644
index 0000000..763f9fc
--- /dev/null
+++ b/third_party/blink/public/common/mediastream/media_devices.typemap
@@ -0,0 +1,20 @@
+# Copyright 2018 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.
+
+mojom = "//third_party/blink/public/mojom/mediastream/media_devices.mojom"
+
+public_headers = [
+  "//third_party/blink/public/common/mediastream/media_devices.h",
+  "//third_party/blink/public/common/common_export.h",
+]
+
+traits_headers = [
+  "//third_party/blink/public/common/mediastream/media_devices_mojom_traits.h",
+]
+
+type_mappings = [
+  "blink.mojom.MediaDeviceType=blink::MediaDeviceType",
+  "blink.mojom.MediaDeviceInfo=blink::WebMediaDeviceInfo",
+  "blink.mojom.FacingMode=media::VideoFacingMode",
+]
diff --git a/third_party/blink/public/common/mediastream/media_devices_mojom_traits.h b/third_party/blink/public/common/mediastream/media_devices_mojom_traits.h
new file mode 100644
index 0000000..34d0413
--- /dev/null
+++ b/third_party/blink/public/common/mediastream/media_devices_mojom_traits.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_MOJOM_TRAITS_H_
+
+#include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<blink::mojom::MediaDeviceType, blink::MediaDeviceType> {
+  static blink::mojom::MediaDeviceType ToMojom(blink::MediaDeviceType type);
+
+  static bool FromMojom(blink::mojom::MediaDeviceType input,
+                        blink::MediaDeviceType* out);
+};
+
+template <>
+struct EnumTraits<blink::mojom::FacingMode, media::VideoFacingMode> {
+  static blink::mojom::FacingMode ToMojom(media::VideoFacingMode facing_mode);
+
+  static bool FromMojom(blink::mojom::FacingMode input,
+                        media::VideoFacingMode* out);
+};
+
+template <>
+struct StructTraits<blink::mojom::MediaDeviceInfoDataView,
+                    blink::WebMediaDeviceInfo> {
+  static const std::string& device_id(const blink::WebMediaDeviceInfo& info) {
+    return info.device_id;
+  }
+
+  static const std::string& label(const blink::WebMediaDeviceInfo& info) {
+    return info.label;
+  }
+
+  static const std::string& group_id(const blink::WebMediaDeviceInfo& info) {
+    return info.group_id;
+  }
+
+  static bool Read(blink::mojom::MediaDeviceInfoDataView input,
+                   blink::WebMediaDeviceInfo* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIASTREAM_MEDIA_DEVICES_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/common/mediastream/media_stream_request.h b/third_party/blink/public/common/mediastream/media_stream_request.h
index 3fd80a1..946c049 100644
--- a/third_party/blink/public/common/mediastream/media_stream_request.h
+++ b/third_party/blink/public/common/mediastream/media_stream_request.h
@@ -21,7 +21,8 @@
 
 namespace blink {
 
-// Types of media streams.
+// Types of media streams. When updating this list, make sure to update the
+// predicates declared below, e.g. IsVideoScreenCaptureMediaType().
 enum MediaStreamType {
   MEDIA_NO_SERVICE = 0,
 
@@ -84,7 +85,10 @@
 BLINK_COMMON_EXPORT bool IsAudioInputMediaType(MediaStreamType type);
 BLINK_COMMON_EXPORT bool IsVideoInputMediaType(MediaStreamType type);
 BLINK_COMMON_EXPORT bool IsScreenCaptureMediaType(MediaStreamType type);
+// Whether the |type| captures anything on the screen.
+BLINK_COMMON_EXPORT bool IsVideoScreenCaptureMediaType(MediaStreamType type);
 BLINK_COMMON_EXPORT bool IsDesktopCaptureMediaType(MediaStreamType type);
+BLINK_COMMON_EXPORT bool IsVideoDesktopCaptureMediaType(MediaStreamType type);
 BLINK_COMMON_EXPORT bool IsTabCaptureMediaType(MediaStreamType type);
 BLINK_COMMON_EXPORT bool IsDeviceMediaType(MediaStreamType type);
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 93a8cf8..e43f975 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -51,6 +51,7 @@
     "manifest/display_mode.mojom",
     "manifest/manifest.mojom",
     "manifest/manifest_manager.mojom",
+    "mediastream/media_devices.mojom",
     "mediastream/media_stream.mojom",
     "net/ip_address_space.mojom",
     "notifications/notification.mojom",
@@ -87,6 +88,7 @@
     "worker/shared_worker_creation_context_type.mojom",
     "worker/shared_worker_host.mojom",
     "worker/shared_worker_info.mojom",
+    "worker/worker_content_settings_proxy.mojom",
     "worker/worker_main_script_load_params.mojom",
   ]
 
diff --git a/third_party/blink/public/platform/modules/mediastream/media_devices.mojom b/third_party/blink/public/mojom/mediastream/media_devices.mojom
similarity index 100%
rename from third_party/blink/public/platform/modules/mediastream/media_devices.mojom
rename to third_party/blink/public/mojom/mediastream/media_devices.mojom
diff --git a/third_party/blink/public/web/worker_content_settings_proxy.mojom b/third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom
similarity index 100%
rename from third_party/blink/public/web/worker_content_settings_proxy.mojom
rename to third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom
diff --git a/third_party/blink/public/platform/modules/mediastream/OWNERS b/third_party/blink/public/platform/modules/mediastream/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/third_party/blink/public/platform/modules/mediastream/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/platform/oom_intervention.mojom b/third_party/blink/public/platform/oom_intervention.mojom
index b6aca16..fd5282b 100644
--- a/third_party/blink/public/platform/oom_intervention.mojom
+++ b/third_party/blink/public/platform/oom_intervention.mojom
@@ -46,9 +46,11 @@
   // memory usage exceeds |detection_args| memory thresholds (pmf, swap, virtual
   // memory usage and blink memory usage), the renderer calls
   // host->OnHighMemoryUsage(). The renderer also triggers OOM intervention when
-  // |renderer_pause_enabled| or |navigate_ads_enabled| is true.
+  // |renderer_pause_enabled|, |navigate_ads_enabled| or
+  // |purge_v8_memory_enabled| is true.
   StartDetection(OomInterventionHost host,
                  DetectionArgs detection_args,
                  bool renderer_pause_enabled,
-                 bool navigate_ads_enabled);
+                 bool navigate_ads_enabled,
+                 bool purge_v8_memory_enabled);
 };
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 586b889..146d8ea 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2169,6 +2169,7 @@
   kElementTimingExplicitlyRequested = 2728,
   kV8HTMLMediaElement_CaptureStream_Method = 2729,
   kQuirkyLineBoxBackgroundSize = 2730,
+  kDirectlyCompositedImage = 2731,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_layer_tree_view.h b/third_party/blink/public/platform/web_layer_tree_view.h
index 31758b7..08ee91c9 100644
--- a/third_party/blink/public/platform/web_layer_tree_view.h
+++ b/third_party/blink/public/platform/web_layer_tree_view.h
@@ -200,21 +200,6 @@
 
   virtual int LayerTreeId() const { return 0; }
 
-  // Toggles the FPS counter in the HUD layer
-  virtual void SetShowFPSCounter(bool) {}
-
-  // Toggles the paint rects in the HUD layer
-  virtual void SetShowPaintRects(bool) {}
-
-  // Toggles the debug borders on layers
-  virtual void SetShowDebugBorders(bool) {}
-
-  // Toggles scroll bottleneck rects on the HUD layer
-  virtual void SetShowScrollBottleneckRects(bool) {}
-
-  // Toggles the hit-test borders on layers
-  virtual void SetShowHitTestBorders(bool) {}
-
   // ReportTimeCallback is a callback that should be fired when the
   // corresponding Swap completes (either with DidSwap or DidNotSwap).
   virtual void NotifySwapTime(ReportTimeCallback callback) {}
diff --git a/third_party/blink/public/platform/web_rtc_certificate_generator.h b/third_party/blink/public/platform/web_rtc_certificate_generator.h
index f5940a3d..7a9f1112 100644
--- a/third_party/blink/public/platform/web_rtc_certificate_generator.h
+++ b/third_party/blink/public/platform/web_rtc_certificate_generator.h
@@ -34,7 +34,7 @@
 #include "third_party/blink/public/platform/web_callbacks.h"
 #include "third_party/blink/public/platform/web_rtc_key_params.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 #include <memory>
 
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
index fd36fd9..fbca114 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler.h
@@ -35,9 +35,9 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_vector.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/api/rtcerror.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/api/rtc_error.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace webrtc {
 enum class RTCErrorType;
diff --git a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
index 767dbf40..10940467 100644
--- a/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
+++ b/third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h
@@ -35,7 +35,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/web_common.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/platform/web_rtc_rtp_receiver.h b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
index 17f086a..816c9950 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_receiver.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_receiver.h
@@ -10,7 +10,7 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
-#include "third_party/webrtc/api/rtpparameters.h"
+#include "third_party/webrtc/api/rtp_parameters.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/platform/web_rtc_rtp_sender.h b/third_party/blink/public/platform/web_rtc_rtp_sender.h
index f1112ff..1be1836a 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_sender.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_sender.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/webrtc/api/rtpparameters.h"
+#include "third_party/webrtc/api/rtp_parameters.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/platform/web_rtc_rtp_transceiver.h b/third_party/blink/public/platform/web_rtc_rtp_transceiver.h
index 435c1fcc..0ce9cdc 100644
--- a/third_party/blink/public/platform/web_rtc_rtp_transceiver.h
+++ b/third_party/blink/public/platform/web_rtc_rtp_transceiver.h
@@ -13,7 +13,7 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/platform/web_rtc_void_request.h b/third_party/blink/public/platform/web_rtc_void_request.h
index fdfdcd5..e4f37df 100644
--- a/third_party/blink/public/platform/web_rtc_void_request.h
+++ b/third_party/blink/public/platform/web_rtc_void_request.h
@@ -34,7 +34,7 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/webrtc/api/rtcerror.h"
+#include "third_party/webrtc/api/rtc_error.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/public_typemaps.gni b/third_party/blink/public/public_typemaps.gni
index 85b04c5..7946b836 100644
--- a/third_party/blink/public/public_typemaps.gni
+++ b/third_party/blink/public/public_typemaps.gni
@@ -9,6 +9,7 @@
   "//third_party/blink/public/common/loader/url_loader_factory_bundle.typemap",
   "//third_party/blink/public/common/manifest/display_mode.typemap",
   "//third_party/blink/public/common/manifest/manifest.typemap",
+  "//third_party/blink/public/common/mediastream/media_devices.typemap",
   "//third_party/blink/public/common/mediastream/media_stream.typemap",
   "//third_party/blink/public/common/notifications/notification_types.typemap",
   "//third_party/blink/public/common/screen_orientation/screen_orientation_lock_types.typemap",
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index ad044e81..a6106e6 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -196,7 +196,6 @@
   virtual void SetPassiveEventListenerDefault(PassiveEventListenerDefault) = 0;
   virtual void SetPasswordEchoDurationInSeconds(double) = 0;
   virtual void SetPasswordEchoEnabled(bool) = 0;
-  virtual void SetPerTilePaintingEnabled(bool) = 0;
   virtual void SetPictographFontFamily(const WebString&,
                                        UScriptCode = USCRIPT_COMMON) = 0;
   virtual void SetPluginsEnabled(bool) = 0;
@@ -221,8 +220,6 @@
   virtual void SetShouldClearDocumentBackground(bool) = 0;
   virtual void SetShouldRespectImageOrientation(bool) = 0;
   virtual void SetShowContextMenuOnMouseUp(bool) = 0;
-  virtual void SetShowFPSCounter(bool) = 0;
-  virtual void SetShowPaintRects(bool) = 0;
   virtual void SetShrinksViewportContentToFit(bool) = 0;
   virtual void SetSmartInsertDeleteEnabled(bool) = 0;
   // Spatial navigation feature, when enabled, improves the experience
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 8d273373..f7f0ae5b 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -90,11 +90,17 @@
   // TODO(danakj): This field should go away as WebWidgets always composite
   // their output.
   BLINK_EXPORT static WebView* Create(WebViewClient*,
-                                      WebWidgetClient*,
                                       bool is_hidden,
                                       bool compositing_enabled,
                                       WebView* opener);
 
+  // Called on WebView when a WebFrameWidget is created for a local main frame,
+  // and can be set back to null when the WebWidgetClient is removed due to the
+  // main frame being detached.
+  // TODO(danakj): Move this to WebWidget and merge with SetLayerTreeView, have
+  // it be null/not set when the main frame is remote.
+  virtual void SetWebWidgetClient(WebWidgetClient*) = 0;
+
   // Initializes the various client interfaces.
   virtual void SetPrerendererClient(WebPrerendererClient*) = 0;
 
@@ -385,10 +391,6 @@
   BLINK_EXPORT static void WillEnterModalLoop();
   BLINK_EXPORT static void DidExitModalLoop();
 
-  virtual void SetShowPaintRects(bool) = 0;
-  virtual void SetShowFPSCounter(bool) = 0;
-  virtual void SetShowScrollBottleneckRects(bool) = 0;
-
   // Scheduling -----------------------------------------------------------
 
   virtual PageScheduler* Scheduler() const = 0;
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index fdac6cd..b19ca4cc 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -72,6 +72,13 @@
   // a synchronous composite.
   virtual void ScheduleAnimation() {}
 
+  // Show or hide compositor debug visualizations.
+  virtual void SetShowFPSCounter(bool) {}
+  virtual void SetShowPaintRects(bool) {}
+  virtual void SetShowDebugBorders(bool) {}
+  virtual void SetShowScrollBottleneckRects(bool) {}
+  virtual void SetShowHitTestBorders(bool) {}
+
   // A notification callback for when the intrinsic sizing of the
   // widget changed. This is only called for SVG within a remote frame.
   virtual void IntrinsicSizingInfoChanged(const WebIntrinsicSizingInfo&) {}
@@ -90,8 +97,11 @@
   virtual void AutoscrollFling(const WebFloatSize& velocity) {}
   virtual void AutoscrollEnd() {}
 
-  // Called when the widget should be closed.  WebWidget::close() should
-  // be called asynchronously as a result of this notification.
+  // Called when the window for this top-level widget should be closed.
+  // WebWidget::Close() should be called asynchronously as a result of this
+  // notification.
+  // TODO(danakj): Move this to WebView::CloseWindowSoon(), so we can call
+  // it when the main frame is remote and there is no top-level widget.
   virtual void CloseWidgetSoon() {}
 
   // Called to show the widget according to the given policy.
@@ -150,6 +160,10 @@
   // event occurs.
   virtual void RequestUnbufferedInputEvents() {}
 
+  // Requests unbuffered (ie. low latency) input due to debugger being
+  // attached. Debugger needs to paint when stopped in the event handler.
+  virtual void SetNeedsUnbufferedInputForDebugger(bool) {}
+
   // Called during WebWidget::HandleInputEvent for a TouchStart event to inform
   // the embedder of the touch actions that are permitted for this touch.
   virtual void SetTouchAction(WebTouchAction touch_action) {}
diff --git a/third_party/blink/renderer/BUILD.gn b/third_party/blink/renderer/BUILD.gn
index 5e49cab..b8718ca 100644
--- a/third_party/blink/renderer/BUILD.gn
+++ b/third_party/blink/renderer/BUILD.gn
@@ -40,9 +40,14 @@
     "BLINK_IMPLEMENTATION=1",
     "INSIDE_BLINK",
   ]
-
   if (is_clang) {
-    cflags += [ "-Wshorten-64-to-32" ]
+    cflags += [
+      "-Wconversion",
+      "-Wno-float-conversion",
+      "-Wno-sign-conversion",
+      "-Wno-implicit-float-conversion",
+      "-Wno-implicit-int-conversion",
+    ]
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
index 901c64be..d226bca 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
@@ -316,7 +316,7 @@
       BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
   ASSERT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
-  GetExecutionContext()->PausePausableObjects();
+  GetExecutionContext()->PausePausableObjects(PauseState::kFrozen);
   resolver->Resolve("hello");
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
diff --git a/third_party/blink/renderer/bindings/modules/v8/BUILD.gn b/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
index 2ca7b7e..8a0f890 100644
--- a/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/modules/v8/BUILD.gn
@@ -103,7 +103,6 @@
     "//services/device/public/mojom:generic_sensor_headers",
     "//services/device/public/mojom:mojom_blink_headers",
     "//services/shape_detection/public/mojom:mojom_blink_headers",
-    "//third_party/blink/public:media_devices_mojo_bindings_blink_headers",
 
     # IndexedDB Mojom Blink headers are provided by the mojom_modules
     # target.
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl.cc b/third_party/blink/renderer/controller/oom_intervention_impl.cc
index 6b88a90bd..b3b43de 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl.cc
+++ b/third_party/blink/renderer/controller/oom_intervention_impl.cc
@@ -36,7 +36,8 @@
     mojom::blink::OomInterventionHostPtr host,
     mojom::blink::DetectionArgsPtr detection_args,
     bool renderer_pause_enabled,
-    bool navigate_ads_enabled) {
+    bool navigate_ads_enabled,
+    bool purge_v8_memory_enabled) {
   host_ = std::move(host);
 
   // Disable intervention if we cannot get memory details of current process.
@@ -46,6 +47,7 @@
   detection_args_ = std::move(detection_args);
   renderer_pause_enabled_ = renderer_pause_enabled;
   navigate_ads_enabled_ = navigate_ads_enabled;
+  purge_v8_memory_enabled_ = purge_v8_memory_enabled;
 
   timer_.Start(TimeDelta(), TimeDelta::FromSeconds(1), FROM_HERE);
 }
@@ -77,12 +79,14 @@
   ReportMemoryStats(current_memory);
 
   if (oom_detected) {
-    if (navigate_ads_enabled_) {
+    if (navigate_ads_enabled_ || purge_v8_memory_enabled_) {
       for (const auto& page : Page::OrdinaryPages()) {
         if (page->MainFrame()->IsLocalFrame()) {
-          ToLocalFrame(page->MainFrame())
-              ->GetDocument()
-              ->NavigateLocalAdsFrames();
+          LocalFrame* frame = ToLocalFrame(page->MainFrame());
+          if (navigate_ads_enabled_)
+            frame->GetDocument()->NavigateLocalAdsFrames();
+          if (purge_v8_memory_enabled_)
+            frame->ForciblyPurgeV8Memory();
         }
       }
     }
@@ -92,6 +96,7 @@
       // mojo strong binding is disconnected.
       pauser_.reset(new ScopedPagePauser);
     }
+
     host_->OnHighMemoryUsage();
     timer_.Stop();
     // Notify V8GCForContextDispose that page navigation gc is needed when
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl.h b/third_party/blink/renderer/controller/oom_intervention_impl.h
index 1b23b81..90df9fe 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl.h
+++ b/third_party/blink/renderer/controller/oom_intervention_impl.h
@@ -31,7 +31,8 @@
   void StartDetection(mojom::blink::OomInterventionHostPtr,
                       mojom::blink::DetectionArgsPtr detection_args,
                       bool renderer_pause_enabled,
-                      bool navigate_ads_enabled) override;
+                      bool navigate_ads_enabled,
+                      bool purge_v8_memory_enabled) override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(OomInterventionImplTest, DetectedAndDeclined);
@@ -54,6 +55,7 @@
   mojom::blink::OomInterventionHostPtr host_;
   bool renderer_pause_enabled_ = false;
   bool navigate_ads_enabled_ = false;
+  bool purge_v8_memory_enabled_ = false;
   TaskRunnerTimer<OomInterventionImpl> timer_;
   std::unique_ptr<ScopedPagePauser> pauser_;
   OomInterventionMetrics metrics_at_intervention_;
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl_test.cc b/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
index 8aa10b35..28fb8dc 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
+++ b/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
@@ -80,11 +80,13 @@
     WebViewImpl* web_view = web_view_helper_.InitializeAndLoad("about:blank");
     Page* page = web_view->MainFrameImpl()->GetFrame()->GetPage();
     EXPECT_FALSE(page->Paused());
-    RunDetection(true, false);
+    RunDetection(true, false, false);
     return page;
   }
 
-  void RunDetection(bool renderer_pause_enabled, bool navigate_ads_enabled) {
+  void RunDetection(bool renderer_pause_enabled,
+                    bool navigate_ads_enabled,
+                    bool purge_v8_memory_enabled) {
     mojom::blink::OomInterventionHostPtr host_ptr;
     MockOomInterventionHost mock_host(mojo::MakeRequest(&host_ptr));
 
@@ -95,7 +97,8 @@
     args->virtual_memory_thresold = kTestVmSizeThreshold;
 
     intervention_->StartDetection(std::move(host_ptr), std::move(args),
-                                  renderer_pause_enabled, navigate_ads_enabled);
+                                  renderer_pause_enabled, navigate_ads_enabled,
+                                  purge_v8_memory_enabled);
     test::RunDelayedTasks(TimeDelta::FromSeconds(1));
   }
 
@@ -241,9 +244,9 @@
   mojom::blink::OomInterventionHostPtr host_ptr;
   MockOomInterventionHost mock_host(mojo::MakeRequest(&host_ptr));
   mojom::blink::DetectionArgsPtr args(mojom::blink::DetectionArgs::New());
-  intervention_->StartDetection(std::move(host_ptr), std::move(args),
-                                true /*renderer_pause_enabled*/,
-                                true /*navigate_ads_enabled*/);
+  intervention_->StartDetection(
+      std::move(host_ptr), std::move(args), true /*renderer_pause_enabled*/,
+      true /*navigate_ads_enabled*/, true /*purge_v8_memory_enabled*/);
   // Create unsafe shared memory region to write metrics in reporter.
   base::UnsafeSharedMemoryRegion shm =
       base::UnsafeSharedMemoryRegion::Create(sizeof(OomInterventionMetrics));
@@ -297,11 +300,28 @@
   EXPECT_TRUE(local_adframe->IsAdSubframe());
   EXPECT_FALSE(local_non_adframe->IsAdSubframe());
 
-  RunDetection(true, true);
+  RunDetection(true, true, false);
 
   EXPECT_EQ(local_adframe->GetDocument()->Url().GetString(), "about:blank");
   EXPECT_NE(local_non_adframe->GetDocument()->Url().GetString(), "about:blank");
   EXPECT_TRUE(page->Paused());
 }
 
+TEST_F(OomInterventionImplTest, V2DetectionV8PurgeMemory) {
+  OomInterventionMetrics mock_metrics = {};
+  mock_metrics.current_blink_usage_kb = (kTestBlinkThreshold / 1024) - 1;
+  mock_metrics.current_private_footprint_kb = (kTestPMFThreshold / 1024) - 1;
+  mock_metrics.current_swap_kb = (kTestSwapThreshold / 1024) - 1;
+  // Set value more than the threshold to trigger intervention.
+  mock_metrics.current_vm_size_kb = (kTestVmSizeThreshold / 1024) + 1;
+  intervention_->SetMetrics(mock_metrics);
+
+  WebViewImpl* web_view = web_view_helper_.InitializeAndLoad("about:blank");
+  Page* page = web_view->MainFrameImpl()->GetFrame()->GetPage();
+  LocalFrame* frame = ToLocalFrame(page->MainFrame());
+  EXPECT_FALSE(frame->GetDocument()->ExecutionContext::IsContextDestroyed());
+  RunDetection(true, true, true);
+  EXPECT_TRUE(frame->GetDocument()->ExecutionContext::IsContextDestroyed());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 96ce53be..7bced7dd 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1817,7 +1817,6 @@
     "dom/names_map_test.cc",
     "dom/node_test.cc",
     "dom/nth_index_cache_test.cc",
-    "dom/pausable_object_test.cc",
     "dom/range_test.cc",
     "dom/scripted_animation_controller_test.cc",
     "dom/scripted_idle_task_controller_test.cc",
@@ -1837,6 +1836,7 @@
     "events/pointer_event_factory_test.cc",
     "events/touch_event_test.cc",
     "events/web_input_event_conversion_test.cc",
+    "execution_context/pausable_object_test.cc",
     "exported/local_frame_client_impl_test.cc",
     "exported/prerendering_test.cc",
     "exported/web_associated_url_loader_impl_test.cc",
diff --git a/third_party/blink/renderer/core/OWNERS b/third_party/blink/renderer/core/OWNERS
index 5109981f5..80e3fa5 100644
--- a/third_party/blink/renderer/core/OWNERS
+++ b/third_party/blink/renderer/core/OWNERS
@@ -70,7 +70,7 @@
 vollick@chromium.org
 wangxianzhu@chromium.org
 yhirano@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
 yutak@chromium.org
 
 # TEAM: blink-dev@chromium.org
diff --git a/third_party/blink/renderer/core/animation/worklet_animation_controller.cc b/third_party/blink/renderer/core/animation/worklet_animation_controller.cc
index fc280aa..666ab97 100644
--- a/third_party/blink/renderer/core/animation/worklet_animation_controller.cc
+++ b/third_party/blink/renderer/core/animation/worklet_animation_controller.cc
@@ -110,6 +110,19 @@
   return mutator_dispatcher;
 }
 
+// TODO(yigu): Currently one animator name is synced back per registration.
+// Eventually all registered names should be synced in batch once a module
+// completes its loading in the worklet scope. https://crbug.com/920722.
+void WorkletAnimationController::SynchronizeAnimatorName(
+    const String& animator_name) {
+  animator_names_.insert(animator_name);
+}
+
+bool WorkletAnimationController::IsAnimatorRegistered(
+    const String& animator_name) const {
+  return animator_names_.Contains(animator_name);
+}
+
 void WorkletAnimationController::SetMutationUpdate(
     std::unique_ptr<AnimationWorkletOutput> output_state) {
   if (!output_state)
diff --git a/third_party/blink/renderer/core/animation/worklet_animation_controller.h b/third_party/blink/renderer/core/animation/worklet_animation_controller.h
index a20ebe8..0acfb2f 100644
--- a/third_party/blink/renderer/core/animation/worklet_animation_controller.h
+++ b/third_party/blink/renderer/core/animation/worklet_animation_controller.h
@@ -55,8 +55,15 @@
   base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
   EnsureMainThreadMutatorDispatcher(
       scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner);
+
   void SetMutationUpdate(
       std::unique_ptr<AnimationWorkletOutput> output) override;
+
+  void SynchronizeAnimatorName(const String& animator_name) override;
+  // Returns true if the animator with given name is registered in
+  // AnimationWorkletGlobalScope.
+  bool IsAnimatorRegistered(const String& animator_name) const;
+
   void Trace(blink::Visitor*);
 
  private:
@@ -67,6 +74,8 @@
   HeapHashSet<Member<WorkletAnimationBase>> pending_animations_;
   HeapHashMap<int, Member<WorkletAnimationBase>> animations_;
 
+  WTF::HashSet<String> animator_names_;
+
   // TODO(yigu): The following proxy is needed for platform/ to access this
   // class. We should bypass it eventually.
   std::unique_ptr<MainThreadMutatorClient> main_thread_mutator_client_;
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 2918f7a..5dfcf39 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -263,12 +263,20 @@
 bool IsSimpleSelectorValidAfterPseudoElement(
     const CSSParserSelector& simple_selector,
     CSSSelector::PseudoType compound_pseudo_element) {
-  if (compound_pseudo_element == CSSSelector::kPseudoUnknown)
-    return true;
-  if (compound_pseudo_element == CSSSelector::kPseudoContent)
-    return simple_selector.Match() != CSSSelector::kPseudoElement;
-  if (compound_pseudo_element == CSSSelector::kPseudoSlotted)
-    return simple_selector.IsTreeAbidingPseudoElement();
+  switch (compound_pseudo_element) {
+    case CSSSelector::kPseudoUnknown:
+      return true;
+    case CSSSelector::kPseudoContent:
+      return simple_selector.Match() != CSSSelector::kPseudoElement;
+    case CSSSelector::kPseudoSlotted:
+      return simple_selector.IsTreeAbidingPseudoElement();
+    case CSSSelector::kPseudoPart:
+      if (simple_selector.IsTreeAbidingPseudoElement())
+        return true;
+      break;
+    default:
+      break;
+  }
   if (simple_selector.Match() != CSSSelector::kPseudoClass)
     return false;
   CSSSelector::PseudoType pseudo = simple_selector.GetPseudoType();
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index a795cba4..26b759c 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -563,6 +563,24 @@
   }
 }
 
+TEST(CSSSelectorParserTest, ShadowPartAndBeforeAfterPseudoElementValid) {
+  const char* test_cases[] = {"::part(ident)::before", "::part(ident)::after",
+                              "::part(ident)::placeholder"};
+
+  for (auto* test_case : test_cases) {
+    SCOPED_TRACE(test_case);
+    CSSTokenizer tokenizer(test_case);
+    const auto tokens = tokenizer.TokenizeToEOF();
+    CSSParserTokenRange range(tokens);
+    CSSSelectorList list = CSSSelectorParser::ParseSelector(
+        range,
+        CSSParserContext::Create(kHTMLStandardMode,
+                                 SecureContextMode::kInsecureContext),
+        nullptr);
+    EXPECT_STREQ(test_case, list.SelectorsText().Ascii().data());
+  }
+}
+
 TEST(CSSSelectorParserTest, UseCountShadowPseudo) {
   std::unique_ptr<DummyPageHolder> dummy_holder =
       DummyPageHolder::Create(IntSize(500, 500));
diff --git a/third_party/blink/renderer/core/dom/BUILD.gn b/third_party/blink/renderer/core/dom/BUILD.gn
index b92b4abe..07e459e 100644
--- a/third_party/blink/renderer/core/dom/BUILD.gn
+++ b/third_party/blink/renderer/core/dom/BUILD.gn
@@ -208,8 +208,6 @@
     "nth_index_cache.h",
     "parent_node.h",
     "parser_content_policy.h",
-    "pausable_object.cc",
-    "pausable_object.h",
     "presentation_attribute_style.cc",
     "presentation_attribute_style.h",
     "processing_instruction.cc",
diff --git a/third_party/blink/renderer/core/dom/context_lifecycle_notifier.cc b/third_party/blink/renderer/core/dom/context_lifecycle_notifier.cc
index 1e755ea..6881ef5 100644
--- a/third_party/blink/renderer/core/dom/context_lifecycle_notifier.cc
+++ b/third_party/blink/renderer/core/dom/context_lifecycle_notifier.cc
@@ -28,7 +28,7 @@
 #include "third_party/blink/renderer/core/dom/context_lifecycle_notifier.h"
 
 #include "base/auto_reset.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 
 namespace blink {
 
@@ -42,11 +42,12 @@
     DCHECK_EQ(pausable_object->GetExecutionContext(), Context());
     DCHECK(pausable_object->PauseIfNeededCalled());
 #endif
-    pausable_object->Unpause();
+    pausable_object->ContextUnpaused();
   });
 }
 
-void ContextLifecycleNotifier::NotifySuspendingPausableObjects() {
+void ContextLifecycleNotifier::NotifySuspendingPausableObjects(
+    PauseState state) {
   ForEachObserver([&](ContextLifecycleObserver* observer) {
     if (observer->ObserverType() !=
         ContextLifecycleObserver::kPausableObjectType)
@@ -56,7 +57,7 @@
     DCHECK_EQ(pausable_object->GetExecutionContext(), Context());
     DCHECK(pausable_object->PauseIfNeededCalled());
 #endif
-    pausable_object->Pause();
+    pausable_object->ContextPaused(state);
   });
 }
 
diff --git a/third_party/blink/renderer/core/dom/context_lifecycle_notifier.h b/third_party/blink/renderer/core/dom/context_lifecycle_notifier.h
index a7895c6..c8aa17b7 100644
--- a/third_party/blink/renderer/core/dom/context_lifecycle_notifier.h
+++ b/third_party/blink/renderer/core/dom/context_lifecycle_notifier.h
@@ -30,6 +30,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/execution_context/pause_state.h"
 #include "third_party/blink/renderer/platform/lifecycle_notifier.h"
 
 namespace blink {
@@ -42,7 +43,7 @@
     : public LifecycleNotifier<ExecutionContext, ContextLifecycleObserver> {
  public:
   void NotifyResumingPausableObjects();
-  void NotifySuspendingPausableObjects();
+  void NotifySuspendingPausableObjects(PauseState state);
 
   unsigned PausableObjectCount() const;
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index bc38bc5d..bd5e91bc 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -724,9 +724,9 @@
   } else if (imports_controller_) {
     fetcher_ = FrameFetchContext::CreateFetcherForImportedDocument(this);
   } else {
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
         *MakeGarbageCollected<NullResourceFetcherProperties>(),
-        &FetchContext::NullInstance(GetTaskRunner(TaskType::kNetworking)));
+        &FetchContext::NullInstance(GetTaskRunner(TaskType::kNetworking))));
   }
   DCHECK(fetcher_);
 
@@ -7022,7 +7022,7 @@
     // don't have an attached frame and if execution context is destroyed.
     if (!frame_ || !frame_->IsAttached() ||
         ExecutionContext::IsContextDestroyed()) {
-      scripted_idle_task_controller_->Pause();
+      scripted_idle_task_controller_->ContextPaused(PauseState::kFrozen);
     }
   }
   return *scripted_idle_task_controller_;
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.h b/third_party/blink/renderer/core/dom/layout_tree_builder.h
index f71adc0..1f565a3 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.h
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.h
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/text.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -86,7 +87,8 @@
     // AddChild() implementations to walk up the tree to find the correct
     // layout tree parent/siblings.
     if (next && next->IsText() && next->Parent()->IsAnonymous() &&
-        next->Parent()->IsInline()) {
+        next->Parent()->IsInline() &&
+        !ToLayoutInline(next->Parent())->IsFirstLineAnonymous()) {
       return next->Parent();
     }
     return next;
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc
index d9bb0cb..ec42ac0 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc
@@ -226,11 +226,11 @@
   idle_tasks_.clear();
 }
 
-void ScriptedIdleTaskController::Pause() {
+void ScriptedIdleTaskController::ContextPaused(PauseState) {
   paused_ = true;
 }
 
-void ScriptedIdleTaskController::Unpause() {
+void ScriptedIdleTaskController::ContextUnpaused() {
   DCHECK(paused_);
   paused_ = false;
 
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h
index c734b7c..39754b4b 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_idle_request_callback.h"
 #include "third_party/blink/renderer/core/dom/idle_deadline.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -78,8 +78,8 @@
 
   // PausableObject interface.
   void ContextDestroyed(ExecutionContext*) override;
-  void Pause() override;
-  void Unpause() override;
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
 
   void CallbackFired(CallbackId,
                      TimeTicks deadline,
diff --git a/third_party/blink/renderer/core/execution_context/BUILD.gn b/third_party/blink/renderer/core/execution_context/BUILD.gn
index eed99d1..ea7da46 100644
--- a/third_party/blink/renderer/core/execution_context/BUILD.gn
+++ b/third_party/blink/renderer/core/execution_context/BUILD.gn
@@ -8,6 +8,9 @@
   sources = [
     "execution_context.cc",
     "execution_context.h",
+    "pausable_object.cc",
+    "pausable_object.h",
+    "pause_state.h",
     "remote_security_context.cc",
     "remote_security_context.h",
     "security_context.cc",
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 77c9af1..601da87 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -31,8 +31,8 @@
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
 #include "third_party/blink/renderer/core/events/error_event.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
 #include "third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
@@ -51,7 +51,6 @@
     : isolate_(isolate),
       circular_sequential_id_(0),
       in_dispatch_error_event_(false),
-      is_context_paused_(false),
       is_context_destroyed_(false),
       csp_delegate_(MakeGarbageCollected<ExecutionContextCSPDelegate>(*this)),
       window_interaction_tokens_(0),
@@ -78,15 +77,15 @@
   return ToExecutionContext(info.Holder()->CreationContext());
 }
 
-void ExecutionContext::PausePausableObjects() {
-  DCHECK(!is_context_paused_);
-  NotifySuspendingPausableObjects();
-  is_context_paused_ = true;
+void ExecutionContext::PausePausableObjects(PauseState state) {
+  DCHECK(!pause_state_);
+  pause_state_ = state;
+  NotifySuspendingPausableObjects(state);
 }
 
 void ExecutionContext::UnpausePausableObjects() {
-  DCHECK(is_context_paused_);
-  is_context_paused_ = false;
+  DCHECK(pause_state_.has_value());
+  pause_state_.reset();
   NotifyResumingPausableObjects();
 }
 
@@ -96,8 +95,8 @@
   ContextLifecycleNotifier::NotifyContextDestroyed();
 }
 
-void ExecutionContext::PauseScheduledTasks() {
-  PausePausableObjects();
+void ExecutionContext::PauseScheduledTasks(PauseState state) {
+  PausePausableObjects(state);
   TasksWerePaused();
 }
 
@@ -111,8 +110,8 @@
   DCHECK(Contains(object));
 #endif
   // Ensure all PausableObjects are paused also newly created ones.
-  if (is_context_paused_)
-    object->Pause();
+  if (pause_state_)
+    object->ContextPaused(pause_state_.value());
 }
 
 void ExecutionContext::DispatchErrorEvent(
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index d970ada..037c5f3a 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -32,11 +32,13 @@
 
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/unguessable_token.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/execution_context/pause_state.h"
 #include "third_party/blink/renderer/core/loader/console_logger_impl_base.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/https_state.h"
@@ -195,12 +197,12 @@
 
   virtual void RemoveURLFromMemoryCache(const KURL&);
 
-  void PausePausableObjects();
+  void PausePausableObjects(PauseState);
   void UnpausePausableObjects();
   void StopPausableObjects();
   void NotifyContextDestroyed() override;
 
-  void PauseScheduledTasks();
+  void PauseScheduledTasks(PauseState);
   void UnpauseScheduledTasks();
 
   // TODO(haraken): Remove these methods by making the customers inherit from
@@ -210,8 +212,9 @@
   virtual void TasksWerePaused() {}
   virtual void TasksWereUnpaused() {}
 
-  bool IsContextPaused() const { return is_context_paused_; }
+  bool IsContextPaused() const { return pause_state_.has_value(); }
   bool IsContextDestroyed() const { return is_context_destroyed_; }
+  base::Optional<PauseState> ContextPauseState() const { return pause_state_; }
 
   // Called after the construction of an PausableObject to synchronize
   // pause state.
@@ -286,7 +289,7 @@
   bool in_dispatch_error_event_;
   HeapVector<Member<ErrorEvent>> pending_exceptions_;
 
-  bool is_context_paused_;
+  base::Optional<PauseState> pause_state_;
   bool is_context_destroyed_;
 
   Member<PublicURLManager> public_url_manager_;
diff --git a/third_party/blink/renderer/core/dom/pausable_object.cc b/third_party/blink/renderer/core/execution_context/pausable_object.cc
similarity index 88%
rename from third_party/blink/renderer/core/dom/pausable_object.cc
rename to third_party/blink/renderer/core/execution_context/pausable_object.cc
index bb23fbd..66616d5 100644
--- a/third_party/blink/renderer/core/dom/pausable_object.cc
+++ b/third_party/blink/renderer/core/execution_context/pausable_object.cc
@@ -24,7 +24,7 @@
  *
  */
 
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/instance_counters.h"
@@ -59,9 +59,9 @@
     context->PausePausableObjectIfNeeded(this);
 }
 
-void PausableObject::Pause() {}
+void PausableObject::ContextPaused(PauseState) {}
 
-void PausableObject::Unpause() {}
+void PausableObject::ContextUnpaused() {}
 
 void PausableObject::DidMoveToNewExecutionContext(ExecutionContext* context) {
   SetContext(context);
@@ -71,12 +71,13 @@
     return;
   }
 
-  if (context->IsContextPaused()) {
-    Pause();
+  base::Optional<PauseState> pause_state = context->ContextPauseState();
+  if (pause_state) {
+    ContextPaused(pause_state.value());
     return;
   }
 
-  Unpause();
+  ContextUnpaused();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/pausable_object.h b/third_party/blink/renderer/core/execution_context/pausable_object.h
similarity index 88%
rename from third_party/blink/renderer/core/dom/pausable_object.h
rename to third_party/blink/renderer/core/execution_context/pausable_object.h
index 304f429..b9dd7b6 100644
--- a/third_party/blink/renderer/core/dom/pausable_object.h
+++ b/third_party/blink/renderer/core/execution_context/pausable_object.h
@@ -24,8 +24,8 @@
  *
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PAUSABLE_OBJECT_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PAUSABLE_OBJECT_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSABLE_OBJECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSABLE_OBJECT_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
@@ -48,7 +48,7 @@
 //
 // Objects with asynchronous activity, especially activity that may have an
 // observable effect on web-visible state, on should suspend that activity while
-// the page is paused by overriding Pause() and Unpause().
+// the page is paused by overriding ContextPaused() and ContextUnpaused().
 //
 // https://html.spec.whatwg.org/multipage/webappapis.html#pause
 class CORE_EXPORT PausableObject : public ContextLifecycleObserver {
@@ -64,8 +64,8 @@
 
   // These methods have an empty default implementation so that subclasses
   // which don't need special treatment can skip implementation.
-  virtual void Pause();
-  virtual void Unpause();
+  virtual void ContextPaused(PauseState);
+  virtual void ContextUnpaused();
 
   void DidMoveToNewExecutionContext(ExecutionContext*);
 
@@ -80,4 +80,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PAUSABLE_OBJECT_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSABLE_OBJECT_H_
diff --git a/third_party/blink/renderer/core/dom/pausable_object_test.cc b/third_party/blink/renderer/core/execution_context/pausable_object_test.cc
similarity index 96%
rename from third_party/blink/renderer/core/dom/pausable_object_test.cc
rename to third_party/blink/renderer/core/execution_context/pausable_object_test.cc
index c43ea1e1..8da0960 100644
--- a/third_party/blink/renderer/core/dom/pausable_object_test.cc
+++ b/third_party/blink/renderer/core/execution_context/pausable_object_test.cc
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 
 #include <memory>
 #include "testing/gmock/include/gmock/gmock.h"
@@ -95,7 +95,7 @@
 }
 
 TEST_F(PausableObjectTest, MoveToSuspendedDocument) {
-  DestDocument().PauseScheduledTasks();
+  DestDocument().PauseScheduledTasks(PauseState::kFrozen);
 
   EXPECT_CALL(PausableObject(), Pause());
   PausableObject().DidMoveToNewExecutionContext(&DestDocument());
diff --git a/third_party/blink/renderer/core/execution_context/pause_state.h b/third_party/blink/renderer/core/execution_context/pause_state.h
new file mode 100644
index 0000000..4ec7b3f5
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/pause_state.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSE_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSE_STATE_H_
+
+namespace blink {
+
+// This enum represents the pausing state of the ExecutionContext.
+
+enum class PauseState {
+  // Pause tasks only. Used for nested event loops (alert, print).
+  kPaused,
+  // Freeze everything including media. Used for policies that
+  // have auto stopping of media that is hidden.
+  kFrozen
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_PAUSE_STATE_H_
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index e8f5c98..73736ec 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -461,7 +461,7 @@
   ClientMessageLoopAdapter::PauseForPageWait(web_local_frame_impl_);
 }
 
-bool WebDevToolsAgentImpl::IsInspectorLayer(GraphicsLayer* layer) {
+bool WebDevToolsAgentImpl::IsInspectorLayer(const cc::Layer* layer) {
   for (auto& it : overlay_agents_) {
     if (it.value->IsInspectorLayer(layer))
       return true;
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
index aae7408..8039d45 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
@@ -46,7 +46,6 @@
 
 class ClientMessageLoopAdapter;
 class GraphicsContext;
-class GraphicsLayer;
 class InspectedFrames;
 class InspectorNetworkAgent;
 class InspectorOverlayAgent;
@@ -112,7 +111,7 @@
   void WaitForDebugger() override;
 
   // InspectorLayerTreeAgent::Client implementation.
-  bool IsInspectorLayer(GraphicsLayer*) override;
+  bool IsInspectorLayer(const cc::Layer*) override;
 
   // Thread::TaskObserver implementation.
   void WillProcessTask(const base::PendingTask&) override;
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index 96e1ba1..0652ca8 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -42,8 +42,6 @@
                                  DevToolsEmulator* dev_tools_emulator)
     : settings_(settings),
       dev_tools_emulator_(dev_tools_emulator),
-      show_fps_counter_(false),
-      show_paint_rects_(false),
       render_v_sync_notification_enabled_(false),
       auto_zoom_focused_node_to_legible_scale_(false),
       support_deprecated_target_density_dpi_(false),
@@ -455,14 +453,6 @@
   settings_->SetShowContextMenuOnMouseUp(enabled);
 }
 
-void WebSettingsImpl::SetShowFPSCounter(bool show) {
-  show_fps_counter_ = show;
-}
-
-void WebSettingsImpl::SetShowPaintRects(bool show) {
-  show_paint_rects_ = show;
-}
-
 void WebSettingsImpl::SetEditingBehavior(EditingBehavior behavior) {
   settings_->SetEditingBehaviorType(static_cast<EditingBehaviorType>(behavior));
 }
@@ -558,10 +548,6 @@
   settings_->SetPasswordEchoDurationInSeconds(duration_in_seconds);
 }
 
-void WebSettingsImpl::SetPerTilePaintingEnabled(bool enabled) {
-  per_tile_painting_enabled_ = enabled;
-}
-
 void WebSettingsImpl::SetShouldPrintBackgrounds(bool enabled) {
   settings_->SetShouldPrintBackgrounds(enabled);
 }
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h
index 24cdc9e3..0a133789 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -126,7 +126,6 @@
   void SetPassiveEventListenerDefault(PassiveEventListenerDefault) override;
   void SetPasswordEchoDurationInSeconds(double) override;
   void SetPasswordEchoEnabled(bool) override;
-  void SetPerTilePaintingEnabled(bool) override;
   void SetPictographFontFamily(const WebString&,
                                UScriptCode = USCRIPT_COMMON) override;
   void SetPluginsEnabled(bool) override;
@@ -150,8 +149,6 @@
   void SetShouldClearDocumentBackground(bool) override;
   void SetShouldRespectImageOrientation(bool) override;
   void SetShowContextMenuOnMouseUp(bool) override;
-  void SetShowFPSCounter(bool) override;
-  void SetShowPaintRects(bool) override;
   void SetShrinksViewportContentToFit(bool) override;
   void SetSmartInsertDeleteEnabled(bool) override;
   void SetSmoothScrollForFindEnabled(bool) override;
@@ -219,8 +216,6 @@
   void SetLazyImageLoadingDistanceThresholdPx3G(int) override;
   void SetLazyImageLoadingDistanceThresholdPx4G(int) override;
 
-  bool ShowFPSCounter() const { return show_fps_counter_; }
-  bool ShowPaintRects() const { return show_paint_rects_; }
   bool RenderVSyncNotificationEnabled() const {
     return render_v_sync_notification_enabled_;
   }
@@ -228,7 +223,6 @@
     return auto_zoom_focused_node_to_legible_scale_;
   }
   bool DoubleTapToZoomEnabled() const;
-  bool PerTilePaintingEnabled() const { return per_tile_painting_enabled_; }
   bool SupportDeprecatedTargetDensityDPI() const {
     return support_deprecated_target_density_dpi_;
   }
@@ -249,11 +243,8 @@
  private:
   Settings* settings_;
   UntracedMember<DevToolsEmulator> dev_tools_emulator_;
-  bool show_fps_counter_;
-  bool show_paint_rects_;
   bool render_v_sync_notification_enabled_;
   bool auto_zoom_focused_node_to_legible_scale_;
-  bool per_tile_painting_enabled_;
   bool support_deprecated_target_density_dpi_;
   // This quirk is to maintain compatibility with Android apps built on
   // the Android SDK prior to and including version 18. Presumably, this
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index b7b4877..eb757ea5 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -41,8 +41,8 @@
 #include "third_party/blink/public/common/privacy_preferences.h"
 #include "third_party/blink/public/mojom/csp/content_security_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/net/ip_address_space.mojom-shared.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/public/web/web_shared_worker_client.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/exported/worker_shadow_page.h"
 #include "third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.h"
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 3d43e4b..60696fa 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -255,23 +255,21 @@
 // WebView ----------------------------------------------------------------
 
 WebView* WebView::Create(WebViewClient* client,
-                         WebWidgetClient* widget_client,
                          bool is_hidden,
                          bool compositing_enabled,
                          WebView* opener) {
-  return WebViewImpl::Create(client, widget_client, is_hidden,
-                             compositing_enabled,
+  return WebViewImpl::Create(client, is_hidden, compositing_enabled,
                              static_cast<WebViewImpl*>(opener));
 }
 
 WebViewImpl* WebViewImpl::Create(WebViewClient* client,
-                                 WebWidgetClient* widget_client,
                                  bool is_hidden,
                                  bool compositing_enabled,
                                  WebViewImpl* opener) {
-  // Pass the WebViewImpl's self-reference to the caller.
-  auto web_view = base::AdoptRef(new WebViewImpl(
-      client, widget_client, is_hidden, compositing_enabled, opener));
+  // Take a self-reference for WebViewImpl that is released by calling Close(),
+  // then return a raw pointer to the caller.
+  auto web_view = base::AdoptRef(
+      new WebViewImpl(client, is_hidden, compositing_enabled, opener));
   web_view->AddRef();
   return web_view.get();
 }
@@ -293,12 +291,10 @@
 }
 
 WebViewImpl::WebViewImpl(WebViewClient* client,
-                         WebWidgetClient* widget_client,
                          bool is_hidden,
                          bool does_composite,
                          WebViewImpl* opener)
     : as_view_(client),
-      as_widget_(widget_client),
       chrome_client_(ChromeClientImpl::Create(this)),
       should_auto_resize_(false),
       zoom_level_(0),
@@ -335,7 +331,6 @@
       display_mode_(kWebDisplayModeBrowser),
       elastic_overscroll_(FloatSize()),
       mutator_dispatcher_(nullptr) {
-  DCHECK_EQ(!!AsView().client, !!AsWidget().client);
   if (!AsView().client) {
     DCHECK(!does_composite_);
   }
@@ -367,6 +362,10 @@
   DCHECK(!AsView().page);
 }
 
+void WebViewImpl::SetWebWidgetClient(WebWidgetClient* client) {
+  AsWidget().client = client;
+}
+
 WebDevToolsAgentImpl* WebViewImpl::MainFrameDevToolsAgentImpl() {
   WebLocalFrameImpl* main_frame = MainFrameImpl();
   return main_frame ? main_frame->DevToolsAgentImpl() : nullptr;
@@ -714,35 +713,6 @@
   fake_page_scale_animation_page_scale_factor_ = 0;
 }
 
-void WebViewImpl::SetShowFPSCounter(bool show) {
-  if (layer_tree_view_) {
-    TRACE_EVENT0("blink", "WebViewImpl::setShowFPSCounter");
-    layer_tree_view_->SetShowFPSCounter(show);
-  }
-}
-
-void WebViewImpl::SetShowPaintRects(bool show) {
-  if (layer_tree_view_) {
-    TRACE_EVENT0("blink", "WebViewImpl::setShowPaintRects");
-    layer_tree_view_->SetShowPaintRects(show);
-  }
-}
-
-void WebViewImpl::SetShowDebugBorders(bool show) {
-  if (layer_tree_view_)
-    layer_tree_view_->SetShowDebugBorders(show);
-}
-
-void WebViewImpl::SetShowScrollBottleneckRects(bool show) {
-  if (layer_tree_view_)
-    layer_tree_view_->SetShowScrollBottleneckRects(show);
-}
-
-void WebViewImpl::SetShowHitTestBorders(bool show) {
-  if (layer_tree_view_)
-    layer_tree_view_->SetShowHitTestBorders(show);
-}
-
 void WebViewImpl::AcceptLanguagesChanged() {
   if (AsView().client)
     FontCache::AcceptLanguagesChanged(AsView().client->AcceptLanguages());
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 2b0e9a2..a60e9533 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -101,7 +101,6 @@
                                       public PageWidgetEventHandler {
  public:
   static WebViewImpl* Create(WebViewClient*,
-                             WebWidgetClient*,
                              bool is_hidden,
                              bool compositing_enabled,
                              WebViewImpl* opener);
@@ -111,6 +110,7 @@
   static bool UseExternalPopupMenus();
 
   // WebView methods:
+  void SetWebWidgetClient(WebWidgetClient*) override;
   void SetPrerendererClient(WebPrerendererClient*) override;
   WebSettings* GetSettings() override;
   WebString PageEncoding() const override;
@@ -188,11 +188,6 @@
   WebPagePopupImpl* GetPagePopup() const override { return page_popup_.get(); }
   void SetMainFrameOverlayColor(SkColor) override;
   WebPageImportanceSignals* PageImportanceSignals() override;
-  void SetShowPaintRects(bool) override;
-  void SetShowDebugBorders(bool);
-  void SetShowFPSCounter(bool) override;
-  void SetShowScrollBottleneckRects(bool) override;
-  void SetShowHitTestBorders(bool);
   void AcceptLanguagesChanged() override;
   void SetPageFrozen(bool frozen) override;
   WebWidget* MainFrameWidget() override;
@@ -489,7 +484,6 @@
   friend class WTF::RefCounted<WebViewImpl>;
 
   WebViewImpl(WebViewClient*,
-              WebWidgetClient*,
               bool is_hidden,
               bool does_composite,
               WebViewImpl* opener);
@@ -563,7 +557,7 @@
   struct ViewData {
     ViewData(WebViewClient* client) : client(client) {}
 
-    // Can be null (e.g. unittests, shared workers, etc.)
+    // Can be null (e.g. unittests, shared workers, etc).
     WebViewClient* client;
     Persistent<Page> page;
   } as_view_;
@@ -572,9 +566,8 @@
   // APIs. They can be called from within WebWidget APIs, and internal methods,
   // though these need to be sorted as being for the view or the widget also.
   struct WidgetData {
-    WidgetData(WebWidgetClient* client) : client(client) {}
-
-    WebWidgetClient* client;  // Can also be null.
+    // Can be null (e.g. unittests, shared workers, etc).
+    WebWidgetClient* client = nullptr;
   } as_widget_;
 
   Persistent<ChromeClient> chrome_client_;
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index e5d7655..0822758 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -491,7 +491,7 @@
   frame_test_helpers::TestWebViewClient web_view_client;
   frame_test_helpers::TestWebWidgetClient web_widget_client;
   WebViewImpl* web_view = static_cast<WebViewImpl*>(
-      WebView::Create(&web_view_client, &web_widget_client,
+      WebView::Create(&web_view_client,
                       /*is_hidden=*/false,
                       /*compositing_enabled=*/true, nullptr));
   EXPECT_NE(SK_ColorBLUE, web_view->BackgroundColor());
@@ -499,6 +499,10 @@
   // background color.
   web_view->SetBaseBackgroundColor(SK_ColorBLUE);
   EXPECT_EQ(SK_ColorBLUE, web_view->BackgroundColor());
+
+  // TODO(danakj): Make this part of attaching the main frame's WebFrameWidget.
+  web_view->SetWebWidgetClient(&web_widget_client);
+
   frame_test_helpers::TestWebFrameClient web_frame_client;
   mojom::blink::DocumentInterfaceBrokerPtrInfo document_interface_broker;
   WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
@@ -2561,7 +2565,7 @@
   // Note: this test doesn't use WebViewHelper since WebViewHelper creates an
   // internal WebViewClient on demand if the supplied WebViewClient is null.
   WebViewImpl* web_view = static_cast<WebViewImpl*>(
-      WebView::Create(nullptr, nullptr, /*is_hidden=*/false,
+      WebView::Create(nullptr, /*is_hidden=*/false,
                       /*compositing_enabled=*/false, nullptr));
   frame_test_helpers::TestWebFrameClient web_frame_client;
   frame_test_helpers::TestWebWidgetClient web_widget_client;
diff --git a/third_party/blink/renderer/core/exported/worker_shadow_page.cc b/third_party/blink/renderer/core/exported/worker_shadow_page.cc
index ff6c7ea..1fe55298c 100644
--- a/third_party/blink/renderer/core/exported/worker_shadow_page.cc
+++ b/third_party/blink/renderer/core/exported/worker_shadow_page.cc
@@ -30,7 +30,6 @@
     PrivacyPreferences preferences)
     : client_(client),
       web_view_(WebViewImpl::Create(nullptr,
-                                    nullptr,
                                     /*is_hidden=*/false,
                                     /*compositing_enabled=*/false,
                                     nullptr)),
diff --git a/third_party/blink/renderer/core/frame/external.idl b/third_party/blink/renderer/core/frame/external.idl
index 977e4e0..3a7787d3 100644
--- a/third_party/blink/renderer/core/frame/external.idl
+++ b/third_party/blink/renderer/core/frame/external.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-[NoInterfaceObject]
+[Exposed=Window]
 interface External {
   void AddSearchProvider();
   void IsSearchProviderInstalled();
diff --git a/third_party/blink/renderer/core/frame/frame_overlay.h b/third_party/blink/renderer/core/frame/frame_overlay.h
index b77c3923..ea0555da 100644
--- a/third_party/blink/renderer/core/frame/frame_overlay.h
+++ b/third_party/blink/renderer/core/frame/frame_overlay.h
@@ -77,6 +77,8 @@
   // FrameOverlay is always the same size as the viewport.
   IntSize Size() const;
 
+  const Delegate* GetDelegate() const { return delegate_.get(); }
+
   // DisplayItemClient methods.
   String DebugName() const final { return "FrameOverlay"; }
   LayoutRect VisualRect() const override;
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index edb5a03..d15a9b14 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -219,22 +219,24 @@
           mojo::MakeRequest(&document_interface_broker).PassMessagePipe(),
           &old_frame, WebSandboxFlags::kNone, ParsedFeaturePolicy()));
   client->Bind(frame, std::move(owned_client));
+  std::unique_ptr<TestWebWidgetClient> widget_client;
   // Create a local root, if necessary.
   if (!frame->Parent()) {
+    widget_client = std::make_unique<TestWebWidgetClient>();
     // TODO(dcheng): The main frame widget currently has a special case.
     // Eliminate this once WebView is no longer a WebWidget.
-    WebWidgetClient* widget_client = frame->ViewImpl()->WidgetClient();
-    WebFrameWidget::CreateForMainFrame(widget_client, frame);
+    WebFrameWidget::CreateForMainFrame(widget_client.get(), frame);
   } else if (frame->Parent()->IsWebRemoteFrame()) {
-    auto widget_client = std::make_unique<TestWebWidgetClient>();
+    widget_client = std::make_unique<TestWebWidgetClient>();
     WebFrameWidget* frame_widget =
         WebFrameWidget::CreateForChildLocalRoot(widget_client.get(), frame);
     frame_widget->Resize(WebSize());
     // The WebWidget requires a LayerTreeView to be set, either by the
     // WebWidgetClient itself or by someone else. We do that here.
     frame_widget->SetLayerTreeView(widget_client->layer_tree_view());
-    client->BindWidgetClient(std::move(widget_client));
   }
+  if (widget_client)
+    client->BindWidgetClient(std::move(widget_client));
   return frame;
 }
 
@@ -311,8 +313,7 @@
     void (*update_settings_func)(WebSettings*)) {
   Reset();
 
-  InitializeWebView(web_view_client, web_widget_client,
-                    opener ? opener->View() : nullptr);
+  InitializeWebView(web_view_client, opener ? opener->View() : nullptr);
   if (update_settings_func)
     update_settings_func(web_view_->GetSettings());
 
@@ -325,9 +326,15 @@
       mojo::MakeRequest(&document_interface_broker).PassMessagePipe(), opener);
   web_frame_client->Bind(frame, std::move(owned_web_frame_client));
 
+  test_web_widget_client_ = CreateDefaultClientIfNeeded(
+      web_widget_client, owned_test_web_widget_client_);
   // TODO(dcheng): The main frame widget currently has a special case.
   // Eliminate this once WebView is no longer a WebWidget.
   blink::WebFrameWidget::CreateForMainFrame(test_web_widget_client_, frame);
+  // TODO(danakj): Make this part of attaching the main frame's WebFrameWidget.
+  web_view_->MainFrameWidget()->SetLayerTreeView(
+      test_web_widget_client_->layer_tree_view());
+
   // Set an initial size for subframes.
   if (frame->Parent())
     frame->FrameWidget()->Resize(WebSize());
@@ -371,7 +378,7 @@
     TestWebWidgetClient* web_widget_client) {
   Reset();
 
-  InitializeWebView(web_view_client, web_widget_client, nullptr);
+  InitializeWebView(web_view_client, nullptr);
 
   std::unique_ptr<TestWebRemoteFrameClient> owned_web_remote_frame_client;
   web_remote_frame_client = CreateDefaultClientIfNeeded(
@@ -384,6 +391,13 @@
     security_origin = SecurityOrigin::CreateUniqueOpaque();
   frame->GetFrame()->GetSecurityContext()->SetReplicatedOrigin(
       std::move(security_origin));
+
+  test_web_widget_client_ = CreateDefaultClientIfNeeded(
+      web_widget_client, owned_test_web_widget_client_);
+  // TODO(danakj): Remove this! Make WebViewImpl not need a WebWidgetClient when
+  // the main frame is remote.
+  web_view_->SetWebWidgetClient(test_web_widget_client_);
+
   return web_view_;
 }
 
@@ -419,15 +433,13 @@
 }
 
 void WebViewHelper::InitializeWebView(TestWebViewClient* web_view_client,
-                                      TestWebWidgetClient* widget_client,
                                       class WebView* opener) {
   test_web_view_client_ =
       CreateDefaultClientIfNeeded(web_view_client, owned_test_web_view_client_);
-  test_web_widget_client_ =
-      CreateDefaultClientIfNeeded(widget_client, owned_test_web_widget_client_);
-  web_view_ = static_cast<WebViewImpl*>(WebView::Create(
-      test_web_view_client_, test_web_widget_client_, /*is_hidden=*/false,
-      /*compositing_enabled=*/true, opener));
+  web_view_ = static_cast<WebViewImpl*>(
+      WebView::Create(test_web_view_client_,
+                      /*is_hidden=*/false,
+                      /*compositing_enabled=*/true, opener));
   web_view_->GetSettings()->SetJavaScriptEnabled(true);
   web_view_->GetSettings()->SetPluginsEnabled(true);
   // Enable (mocked) network loads of image URLs, as this simplifies
@@ -437,8 +449,6 @@
   // Consequently, all external image resources must be mocked.
   web_view_->GetSettings()->SetLoadsImagesAutomatically(true);
 
-  web_view_->MainFrameWidget()->SetLayerTreeView(
-      test_web_widget_client_->layer_tree_view());
   web_view_->SetDeviceScaleFactor(
       test_web_view_client_->GetScreenInfo().device_scale_factor);
   web_view_->SetDefaultPageScaleLimits(1, 4);
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index 5391b0e..bd4aa75 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -312,7 +312,6 @@
 
  private:
   void InitializeWebView(TestWebViewClient*,
-                         TestWebWidgetClient*,
                          class WebView* opener);
 
   WebViewImpl* web_view_;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 4ffa99c..0918eb6 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2695,6 +2695,20 @@
   }
 }
 
+const cc::Layer* LocalFrameView::RootCcLayer() const {
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    return paint_artifact_compositor_ ? paint_artifact_compositor_->RootLayer()
+                                      : nullptr;
+  }
+
+  if (const auto* root_graphics_layer =
+          frame_->GetPage()->GetVisualViewport().RootGraphicsLayer()) {
+    return root_graphics_layer->CcLayer();
+  }
+  return nullptr;
+}
+
 void LocalFrameView::PushPaintArtifactToCompositor(
     CompositorElementIdSet& composited_element_ids) {
   TRACE_EVENT0("blink", "LocalFrameView::pushPaintArtifactToCompositor");
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index d74113a..c29b40b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -49,6 +49,10 @@
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/skia/include/core/SkColor.h"
 
+namespace cc {
+class Layer;
+}
+
 namespace blink {
 
 class AXObjectCache;
@@ -670,6 +674,8 @@
     return paint_artifact_compositor_.get();
   }
 
+  const cc::Layer* RootCcLayer() const;
+
   enum ForceThrottlingInvalidationBehavior {
     kDontForceThrottlingInvalidation,
     kForceThrottlingInvalidation
diff --git a/third_party/blink/renderer/core/frame/page_scale_constraints_set.cc b/third_party/blink/renderer/core/frame/page_scale_constraints_set.cc
index 9911e24..148e0a7 100644
--- a/third_party/blink/renderer/core/frame/page_scale_constraints_set.cc
+++ b/third_party/blink/renderer/core/frame/page_scale_constraints_set.cc
@@ -64,7 +64,7 @@
 
 void PageScaleConstraintsSet::UpdatePageDefinedConstraints(
     const ViewportDescription& description,
-    Length legacy_fallback_width) {
+    const Length& legacy_fallback_width) {
   page_defined_constraints_ =
       description.Resolve(FloatSize(icb_size_), legacy_fallback_width);
 
diff --git a/third_party/blink/renderer/core/frame/page_scale_constraints_set.h b/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
index 5a666d2..6144ae4 100644
--- a/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
+++ b/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
@@ -68,7 +68,7 @@
     return page_defined_constraints_;
   }
   void UpdatePageDefinedConstraints(const ViewportDescription&,
-                                    Length legacy_fallback_width);
+                                    const Length& legacy_fallback_width);
   void AdjustForAndroidWebViewQuirks(const ViewportDescription&,
                                      int layout_fallback_width,
                                      float device_scale_factor,
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index bba7fb6..8831aaf 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -11,13 +11,18 @@
 
 WebViewFrameWidget::WebViewFrameWidget(WebWidgetClient& client,
                                        WebViewImpl& web_view)
-    : WebFrameWidgetBase(client),
-      web_view_(&web_view),
-      self_keep_alive_(this) {}
+    : WebFrameWidgetBase(client), web_view_(&web_view), self_keep_alive_(this) {
+  // TODO(danakj): SetLayerTreeView() here as well, then we can Close() the
+  // WebViewImpl's widget bits in Close().
+  web_view_->SetWebWidgetClient(&client);
+}
 
 WebViewFrameWidget::~WebViewFrameWidget() = default;
 
 void WebViewFrameWidget::Close() {
+  // TODO(danakj): Close() the WebViewImpl here, when we reset the LayerTreeView
+  // in the constructor.
+  web_view_->SetWebWidgetClient(nullptr);
   web_view_ = nullptr;
   WebFrameWidgetBase::Close();
 
diff --git a/third_party/blink/renderer/core/frame/window_event_handlers.h b/third_party/blink/renderer/core/frame/window_event_handlers.h
index c5b06f34..d1c9a72 100644
--- a/third_party/blink/renderer/core/frame/window_event_handlers.h
+++ b/third_party/blink/renderer/core/frame/window_event_handlers.h
@@ -52,6 +52,8 @@
   DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(pagehide, kPagehide);
   DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(pageshow, kPageshow);
   DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(popstate, kPopstate);
+  DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(portalactivate,
+                                                kPortalactivate);
   DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(rejectionhandled,
                                                 kRejectionhandled);
   DEFINE_STATIC_WINDOW_ATTRIBUTE_EVENT_LISTENER(storage, kStorage);
diff --git a/third_party/blink/renderer/core/frame/window_event_handlers.idl b/third_party/blink/renderer/core/frame/window_event_handlers.idl
index f8c25fb..2ee41ea 100644
--- a/third_party/blink/renderer/core/frame/window_event_handlers.idl
+++ b/third_party/blink/renderer/core/frame/window_event_handlers.idl
@@ -46,6 +46,7 @@
     attribute EventHandler onpagehide;
     attribute EventHandler onpageshow;
     attribute EventHandler onpopstate;
+    [RuntimeEnabled=Portals] attribute EventHandler onportalactivate;
     attribute EventHandler onrejectionhandled;
     attribute EventHandler onstorage;
     attribute EventHandler onunhandledrejection;
diff --git a/third_party/blink/renderer/core/html/forms/form_data.cc b/third_party/blink/renderer/core/html/forms/form_data.cc
index b2b9745..63256a745 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.cc
+++ b/third_party/blink/renderer/core/html/forms/form_data.cc
@@ -96,12 +96,12 @@
 
 FormData* FormData::Create(HTMLFormElement* form,
                            ExceptionState& exception_state) {
-  auto* form_data = MakeGarbageCollected<FormData>();
   // TODO(tkent): Null check should be unnecessary.  We should remove
   // LegacyInterfaceTypeChecking from form_data.idl.  crbug.com/561338
   if (!form)
-    return form_data;
-  if (!form->ConstructEntryList(nullptr, *form_data)) {
+    return MakeGarbageCollected<FormData>();
+  FormData* form_data = form->ConstructEntryList(nullptr, UTF8Encoding());
+  if (!form_data) {
     DCHECK(RuntimeEnabledFeatures::FormDataEventEnabled());
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The form is constructing entry list.");
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
index aae72bb..9dee6ad0 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
@@ -24,12 +24,8 @@
 
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
 
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/frame/local_frame_client.h"
-#include "third_party/blink/renderer/core/html/forms/form_controller.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
-#include "third_party/blink/renderer/core/page/chrome_client.h"
 
 namespace blink {
 
@@ -120,21 +116,6 @@
 
 HTMLFormControlElementWithState::~HTMLFormControlElementWithState() = default;
 
-Node::InsertionNotificationRequest
-HTMLFormControlElementWithState::InsertedInto(ContainerNode& insertion_point) {
-  if (insertion_point.isConnected() && !ContainingShadowRoot())
-    GetDocument().GetFormController().InvalidateStatefulFormControlList();
-  return HTMLFormControlElement::InsertedInto(insertion_point);
-}
-
-void HTMLFormControlElementWithState::RemovedFrom(
-    ContainerNode& insertion_point) {
-  if (insertion_point.isConnected() && !ContainingShadowRoot() &&
-      !insertion_point.ContainingShadowRoot())
-    GetDocument().GetFormController().InvalidateStatefulFormControlList();
-  HTMLFormControlElement::RemovedFrom(insertion_point);
-}
-
 bool HTMLFormControlElementWithState::ShouldAutocomplete() const {
   if (!Form())
     return true;
@@ -284,27 +265,15 @@
   setAttribute(html_names::kAutocompleteAttr, AtomicString(autocomplete_value));
 }
 
-void HTMLFormControlElementWithState::NotifyFormStateChanged() {
-  // This can be called during fragment parsing as a result of option
-  // selection before the document is active (or even in a frame).
-  if (!GetDocument().IsActive())
-    return;
-  GetDocument().GetFrame()->Client()->DidUpdateCurrentHistoryItem();
-}
-
 bool HTMLFormControlElementWithState::ShouldSaveAndRestoreFormControlState()
     const {
   // We don't save/restore control state in a form with autocomplete=off.
   return isConnected() && ShouldAutocomplete();
 }
 
-FormControlState HTMLFormControlElementWithState::SaveFormControlState() const {
-  return FormControlState();
-}
-
 void HTMLFormControlElementWithState::FinishParsingChildren() {
   HTMLFormControlElement::FinishParsingChildren();
-  GetDocument().GetFormController().RestoreControlStateFor(*this);
+  ListedElement::TakeStateAndRestore();
 }
 
 bool HTMLFormControlElementWithState::IsFormControlElementWithState() const {
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
index 6d42097b..3ce72691 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
@@ -30,8 +30,6 @@
 
 namespace blink {
 
-class FormControlState;
-
 class CORE_EXPORT HTMLFormControlElementWithState
     : public HTMLFormControlElement {
  public:
@@ -44,11 +42,8 @@
   String IDLExposedAutofillValue() const;
   void setIDLExposedAutofillValue(const String& autocomplete_value);
 
-  virtual bool ShouldSaveAndRestoreFormControlState() const;
-  virtual FormControlState SaveFormControlState() const;
-  // The specified FormControlState must have at least one string value.
-  virtual void RestoreFormControlState(const FormControlState&) {}
-  void NotifyFormStateChanged();
+  // ListedElement override:
+  bool ShouldSaveAndRestoreFormControlState() const override;
 
   bool UserHasEditedTheField() const { return user_has_edited_the_field_; }
   // This is only used in tests, to fake the user's action
@@ -59,8 +54,6 @@
   HTMLFormControlElementWithState(const QualifiedName& tag_name, Document&);
 
   void FinishParsingChildren() override;
-  InsertionNotificationRequest InsertedInto(ContainerNode&) override;
-  void RemovedFrom(ContainerNode&) override;
   bool IsFormControlElementWithState() const final;
 
  private:
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 7cd08ac1..d3416e3 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -421,11 +421,13 @@
   }
 }
 
-bool HTMLFormElement::ConstructEntryList(HTMLFormControlElement* submit_button,
-                                         FormData& form_data) {
+FormData* HTMLFormElement::ConstructEntryList(
+    HTMLFormControlElement* submit_button,
+    const WTF::TextEncoding& encoding) {
   if (is_constructing_entry_list_) {
-    return false;
+    return nullptr;
   }
+  auto& form_data = *MakeGarbageCollected<FormData>(encoding);
   base::AutoReset<bool> entry_list_scope(&is_constructing_entry_list_, true);
   if (submit_button)
     submit_button->SetActivatedSubmit(true);
@@ -445,7 +447,7 @@
 
   if (submit_button)
     submit_button->SetActivatedSubmit(false);
-  return true;
+  return &form_data;
 }
 
 void HTMLFormElement::ScheduleFormSubmission(FormSubmission* submission) {
@@ -634,16 +636,8 @@
     ListedElement::List& elements) const {
   elements.clear();
   for (HTMLElement& element : Traversal<HTMLElement>::StartsAfter(root)) {
-    ListedElement* listed_element = nullptr;
-    if (element.IsFormControlElement())
-      listed_element = ToHTMLFormControlElement(&element);
-    else if (element.IsFormAssociatedCustomElement())
-      listed_element = &element.EnsureElementInternals();
-    else if (auto* object = ToHTMLObjectElementOrNull(element))
-      listed_element = object;
-    else
-      continue;
-    if (listed_element->Form() == this)
+    ListedElement* listed_element = ListedElement::From(element);
+    if (listed_element && listed_element->Form() == this)
       elements.push_back(listed_element);
   }
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h
index 10274dc..541780e 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -112,9 +112,9 @@
 
   // 'construct the entry list'
   // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set
-  // Returns false if this form is already running this function.
-  bool ConstructEntryList(HTMLFormControlElement* submit_button,
-                          FormData& form_data);
+  // Returns nullptr if this form is already running this function.
+  FormData* ConstructEntryList(HTMLFormControlElement* submit_button,
+                               const WTF::TextEncoding& encoding);
 
   unsigned UniqueRendererFormId() const { return unique_renderer_form_id_; }
 
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.cc b/third_party/blink/renderer/core/html/forms/listed_element.cc
index bf31b0b7..cc6aa2d2 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -28,10 +28,14 @@
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/id_target_observer.h"
 #include "third_party/blink/renderer/core/dom/node_traversal.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/html/custom/element_internals.h"
+#include "third_party/blink/renderer/core/html/forms/form_controller.h"
 #include "third_party/blink/renderer/core/html/forms/html_data_list_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_field_set_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_legend_element.h"
 #include "third_party/blink/renderer/core/html/forms/validity_state.h"
@@ -114,6 +118,13 @@
   FieldSetAncestorsSetNeedsValidityCheck(&insertion_point);
   DisabledStateMightBeChanged();
 
+  if (IsFormControlElementWithState() && insertion_point.isConnected() &&
+      !element->ContainingShadowRoot()) {
+    element->GetDocument()
+        .GetFormController()
+        .InvalidateStatefulFormControlList();
+  }
+
   // Trigger for elements outside of forms.
   if (!form_ && insertion_point.isConnected())
     element->GetDocument().DidAssociateFormControl(element);
@@ -141,6 +152,14 @@
     ResetFormOwner();
 
   DisabledStateMightBeChanged();
+
+  if (IsFormControlElementWithState() && insertion_point.isConnected() &&
+      !element->ContainingShadowRoot() &&
+      !insertion_point.ContainingShadowRoot()) {
+    element->GetDocument()
+        .GetFormController()
+        .InvalidateStatefulFormControlList();
+  }
 }
 
 HTMLFormElement* ListedElement::FindAssociatedForm(
@@ -570,6 +589,34 @@
   return ancestor_disabled_state_ == AncestorDisabledState::kDisabled;
 }
 
+bool ListedElement::ShouldSaveAndRestoreFormControlState() const {
+  return false;
+}
+
+FormControlState ListedElement::SaveFormControlState() const {
+  return FormControlState();
+}
+
+void ListedElement::RestoreFormControlState(const FormControlState& state) {}
+
+void ListedElement::NotifyFormStateChanged() {
+  Document& doc = ToHTMLElement(*this).GetDocument();
+  // This can be called during fragment parsing as a result of option
+  // selection before the document is active (or even in a frame).
+  if (!doc.IsActive())
+    return;
+  doc.GetFrame()->Client()->DidUpdateCurrentHistoryItem();
+}
+
+void ListedElement::TakeStateAndRestore() {
+  if (IsFormControlElementWithState()) {
+    ToHTMLElement(*this)
+        .GetDocument()
+        .GetFormController()
+        .RestoreControlStateFor(ToHTMLFormControlElementWithState(*this));
+  }
+}
+
 void ListedElement::SetFormAttributeTargetObserver(
     FormAttributeTargetObserver* new_observer) {
   if (form_attribute_target_observer_)
@@ -605,6 +652,19 @@
   return false;
 }
 
+ListedElement* ListedElement::From(Element& element) {
+  auto* html_element = ToHTMLElementOrNull(element);
+  if (!html_element)
+    return nullptr;
+  if (html_element->IsFormControlElement())
+    return ToHTMLFormControlElement(&element);
+  if (html_element->IsFormAssociatedCustomElement())
+    return &element.EnsureElementInternals();
+  if (auto* object = ToHTMLObjectElementOrNull(html_element))
+    return object;
+  return nullptr;
+}
+
 const HTMLElement& ToHTMLElement(const ListedElement& listed_element) {
   if (listed_element.IsFormControlElement())
     return ToHTMLFormControlElement(listed_element);
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.h b/third_party/blink/renderer/core/html/forms/listed_element.h
index 5d19ccfd..075e096 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.h
+++ b/third_party/blink/renderer/core/html/forms/listed_element.h
@@ -34,7 +34,9 @@
 
 class ContainerNode;
 class Document;
+class Element;
 class FormAttributeTargetObserver;
+class FormControlState;
 class FormData;
 class HTMLElement;
 class HTMLFormElement;
@@ -46,6 +48,10 @@
 class CORE_EXPORT ListedElement : public GarbageCollectedMixin {
  public:
   virtual ~ListedElement();
+  // Returns a valid ListedElement pointer if the specified element is an
+  // instance of a ListedElement subclass, or a form-associated custom element.
+  // Returns nullptr otherwise.
+  static ListedElement* From(Element& element);
 
   static HTMLFormElement* FindAssociatedForm(const HTMLElement*,
                                              const AtomicString& form_id,
@@ -141,6 +147,16 @@
   // https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
   bool IsActuallyDisabled() const;
 
+  virtual bool ShouldSaveAndRestoreFormControlState() const;
+  virtual FormControlState SaveFormControlState() const;
+  // The specified FormControlState must have at least one string value.
+  virtual void RestoreFormControlState(const FormControlState& state);
+  // This should be called whenever an element supports state restore changes
+  // its state.
+  void NotifyFormStateChanged();
+  // This should be called in Element::FinishParsingChildren() override.
+  void TakeStateAndRestore();
+
   void Trace(blink::Visitor*) override;
 
  protected:
diff --git a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
index 7c8e72c..62b9ee0 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
@@ -180,7 +180,7 @@
 
     // We'd like to remove line-height if it's unnecessary because
     // overflow:scroll clips editing text by line-height.
-    Length logical_height = start_style.LogicalHeight();
+    const Length& logical_height = start_style.LogicalHeight();
     // Here, we remove line-height if the INPUT fixed height is taller than the
     // line-height.  It's not the precise condition because logicalHeight
     // includes border and padding if box-sizing:border-box, and there are cases
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index d718fd2..97b3c43e 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -225,6 +225,7 @@
     "onpointerrawmove",
     "onpointerup",
     "onpopstate",
+    "onportalactivate",
     "onprogress",
     "onratechange",
     "onreset",
diff --git a/third_party/blink/renderer/core/html/html_body_element.cc b/third_party/blink/renderer/core/html/html_body_element.cc
index 86a9973..d668722 100644
--- a/third_party/blink/renderer/core/html/html_body_element.cc
+++ b/third_party/blink/renderer/core/html/html_body_element.cc
@@ -211,6 +211,11 @@
     GetDocument().SetWindowAttributeEventListener(
         event_type_names::kLanguagechange,
         CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+  } else if (RuntimeEnabledFeatures::PortalsEnabled() &&
+             name == kOnportalactivateAttr) {
+    GetDocument().SetWindowAttributeEventListener(
+        event_type_names::kPortalactivate,
+        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
   } else {
     HTMLElement::ParseAttribute(params);
   }
diff --git a/third_party/blink/renderer/core/html/html_frame_set_element.cc b/third_party/blink/renderer/core/html/html_frame_set_element.cc
index 3a3503d4..31597f36 100644
--- a/third_party/blink/renderer/core/html/html_frame_set_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_set_element.cc
@@ -205,6 +205,11 @@
     GetDocument().SetWindowAttributeEventListener(
         event_type_names::kLanguagechange,
         CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+  } else if (RuntimeEnabledFeatures::PortalsEnabled() &&
+             name == kOnportalactivateAttr) {
+    GetDocument().SetWindowAttributeEventListener(
+        event_type_names::kPortalactivate,
+        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
   } else {
     HTMLElement::ParseAttribute(params);
   }
diff --git a/third_party/blink/renderer/core/html/html_image_fallback_helper.cc b/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
index 8f1f32cc..ac14855 100644
--- a/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
+++ b/third_party/blink/renderer/core/html/html_image_fallback_helper.cc
@@ -36,8 +36,8 @@
 }
 
 static bool ImageSmallerThanAltImage(int pixels_for_alt_image,
-                                     const Length width,
-                                     const Length height) {
+                                     const Length& width,
+                                     const Length& height) {
   // We don't have a layout tree so can't compute the size of an image
   // relative dimensions - so we just assume we should display the alt image.
   if (!width.IsFixed() && !height.IsFixed())
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index b43bb3e..cf4fb69 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -501,6 +501,7 @@
       muted_(false),
       paused_(true),
       seeking_(false),
+      paused_by_context_paused_(false),
       sent_stalled_event_(false),
       ignore_preload_none_(false),
       text_tracks_visible_(false),
@@ -3578,6 +3579,20 @@
     GetLayoutObject()->SetShouldDoFullPaintInvalidation();
 }
 
+void HTMLMediaElement::ContextPaused(PauseState pause_state) {
+  if (pause_state == PauseState::kFrozen && playing_) {
+    paused_by_context_paused_ = true;
+    pause();
+  }
+}
+
+void HTMLMediaElement::ContextUnpaused() {
+  if (paused_by_context_paused_) {
+    paused_by_context_paused_ = false;
+    Play();
+  }
+}
+
 void HTMLMediaElement::ContextDestroyed(ExecutionContext*) {
   BLINK_MEDIA_LOG << "contextDestroyed(" << (void*)this << ")";
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 3626169..7f0f0f9 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -35,7 +35,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/media/media_controls.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
@@ -383,6 +383,8 @@
   bool IsInteractiveContent() const final;
 
   // PausableObject functions.
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
   void ContextDestroyed(ExecutionContext*) override;
 
   virtual void UpdateDisplayState() {}
@@ -498,8 +500,6 @@
   // This does not stop autoplay visibility observation.
   void PauseInternal();
 
-  void AllowVideoRendering();
-
   void UpdateVolume();
   void UpdatePlayState();
   bool PotentiallyPlaying() const;
@@ -654,6 +654,7 @@
   bool muted_ : 1;
   bool paused_ : 1;
   bool seeking_ : 1;
+  bool paused_by_context_paused_ : 1;
 
   // data has not been loaded since sending a "stalled" event
   bool sent_stalled_event_ : 1;
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index 317d3741..9f418253 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -131,6 +131,10 @@
     return !!Media()->lazy_load_visibility_observer_;
   }
 
+  ExecutionContext* GetExecutionContext() const {
+    return &dummy_page_holder_->GetDocument();
+  }
+
  private:
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
   Persistent<HTMLMediaElement> media_;
@@ -467,4 +471,18 @@
   EXPECT_FALSE(Media()->GetDocument().GetTiming().DomInteractive().is_null());
 }
 
+TEST_P(HTMLMediaElementTest, ContextPaused) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  Media()->Play();
+
+  test::RunPendingTasks();
+  SetReadyState(HTMLMediaElement::kHaveFutureData);
+
+  EXPECT_FALSE(Media()->paused());
+  GetExecutionContext()->PausePausableObjects(PauseState::kFrozen);
+  EXPECT_TRUE(Media()->paused());
+  GetExecutionContext()->UnpausePausableObjects();
+  EXPECT_FALSE(Media()->paused());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
index 8ab20da..72d750d 100644
--- a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
+++ b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.cc
@@ -64,13 +64,18 @@
   return false;
 }
 
-void ReportUnsizedMediaViolation(const LayoutObject* layout_object) {
+void ReportUnsizedMediaViolation(const LayoutObject* layout_object,
+                                 bool send_report) {
   const ComputedStyle& style = layout_object->StyleRef();
   if (!style.LogicalWidth().IsSpecified() &&
       !style.LogicalHeight().IsSpecified()) {
-    layout_object->GetDocument().ReportFeaturePolicyViolation(
-        mojom::FeaturePolicyFeature::kUnsizedMedia,
-        mojom::FeaturePolicyDisposition::kEnforce);
+    layout_object->GetDocument().CountPotentialFeaturePolicyViolation(
+        mojom::FeaturePolicyFeature::kUnsizedMedia);
+    if (send_report) {
+      layout_object->GetDocument().ReportFeaturePolicyViolation(
+          mojom::FeaturePolicyFeature::kUnsizedMedia,
+          mojom::FeaturePolicyDisposition::kEnforce);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
index 847f8bd..be88a6eb0 100644
--- a/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
+++ b/third_party/blink/renderer/core/html/media/media_element_parser_helpers.h
@@ -27,7 +27,12 @@
 // are not in an image or media document; returns false otherwise.
 bool IsMediaElement(const Element* element);
 
-void ReportUnsizedMediaViolation(const LayoutObject* layout_object);
+// When |layout_object| is not properly styled (according to
+// FeaturePolocyFeature::kUnsizedMedia) this invocation counts a potential
+// violation. If |send_report| is set, then an actual violation report is
+// generated.
+void ReportUnsizedMediaViolation(const LayoutObject* layout_object,
+                                 bool send_report);
 
 }  // namespace media_element_parser_helpers
 
diff --git a/third_party/blink/renderer/core/html/parser/OWNERS b/third_party/blink/renderer/core/html/parser/OWNERS
index d9dc40ec..23b641c 100644
--- a/third_party/blink/renderer/core/html/parser/OWNERS
+++ b/third_party/blink/renderer/core/html/parser/OWNERS
@@ -1,5 +1,5 @@
 kouhei@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
 csharrison@chromium.org
 
 # TEAM: loading-dev@chromium.org
diff --git a/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html b/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
index 6142383..dae4b5c1 100644
--- a/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
+++ b/third_party/blink/renderer/core/inspector/InspectorOverlayPage.html
@@ -34,6 +34,14 @@
     padding: 0;
     font-size: 13px;
     color: #222;
+    --arrow-width: 15px;
+    --arrow-height: 8px;
+    --shadow-up: 5px;
+    --shadow-down: -5px;
+    --shadow-direction: var(--shadow-up);
+    --arrow-up: polygon(0 0, 100% 0, 50% 100%);
+    --arrow-down: polygon(50% 0, 0 100%, 100% 100%);
+    --arrow: var(--arrow-up);
 }
 
 body.platform-linux {
@@ -145,8 +153,7 @@
     display: none !important;
 }
 
-.tooltip-content,
-.material-tooltip-arrow {
+.tooltip-content {
     position: absolute;
     z-index: 10;
     -webkit-user-select: none;
@@ -166,6 +173,18 @@
     filter: drop-shadow(0 2px 4px rgba(0,0,0,0.35));
 }
 
+.tooltip-content::after {
+    content: "";
+    background: white;
+    width: var(--arrow-width);
+    height: var(--arrow-height);
+    clip-path: var(--arrow);
+    position: absolute;
+    top: var(--arrow-top);
+    left: var(--arrow-left);
+    visibility: var(--arrow-visibility);
+}
+
 .element-info {
     display: flex;
     flex-direction: column;
@@ -225,19 +244,6 @@
     border: 1px solid rgba(128, 128, 128, 0.6);
 }
 
-.material-tooltip-arrow {
-    border: solid;
-    border-color: white transparent;
-    border-width: 0 8px 8px 8px;
-    z-index: 2;
-    margin-top: 1px;
-}
-
-.material-tooltip-arrow.tooltip-arrow-top {
-    border-width: 8px 8px 0 8px;
-    margin-top: -1px;
-}
-
 .element-description {
     flex: 1 1;
     font-weight: bold;
@@ -694,7 +700,7 @@
             arrowX = Number.constrain(containerMinX, xFromLeftBound, xFromRightBound);
     }
     // Hide arrow if element is completely off the sides of the page.
-    const arrowHidden = arrowX < containerMinX || arrowX > containerMaxX;
+    const arrowHidden = !withArrow || arrowX < containerMinX || arrowX > containerMaxX;
 
     let boxX = arrowX - arrowInset;
     boxX = Number.constrain(boxX, pageMargin, canvasWidth - titleWidth - pageMargin);
@@ -710,16 +716,14 @@
 
     tooltipContent.style.top = boxY + "px";
     tooltipContent.style.left = boxX + "px";
-    if (!withArrow)
+    tooltipContent.style.setProperty('--arrow-visibility', arrowHidden ? 'hidden' : 'visible');
+    if (arrowHidden)
         return;
 
-    const tooltipArrow = tooltipContainer.createChild("div", "material-tooltip-arrow");
-    tooltipArrow.classList.toggle("hidden", arrowHidden);
-    if (!arrowHidden) {
-        tooltipArrow.classList.toggle("tooltip-arrow-top", onTop);
-        tooltipArrow.style.top = (onTop ? boxY + titleHeight : boxY - arrowHalfWidth) + "px";
-        tooltipArrow.style.left = arrowX + "px";
-    }
+    tooltipContent.style.setProperty('--arrow', onTop ? 'var(--arrow-up)' : 'var(--arrow-down)');
+    tooltipContent.style.setProperty('--shadow-direction', onTop ? 'var(--shadow-up)' : 'var(--shadow-down)');
+    tooltipContent.style.setProperty('--arrow-top', (onTop ? titleHeight : -arrowHalfWidth) + 'px');
+    tooltipContent.style.setProperty('--arrow-left', (arrowX - boxX) + 'px');
 }
 
 function _drawRulers(context, bounds, rulerAtRight, rulerAtBottom)
@@ -1057,17 +1061,9 @@
         scrollY: 0
     });
     drawHighlight(
-        {
-            paths: [{path: []}],
-            elementInfo: {
-                tagName:"a",
-                className:"class",
-                nodeWidth: 800,
-                nodeHeight: 600,
-                style: {"color": "#FF0000FF", "background-color": "#FFFF107F", "font-family": "serif", "font-size": "12px", "font-weight": "normal"},
-                contrast: { fontWidth: "normal", fontSize: "12px", backgroundColor: "#FFFFFFFF" }
-            }
-        });
+        {"paths":[{"path":["M",122,133.796875,"L",822,133.796875,"L",822,208.796875,"L",122,208.796875,"Z"], "fillColor":"rgba(111, 168, 220, 0.658823529411765)","name":"content"},
+        {"path":["M",122,113.796875,"L",822,113.796875,"L",822,228.796875,"L",122,228.796875,"Z"],"fillColor":"rgba(246, 178, 107, 0.66)","name":"margin"}],"showRulers":false,"showExtensionLines":false,
+        "elementInfo":{"tagName":"p","idValue":"","nodeWidth":"700","nodeHeight":"75","style":{"color":"#FFFFFFFF","font-family":"\"Product Sans\", \"Open Sans\", Roboto, Arial","font-size":"20px","line-height":"25px","padding":"0px","margin":"20px 0px","background-color":"#00000000"},"contrast":{"fontSize":"20px","fontWeight":"400","backgroundColor":"#F9B826BF"}}});
 }
 
 window.addEventListener("DOMContentLoaded", onLoaded);
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 5d7f1f9..1b83eb5 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -4758,7 +4758,7 @@
   # Fired when user asks to capture screenshot of some area on the page.
   event screenshotRequested
     parameters
-      # Viewport to capture, in CSS.
+      # Viewport to capture, in device independent pixels (dip).
       Page.Viewport viewport
 
 # Actions and events related to the inspected page belong to the page domain.
@@ -4928,17 +4928,19 @@
       number clientHeight
       # Scale relative to the ideal viewport (size at width=device-width).
       number scale
+      # Page zoom factor (CSS to device independent pixels ratio).
+      optional number zoom
 
   # Viewport for capturing screenshot.
   type Viewport extends object
     properties
-      # X offset in CSS pixels.
+      # X offset in device independent pixels (dip).
       number x
-      # Y offset in CSS pixels
+      # Y offset in device independent pixels (dip).
       number y
-      # Rectangle width in CSS pixels
+      # Rectangle width in device independent pixels (dip).
       number width
-      # Rectangle height in CSS pixels
+      # Rectangle height in device independent pixels (dip).
       number height
       # Page scale factor.
       number scale
diff --git a/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.cc b/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.cc
index c6d1ebe..d7f42bc 100644
--- a/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.cc
@@ -49,8 +49,6 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
-#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
@@ -70,8 +68,8 @@
 using protocol::Response;
 unsigned InspectorLayerTreeAgent::last_snapshot_id_;
 
-inline String IdForLayer(const GraphicsLayer* graphics_layer) {
-  return String::Number(graphics_layer->CcLayer()->id());
+inline String IdForLayer(const cc::Layer* layer) {
+  return String::Number(layer->id());
 }
 
 static std::unique_ptr<protocol::DOM::Rect> BuildObjectForRect(
@@ -97,20 +95,18 @@
 }
 
 static std::unique_ptr<Array<protocol::LayerTree::ScrollRect>>
-BuildScrollRectsForLayer(GraphicsLayer* graphics_layer,
-                         bool report_wheel_scrollers) {
+BuildScrollRectsForLayer(const cc::Layer* layer, bool report_wheel_scrollers) {
   std::unique_ptr<Array<protocol::LayerTree::ScrollRect>> scroll_rects =
       Array<protocol::LayerTree::ScrollRect>::create();
-  cc::Layer* cc_layer = graphics_layer->CcLayer();
   const cc::Region& non_fast_scrollable_rects =
-      cc_layer->non_fast_scrollable_region();
+      layer->non_fast_scrollable_region();
   for (const gfx::Rect& rect : non_fast_scrollable_rects) {
     scroll_rects->addItem(BuildScrollRect(
         IntRect(rect),
         protocol::LayerTree::ScrollRect::TypeEnum::RepaintsOnScroll));
   }
   const cc::Region& touch_event_handler_region =
-      cc_layer->touch_action_region().region();
+      layer->touch_action_region().region();
 
   for (const gfx::Rect& rect : touch_event_handler_region) {
     scroll_rects->addItem(BuildScrollRect(
@@ -120,8 +116,8 @@
   if (report_wheel_scrollers) {
     scroll_rects->addItem(BuildScrollRect(
         // TODO(yutak): This truncates the floating point position to integers.
-        gfx::Rect(cc_layer->position().x(), cc_layer->position().y(),
-                  cc_layer->bounds().width(), cc_layer->bounds().height()),
+        gfx::Rect(layer->position().x(), layer->position().y(),
+                  layer->bounds().width(), layer->bounds().height()),
         protocol::LayerTree::ScrollRect::TypeEnum::WheelEventHandler));
   }
   return scroll_rects->length() ? std::move(scroll_rects) : nullptr;
@@ -129,20 +125,19 @@
 
 // TODO(flackr): We should be getting the sticky position constraints from the
 // property tree once blink is able to access them. https://crbug.com/754339
-static GraphicsLayer* FindLayerByElementId(GraphicsLayer* root,
-                                           CompositorElementId element_id) {
-  if (root->CcLayer()->element_id() == element_id)
+static const cc::Layer* FindLayerByElementId(const cc::Layer* root,
+                                             CompositorElementId element_id) {
+  if (root->element_id() == element_id)
     return root;
-  for (wtf_size_t i = 0, size = root->Children().size(); i < size; ++i) {
-    if (GraphicsLayer* layer =
-            FindLayerByElementId(root->Children()[i], element_id))
+  for (auto child : root->children()) {
+    if (const auto* layer = FindLayerByElementId(child.get(), element_id))
       return layer;
   }
   return nullptr;
 }
 
 static std::unique_ptr<protocol::LayerTree::StickyPositionConstraint>
-BuildStickyInfoForLayer(GraphicsLayer* root, cc::Layer* layer) {
+BuildStickyInfoForLayer(const cc::Layer* root, const cc::Layer* layer) {
   cc::LayerStickyPositionConstraint constraints =
       layer->sticky_position_constraint();
   if (!constraints.is_sticky)
@@ -165,14 +160,12 @@
     constraints_obj->setNearestLayerShiftingStickyBox(String::Number(
         FindLayerByElementId(root,
                              constraints.nearest_element_shifting_sticky_box)
-            ->CcLayer()
             ->id()));
   }
   if (constraints.nearest_element_shifting_containing_block) {
     constraints_obj->setNearestLayerShiftingContainingBlock(String::Number(
         FindLayerByElementId(
             root, constraints.nearest_element_shifting_containing_block)
-            ->CcLayer()
             ->id()));
   }
 
@@ -180,60 +173,73 @@
 }
 
 static std::unique_ptr<protocol::LayerTree::Layer> BuildObjectForLayer(
-    GraphicsLayer* root,
-    GraphicsLayer* graphics_layer,
-    int node_id,
+    const cc::Layer* root,
+    const cc::Layer* layer,
     bool report_wheel_event_listeners) {
-  cc::Layer* cc_layer = graphics_layer->CcLayer();
+  bool using_layer_list =
+      RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled();
+
+  // When the front-end doesn't show internal layers, it will use the the first
+  // DrawsContent layer as the root of the shown layer tree. This doesn't work
+  // for layer list because the non-DrawsContent root layer is the parent of
+  // all DrawsContent layers. We have to cheat the front-end by setting
+  // drawsContent to true for the root layer.
+  bool draws_content =
+      (using_layer_list && root == layer) || layer->DrawsContent();
+
   std::unique_ptr<protocol::LayerTree::Layer> layer_object =
       protocol::LayerTree::Layer::create()
-          .setLayerId(IdForLayer(graphics_layer))
-          .setOffsetX(cc_layer->position().x())
-          .setOffsetY(cc_layer->position().y())
-          .setWidth(cc_layer->bounds().width())
-          .setHeight(cc_layer->bounds().height())
-          .setPaintCount(graphics_layer->PaintCount())
-          .setDrawsContent(cc_layer->DrawsContent())
+          .setLayerId(IdForLayer(layer))
+          .setOffsetX(using_layer_list ? 0 : layer->position().x())
+          .setOffsetY(using_layer_list ? 0 : layer->position().y())
+          .setWidth(layer->bounds().width())
+          .setHeight(layer->bounds().height())
+          .setPaintCount(layer->paint_count())
+          .setDrawsContent(draws_content)
           .build();
 
-  if (node_id)
+  if (auto node_id = layer->owner_node_id())
     layer_object->setBackendNodeId(node_id);
 
-  GraphicsLayer* parent = graphics_layer->Parent();
-  if (parent)
+  if (const auto* parent = layer->parent())
     layer_object->setParentLayerId(IdForLayer(parent));
-  if (!graphics_layer->ContentsAreVisible())
-    layer_object->setInvisible(true);
-  const TransformationMatrix& transform = graphics_layer->Transform();
+
+  gfx::Transform transform;
+  gfx::Point3F transform_origin;
+  if (using_layer_list) {
+    transform = layer->ScreenSpaceTransform();
+  } else {
+    transform = layer->transform();
+    transform_origin = layer->transform_origin();
+  }
+
   if (!transform.IsIdentity()) {
-    TransformationMatrix::FloatMatrix4 flattened_matrix;
-    transform.ToColumnMajorFloatArray(flattened_matrix);
-    std::unique_ptr<Array<double>> transform_array = Array<double>::create();
-    for (size_t i = 0; i < base::size(flattened_matrix); ++i)
-      transform_array->addItem(flattened_matrix[i]);
+    auto transform_array = Array<double>::create();
+    for (int col = 0; col < 4; ++col) {
+      for (int row = 0; row < 4; ++row)
+        transform_array->addItem(transform.matrix().get(row, col));
+    }
     layer_object->setTransform(std::move(transform_array));
-    const FloatPoint3D& transform_origin = graphics_layer->TransformOrigin();
     // FIXME: rename these to setTransformOrigin*
-    if (cc_layer->bounds().width() > 0) {
-      layer_object->setAnchorX(transform_origin.X() /
-                               cc_layer->bounds().width());
+    if (layer->bounds().width() > 0) {
+      layer_object->setAnchorX(transform_origin.x() / layer->bounds().width());
     } else {
       layer_object->setAnchorX(0.f);
     }
-    if (cc_layer->bounds().height() > 0) {
-      layer_object->setAnchorY(transform_origin.Y() /
-                               cc_layer->bounds().height());
+    if (layer->bounds().height() > 0) {
+      layer_object->setAnchorY(transform_origin.y() / layer->bounds().height());
     } else {
       layer_object->setAnchorY(0.f);
     }
-    layer_object->setAnchorZ(transform_origin.Z());
+    layer_object->setAnchorZ(transform_origin.z());
   }
   std::unique_ptr<Array<protocol::LayerTree::ScrollRect>> scroll_rects =
-      BuildScrollRectsForLayer(graphics_layer, report_wheel_event_listeners);
+      BuildScrollRectsForLayer(layer, report_wheel_event_listeners);
   if (scroll_rects)
     layer_object->setScrollRects(std::move(scroll_rects));
   std::unique_ptr<protocol::LayerTree::StickyPositionConstraint> sticky_info =
-      BuildStickyInfoForLayer(root, cc_layer);
+      BuildStickyInfoForLayer(root, layer);
   if (sticky_info)
     layer_object->setStickyPositionConstraint(std::move(sticky_info));
   return layer_object;
@@ -278,14 +284,15 @@
   GetFrontend()->layerTreeDidChange(BuildLayerTree());
 }
 
-void InspectorLayerTreeAgent::DidPaint(const GraphicsLayer* graphics_layer,
+void InspectorLayerTreeAgent::DidPaint(const cc::Layer* layer,
                                        GraphicsContext&,
                                        const LayoutRect& rect) {
   if (suppress_layer_paint_events_)
     return;
+
   // Should only happen for LocalFrameView paints when compositing is off.
   // Consider different instrumentation method for that.
-  if (!graphics_layer)
+  if (!layer)
     return;
 
   std::unique_ptr<protocol::DOM::Rect> dom_rect = protocol::DOM::Rect::create()
@@ -294,120 +301,71 @@
                                                       .setWidth(rect.Width())
                                                       .setHeight(rect.Height())
                                                       .build();
-  GetFrontend()->layerPainted(IdForLayer(graphics_layer), std::move(dom_rect));
+  GetFrontend()->layerPainted(IdForLayer(layer), std::move(dom_rect));
 }
 
 std::unique_ptr<Array<protocol::LayerTree::Layer>>
 InspectorLayerTreeAgent::BuildLayerTree() {
-  PaintLayerCompositor* compositor = GetPaintLayerCompositor();
-  if (!compositor || !compositor->InCompositingMode())
+  const auto* root_layer = RootLayer();
+  if (!root_layer)
     return nullptr;
 
-  LayerIdToNodeIdMap layer_id_to_node_id_map;
   std::unique_ptr<Array<protocol::LayerTree::Layer>> layers =
       Array<protocol::LayerTree::Layer>::create();
-  BuildLayerIdToNodeIdMap(compositor->RootLayer(), layer_id_to_node_id_map);
+  auto* root_frame = inspected_frames_->Root();
   auto* layer_for_scrolling =
-      inspected_frames_->Root()->View()->LayoutViewport()->LayerForScrolling();
+      root_frame->View()->LayoutViewport()->LayerForScrolling();
   int scrolling_layer_id =
       layer_for_scrolling ? layer_for_scrolling->CcLayer()->id() : 0;
   bool have_blocking_wheel_event_handlers =
-      inspected_frames_->Root()->GetChromeClient().EventListenerProperties(
-          inspected_frames_->Root(), cc::EventListenerClass::kMouseWheel) ==
+      root_frame->GetChromeClient().EventListenerProperties(
+          root_frame, cc::EventListenerClass::kMouseWheel) ==
       cc::EventListenerProperties::kBlocking;
 
-  GatherGraphicsLayers(RootGraphicsLayer(), layer_id_to_node_id_map, layers,
-                       have_blocking_wheel_event_handlers, scrolling_layer_id);
+  GatherLayers(root_layer, layers, have_blocking_wheel_event_handlers,
+               scrolling_layer_id);
   return layers;
 }
 
-void InspectorLayerTreeAgent::BuildLayerIdToNodeIdMap(
-    PaintLayer* root,
-    LayerIdToNodeIdMap& layer_id_to_node_id_map) {
-  if (root->HasCompositedLayerMapping()) {
-    if (Node* node = root->GetLayoutObject().GeneratingNode()) {
-      GraphicsLayer* graphics_layer =
-          root->GetCompositedLayerMapping()->ChildForSuperlayers();
-      layer_id_to_node_id_map.Set(graphics_layer->CcLayer()->id(),
-                                  IdentifiersFactory::IntIdForNode(node));
-    }
-  }
-  for (PaintLayer* child = root->FirstChild(); child;
-       child = child->NextSibling())
-    BuildLayerIdToNodeIdMap(child, layer_id_to_node_id_map);
-  if (!root->GetLayoutObject().IsLayoutIFrame())
-    return;
-  FrameView* child_frame_view =
-      ToLayoutEmbeddedContent(root->GetLayoutObject()).ChildFrameView();
-  if (!child_frame_view || !child_frame_view->IsLocalFrameView())
-    return;
-  LayoutView* child_layout_view =
-      ToLocalFrameView(child_frame_view)->GetLayoutView();
-  if (!child_layout_view)
-    return;
-  PaintLayerCompositor* child_compositor = child_layout_view->Compositor();
-  if (!child_compositor)
-    return;
-  BuildLayerIdToNodeIdMap(child_compositor->RootLayer(),
-                          layer_id_to_node_id_map);
-}
-
-void InspectorLayerTreeAgent::GatherGraphicsLayers(
-    GraphicsLayer* layer,
-    HashMap<int, int>& layer_id_to_node_id_map,
+void InspectorLayerTreeAgent::GatherLayers(
+    const cc::Layer* layer,
     std::unique_ptr<Array<protocol::LayerTree::Layer>>& layers,
     bool has_wheel_event_handlers,
     int scrolling_layer_id) {
   if (client_->IsInspectorLayer(layer))
     return;
-  int layer_id = layer->CcLayer()->id();
+  int layer_id = layer->id();
   layers->addItem(BuildObjectForLayer(
-      RootGraphicsLayer(), layer, layer_id_to_node_id_map.at(layer_id),
+      RootLayer(), layer,
       has_wheel_event_handlers && layer_id == scrolling_layer_id));
-  for (wtf_size_t i = 0, size = layer->Children().size(); i < size; ++i)
-    GatherGraphicsLayers(layer->Children()[i], layer_id_to_node_id_map, layers,
-                         has_wheel_event_handlers, scrolling_layer_id);
+  for (auto child : layer->children()) {
+    GatherLayers(child.get(), layers, has_wheel_event_handlers,
+                 scrolling_layer_id);
+  }
 }
 
-PaintLayerCompositor* InspectorLayerTreeAgent::GetPaintLayerCompositor() {
-  // TODO(crbug.com/916768): Implement the feature.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return nullptr;
-
-  auto* layout_view = inspected_frames_->Root()->ContentLayoutObject();
-  PaintLayerCompositor* compositor =
-      layout_view ? layout_view->Compositor() : nullptr;
-  return compositor;
+const cc::Layer* InspectorLayerTreeAgent::RootLayer() {
+  return inspected_frames_->Root()->View()->RootCcLayer();
 }
 
-GraphicsLayer* InspectorLayerTreeAgent::RootGraphicsLayer() {
-  return inspected_frames_->Root()
-      ->GetPage()
-      ->GetVisualViewport()
-      .RootGraphicsLayer();
-}
-
-static GraphicsLayer* FindLayerById(GraphicsLayer* root, int layer_id) {
-  if (root->CcLayer()->id() == layer_id)
+static const cc::Layer* FindLayerById(const cc::Layer* root, int layer_id) {
+  if (root->id() == layer_id)
     return root;
-  for (wtf_size_t i = 0, size = root->Children().size(); i < size; ++i) {
-    if (GraphicsLayer* layer = FindLayerById(root->Children()[i], layer_id))
+  for (auto child : root->children()) {
+    if (const auto* layer = FindLayerById(child.get(), layer_id))
       return layer;
   }
   return nullptr;
 }
 
 Response InspectorLayerTreeAgent::LayerById(const String& layer_id,
-                                            GraphicsLayer*& result) {
+                                            const cc::Layer*& result) {
   bool ok;
   int id = layer_id.ToInt(&ok);
   if (!ok)
     return Response::Error("Invalid layer id");
-  PaintLayerCompositor* compositor = GetPaintLayerCompositor();
-  if (!compositor)
-    return Response::Error("Not in compositing mode");
 
-  result = FindLayerById(RootGraphicsLayer(), id);
+  result = FindLayerById(RootLayer(), id);
   if (!result)
     return Response::Error("No layer matching given id found");
   return Response::OK();
@@ -416,27 +374,26 @@
 Response InspectorLayerTreeAgent::compositingReasons(
     const String& layer_id,
     std::unique_ptr<Array<String>>* reason_strings) {
-  GraphicsLayer* graphics_layer = nullptr;
-  Response response = LayerById(layer_id, graphics_layer);
+  const cc::Layer* layer = nullptr;
+  Response response = LayerById(layer_id, layer);
   if (!response.isSuccess())
     return response;
-  CompositingReasons reasons_bitmask = graphics_layer->GetCompositingReasons();
+  CompositingReasons reasons = layer->compositing_reasons();
   *reason_strings = Array<String>::create();
-  for (const char* name : CompositingReason::ShortNames(reasons_bitmask))
+  for (const char* name : CompositingReason::ShortNames(reasons))
     (*reason_strings)->addItem(name);
   return Response::OK();
 }
 
 Response InspectorLayerTreeAgent::makeSnapshot(const String& layer_id,
                                                String* snapshot_id) {
-  GraphicsLayer* layer = nullptr;
+  const cc::Layer* layer = nullptr;
   Response response = LayerById(layer_id, layer);
   if (!response.isSuccess())
     return response;
   if (!layer->DrawsContent())
     return Response::Error("Layer does not draw content");
 
-  IntRect interest_rect(IntPoint(), IntSize(layer->Size()));
   suppress_layer_paint_events_ = true;
 
   // If we hit a devtool break point in the middle of document lifecycle, for
@@ -448,22 +405,16 @@
                                                       .LifecyclePostponed())
     return Response::Error("Layer does not draw content");
 
-  inspected_frames_->Root()->View()->UpdateAllLifecyclePhasesExceptPaint();
-  for (auto frame = inspected_frames_->begin();
-       frame != inspected_frames_->end(); ++frame) {
-    frame->GetDocument()->Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
-  }
-  layer->Paint(&interest_rect);
-  for (auto frame = inspected_frames_->begin();
-       frame != inspected_frames_->end(); ++frame) {
-    frame->GetDocument()->Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
-  }
+  inspected_frames_->Root()->View()->UpdateAllLifecyclePhases(
+      DocumentLifecycle::LifecycleUpdateReason::kOther);
 
   suppress_layer_paint_events_ = false;
 
-  auto snapshot = base::AdoptRef(new PictureSnapshot(
-      ToSkPicture(layer->CapturePaintRecord(), interest_rect)));
+  auto picture = layer->GetPicture();
+  if (!picture)
+    return Response::Error("Layer does not produce picture");
 
+  auto snapshot = base::MakeRefCounted<PictureSnapshot>(std::move(picture));
   *snapshot_id = String::Number(++last_snapshot_id_);
   bool new_entry = snapshot_by_id_.insert(*snapshot_id, snapshot).is_new_entry;
   DCHECK(new_entry);
diff --git a/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h b/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h
index 2f6c552b..6346595 100644
--- a/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h
@@ -38,15 +38,16 @@
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
+namespace cc {
+class Layer;
+}
+
 namespace blink {
 
 class GraphicsContext;
-class GraphicsLayer;
 class InspectedFrames;
 class LayoutRect;
 class PictureSnapshot;
-class PaintLayer;
-class PaintLayerCompositor;
 
 class CORE_EXPORT InspectorLayerTreeAgent final
     : public InspectorBaseAgent<protocol::LayerTree::Metainfo> {
@@ -54,7 +55,7 @@
   class Client {
    public:
     virtual ~Client() = default;
-    virtual bool IsInspectorLayer(GraphicsLayer*) = 0;
+    virtual bool IsInspectorLayer(const cc::Layer*) = 0;
   };
 
   static InspectorLayerTreeAgent* Create(InspectedFrames* inspected_frames,
@@ -71,7 +72,7 @@
 
   // Called from InspectorInstrumentation
   void LayerTreeDidChange();
-  void DidPaint(const GraphicsLayer*, GraphicsContext&, const LayoutRect&);
+  void DidPaint(const cc::Layer*, GraphicsContext&, const LayoutRect&);
 
   // Called from the front-end.
   protocol::Response enable() override;
@@ -108,18 +109,13 @@
  private:
   static unsigned last_snapshot_id_;
 
-  GraphicsLayer* RootGraphicsLayer();
+  const cc::Layer* RootLayer();
 
-  PaintLayerCompositor* GetPaintLayerCompositor();
-  protocol::Response LayerById(const String& layer_id, GraphicsLayer*&);
+  protocol::Response LayerById(const String& layer_id, const cc::Layer*&);
   protocol::Response GetSnapshotById(const String& snapshot_id,
                                      const PictureSnapshot*&);
-
-  typedef HashMap<int, int> LayerIdToNodeIdMap;
-  void BuildLayerIdToNodeIdMap(PaintLayer*, LayerIdToNodeIdMap&);
-  void GatherGraphicsLayers(
-      GraphicsLayer*,
-      HashMap<int, int>& layer_id_to_node_id_map,
+  void GatherLayers(
+      const cc::Layer*,
       std::unique_ptr<protocol::Array<protocol::LayerTree::Layer>>&,
       bool has_wheel_event_handlers,
       int scrolling_root_layer_id);
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index c99546c..66ff2b9f 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/web/web_widget_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
@@ -185,6 +187,8 @@
       layer_->SetNeedsDisplay();
   }
 
+  const cc::Layer* GetLayer() const { return layer_.get(); }
+
  private:
   // cc::ContentLayerClient implementation
   gfx::Rect PaintableRegion() override { return gfx::Rect(layer_->bounds()); }
@@ -333,6 +337,7 @@
         static_cast<int>(backend_node_id_to_inspect_));
   }
   backend_node_id_to_inspect_ = 0;
+  SetNeedsUnbufferedInput(true);
   return Response::OK();
 }
 
@@ -348,6 +353,7 @@
   setSuspended(false);
   SetSearchingForNode(kNotSearching,
                       Maybe<protocol::Overlay::HighlightConfig>());
+  SetNeedsUnbufferedInput(false);
   return Response::OK();
 }
 
@@ -358,7 +364,12 @@
     if (!response.isSuccess())
       return response;
   }
-  frame_impl_->ViewImpl()->SetShowDebugBorders(show);
+  WebFrameWidget* widget = frame_impl_->LocalRoot()->FrameWidget();
+  WebFrameWidgetBase* widget_impl = static_cast<WebFrameWidgetBase*>(widget);
+  // While a frame is being detached the inspector will shutdown and
+  // turn off debug overlays, but the WebFrameWidget is already gone.
+  if (widget_impl)
+    widget_impl->Client()->SetShowDebugBorders(show);
   return Response::OK();
 }
 
@@ -369,7 +380,12 @@
     if (!response.isSuccess())
       return response;
   }
-  frame_impl_->ViewImpl()->SetShowFPSCounter(show);
+  WebFrameWidget* widget = frame_impl_->LocalRoot()->FrameWidget();
+  WebFrameWidgetBase* widget_impl = static_cast<WebFrameWidgetBase*>(widget);
+  // While a frame is being detached the inspector will shutdown and
+  // turn off debug overlays, but the WebFrameWidget is already gone.
+  if (widget_impl)
+    widget_impl->Client()->SetShowFPSCounter(show);
   return Response::OK();
 }
 
@@ -380,7 +396,12 @@
     if (!response.isSuccess())
       return response;
   }
-  frame_impl_->ViewImpl()->SetShowPaintRects(show);
+  WebFrameWidget* widget = frame_impl_->LocalRoot()->FrameWidget();
+  WebFrameWidgetBase* widget_impl = static_cast<WebFrameWidgetBase*>(widget);
+  // While a frame is being detached the inspector will shutdown and
+  // turn off debug overlays, but the WebFrameWidget is already gone.
+  if (widget_impl)
+    widget_impl->Client()->SetShowPaintRects(show);
   if (!show && frame_impl_->GetFrameView())
     frame_impl_->GetFrameView()->Invalidate();
   return Response::OK();
@@ -393,7 +414,12 @@
     if (!response.isSuccess())
       return response;
   }
-  frame_impl_->ViewImpl()->SetShowScrollBottleneckRects(show);
+  WebFrameWidget* widget = frame_impl_->LocalRoot()->FrameWidget();
+  WebFrameWidgetBase* widget_impl = static_cast<WebFrameWidgetBase*>(widget);
+  // While a frame is being detached the inspector will shutdown and
+  // turn off debug overlays, but the WebFrameWidget is already gone.
+  if (widget_impl)
+    widget_impl->Client()->SetShowScrollBottleneckRects(show);
   return Response::OK();
 }
 
@@ -404,7 +430,12 @@
     if (!response.isSuccess())
       return response;
   }
-  frame_impl_->ViewImpl()->SetShowHitTestBorders(show);
+  WebFrameWidget* widget = frame_impl_->LocalRoot()->FrameWidget();
+  WebFrameWidgetBase* widget_impl = static_cast<WebFrameWidgetBase*>(widget);
+  // While a frame is being detached the inspector will shutdown and
+  // turn off debug overlays, but the WebFrameWidget is already gone.
+  if (widget_impl)
+    widget_impl->Client()->SetShowHitTestBorders(show);
   return Response::OK();
 }
 
@@ -424,6 +455,7 @@
   if (suspended && !suspended_.Get())
     ClearInternal();
   suspended_.Set(suspended);
+  SetNeedsUnbufferedInput(!suspended);
   return Response::OK();
 }
 
@@ -576,8 +608,15 @@
     frame_overlay_->Paint(context);
 }
 
-bool InspectorOverlayAgent::IsInspectorLayer(GraphicsLayer* layer) {
-  return frame_overlay_ && frame_overlay_->GetGraphicsLayer() == layer;
+bool InspectorOverlayAgent::IsInspectorLayer(const cc::Layer* layer) const {
+  if (!frame_overlay_)
+    return false;
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    return layer == static_cast<const InspectorPageOverlayDelegate*>(
+                        frame_overlay_->GetDelegate())
+                        ->GetLayer();
+  }
+  return layer == frame_overlay_->GetGraphicsLayer()->CcLayer();
 }
 
 void InspectorOverlayAgent::DispatchBufferedTouchEvents() {
@@ -1325,4 +1364,12 @@
   return Response::OK();
 }
 
+void InspectorOverlayAgent::SetNeedsUnbufferedInput(bool unbuffered) {
+  LocalFrame* frame = frame_impl_->GetFrame();
+  if (frame) {
+    frame->GetPage()->GetChromeClient().SetNeedsUnbufferedInputForDebugger(
+        frame, unbuffered);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
index 440d005..31fa1c8b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
@@ -47,11 +47,14 @@
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
+namespace cc {
+class Layer;
+}
+
 namespace blink {
 
 class Color;
 class GraphicsContext;
-class GraphicsLayer;
 class InspectedFrames;
 class InspectorDOMAgent;
 class LocalFrame;
@@ -131,7 +134,7 @@
   // For CompositeAfterPaint.
   void PaintOverlay(GraphicsContext&);
 
-  bool IsInspectorLayer(GraphicsLayer*);
+  bool IsInspectorLayer(const cc::Layer*) const;
 
  private:
   class InspectorOverlayChromeClient;
@@ -194,6 +197,8 @@
                           bool omit_tooltip);
   void InnerHideHighlight();
 
+  void SetNeedsUnbufferedInput(bool unbuffered);
+
   Member<WebLocalFrameImpl> frame_impl_;
   Member<InspectedFrames> inspected_frames_;
   Member<Node> highlight_node_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index 74331df..c6235b9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -33,6 +33,7 @@
 #include <memory>
 
 #include "build/build_config.h"
+#include "third_party/blink/public/platform/web_screen_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_regexp.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
@@ -1156,7 +1157,13 @@
 
   LocalFrameView* frame_view = main_frame->View();
   ScrollOffset page_offset = frame_view->GetScrollableArea()->GetScrollOffset();
+  // page_zoom is either CSS-to-DP or CSS-to-DIP depending on
+  // enable-use-zoom-for-dsf flag.
   float page_zoom = main_frame->PageZoomFactor();
+  // page_zoom_factor is CSS to DIP (device independent pixels).
+  float page_zoom_factor =
+      page_zoom /
+      main_frame->GetPage()->GetChromeClient().WindowToViewportScalar(1);
   FloatRect visible_rect = visual_viewport.VisibleRect();
   float scale = visual_viewport.Scale();
 
@@ -1180,6 +1187,7 @@
                              .setClientWidth(visible_rect.Width())
                              .setClientHeight(visible_rect.Height())
                              .setScale(scale)
+                             .setZoom(page_zoom_factor)
                              .build();
   return Response::OK();
 }
diff --git a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
index 5e8d128..7462f7f 100644
--- a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
@@ -171,7 +171,7 @@
   WorkerThread* thread = worker_threads_.at(context_group_id);
   DCHECK(!thread->GlobalScope()->IsClosing());
   thread->GetWorkerInspectorController()->FlushProtocolNotifications();
-  thread->GlobalScope()->PauseScheduledTasks();
+  thread->GlobalScope()->PauseScheduledTasks(PauseState::kPaused);
   auto pause_handle = thread->GetScheduler()->Pause();
   if (!nested_runner_)
     nested_runner_ = Platform::Current()->CreateNestedMessageLoopRunner();
diff --git a/third_party/blink/renderer/core/layout/jank_tracker.cc b/third_party/blink/renderer/core/layout/jank_tracker.cc
index 4a6ccfe..81c4cb4 100644
--- a/third_party/blink/renderer/core/layout/jank_tracker.cc
+++ b/third_party/blink/renderer/core/layout/jank_tracker.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/frame/location.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
@@ -209,17 +210,19 @@
   DVLOG(1) << "viewport " << (jank_fraction * 100)
            << "% janked, raising score to " << score_;
 
+  LocalFrame& frame = frame_view_->GetFrame();
+
   TRACE_EVENT_INSTANT2("loading", "FrameLayoutJank", TRACE_EVENT_SCOPE_THREAD,
                        "data",
                        PerFrameTraceData(jank_fraction, granularity_scale),
-                       "frame", ToTraceValue(&frame_view_->GetFrame()));
+                       "frame", ToTraceValue(&frame));
 
-  frame_view_->GetFrame().Client()->DidObserveLayoutJank(jank_fraction);
+  frame.Client()->DidObserveLayoutJank(jank_fraction);
 
-  if (RuntimeEnabledFeatures::LayoutJankAPIEnabled() &&
-      frame_view_->GetFrame().DomWindow()) {
+  if (origin_trials::LayoutJankAPIEnabled(frame.GetDocument()) &&
+      frame.DomWindow()) {
     WindowPerformance* performance =
-        DOMWindowPerformance::performance(*frame_view_->GetFrame().DomWindow());
+        DOMWindowPerformance::performance(*frame.DomWindow());
     if (performance &&
         performance->HasObserverFor(PerformanceEntry::kLayoutJank)) {
       performance->AddLayoutJankFraction(jank_fraction);
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index c3f36a3e..bdc20aa 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2742,9 +2742,6 @@
                            (!in_vertical_box || !stretching) &&
                            (!IsGridItem() || !HasStretchedLogicalWidth());
   const ComputedStyle& style_to_use = StyleRef();
-  Length logical_width_length =
-      treat_as_replaced ? Length(ComputeReplacedLogicalWidth(), kFixed)
-                        : style_to_use.LogicalWidth();
 
   LayoutBlock* cb = ContainingBlock();
   LayoutUnit container_logical_width =
@@ -2758,11 +2755,11 @@
         style_to_use.MarginStart(), container_logical_width);
     computed_values.margins_.end_ = MinimumValueForLength(
         style_to_use.MarginEnd(), container_logical_width);
-    if (treat_as_replaced)
-      computed_values.extent_ =
-          std::max(LayoutUnit(FloatValueForLength(logical_width_length, 0)) +
-                       BorderAndPaddingLogicalWidth(),
-                   MinPreferredLogicalWidth());
+    if (treat_as_replaced) {
+      computed_values.extent_ = std::max(
+          ComputeReplacedLogicalWidth() + BorderAndPaddingLogicalWidth(),
+          MinPreferredLogicalWidth());
+    }
     return;
   }
 
@@ -2777,8 +2774,8 @@
 
   // Width calculations
   if (treat_as_replaced) {
-    computed_values.extent_ = LayoutUnit(logical_width_length.Value()) +
-                              BorderAndPaddingLogicalWidth();
+    computed_values.extent_ =
+        ComputeReplacedLogicalWidth() + BorderAndPaddingLogicalWidth();
   } else {
     LayoutUnit preferred_width = ComputeLogicalWidthUsing(
         kMainOrPreferredSize, style_to_use.LogicalWidth(),
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 521f39a..d952e08 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -507,8 +507,8 @@
     ValidateImagePolicies();
 
     // Report violation of unsized-media policy.
-    if (image_element->IsDefaultIntrinsicSize())
-      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
+    media_element_parser_helpers::ReportUnsizedMediaViolation(
+        this, image_element->IsDefaultIntrinsicSize());
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index b3c2a8b..b73f2063 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -1631,7 +1631,8 @@
     // TODO(yosin): We should move |SetAncestorLineBoxDirty()| into
     // |DirtyLinesFromChangedChild()| like legacy layout.
     SetAncestorLineBoxDirty();
-    NGPaintFragment::DirtyLinesFromChangedChild(child);
+    if (child->IsInLayoutNGInlineFormattingContext())
+      NGPaintFragment::DirtyLinesFromChangedChild(child);
     return;
   }
   MutableLineBoxes()->DirtyLinesFromChangedChild(
diff --git a/third_party/blink/renderer/core/layout/layout_video.cc b/third_party/blink/renderer/core/layout/layout_video.cc
index a9a9763..93c405e 100644
--- a/third_party/blink/renderer/core/layout/layout_video.cc
+++ b/third_party/blink/renderer/core/layout/layout_video.cc
@@ -200,8 +200,8 @@
   LayoutBox::UpdateAfterLayout();
   // Report violation of unsized-media policy.
   if (auto* video_element = ToHTMLVideoElementOrNull(GetNode())) {
-    if (video_element->IsDefaultIntrinsicSize())
-      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
+    media_element_parser_helpers::ReportUnsizedMediaViolation(
+        this, video_element->IsDefaultIntrinsicSize());
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 45ec4ce..163b6ac 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -64,6 +64,34 @@
 
 namespace blink {
 
+namespace {
+
+class HitTestLatencyRecorder {
+ public:
+  HitTestLatencyRecorder(bool allows_child_frame_content)
+      : start_(CurrentTimeTicks()),
+        allows_child_frame_content_(allows_child_frame_content) {}
+
+  ~HitTestLatencyRecorder() {
+    TimeDelta duration = CurrentTimeTicks() - start_;
+    if (allows_child_frame_content_) {
+      DEFINE_STATIC_LOCAL(CustomCountHistogram, recursive_latency_histogram,
+                          ("Event.Latency.HitTestRecursive", 0, 10000000, 100));
+      recursive_latency_histogram.CountMicroseconds(duration);
+    } else {
+      DEFINE_STATIC_LOCAL(CustomCountHistogram, latency_histogram,
+                          ("Event.Latency.HitTest", 0, 10000000, 100));
+      latency_histogram.CountMicroseconds(duration);
+    }
+  }
+
+ private:
+  TimeTicks start_;
+  bool allows_child_frame_content_;
+};
+
+}  // namespace
+
 LayoutView::LayoutView(Document* document)
     : LayoutBlockFlow(document),
       frame_view_(document->View()),
@@ -107,6 +135,8 @@
   // iframe's inner document.
   if (!GetFrameView()->UpdateAllLifecyclePhasesExceptPaint())
     return false;
+  HitTestLatencyRecorder hit_test_latency_recorder(
+      result.GetHitTestRequest().AllowsChildFrameContent());
   return HitTestNoLifecycleUpdate(location, result);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
index fef96d5..0603ca6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc
@@ -102,7 +102,7 @@
 }
 
 NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems(
-    const ComputedStyle* line_style,
+    const ComputedStyle& line_style,
     FontBaseline baseline_type,
     bool line_height_quirk) {
   if (stack_.IsEmpty()) {
@@ -133,14 +133,14 @@
 
   // Initialize the box state for the line box.
   NGInlineBoxState& line_box = LineBoxState();
-  if (line_box.style != line_style) {
-    line_box.style = line_style;
+  if (line_box.style != &line_style) {
+    line_box.style = &line_style;
 
     // Use a "strut" (a zero-width inline box with the element's font and
     // line height properties) as the initial metrics for the line box.
     // https://drafts.csswg.org/css2/visudet.html#strut
     if (!line_height_quirk)
-      line_box.ComputeTextMetrics(*line_style, baseline_type);
+      line_box.ComputeTextMetrics(line_style, baseline_type);
   }
 
   return &stack_.back();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
index 32d8ae46..cc29536 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
@@ -116,7 +116,7 @@
 
   // Initialize the box state stack for a new line.
   // @return The initial box state for the line.
-  NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle*, FontBaseline, bool);
+  NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle&, FontBaseline, bool);
 
   // Push a box state stack.
   NGInlineBoxState* OnOpenTag(const NGInlineItem&,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 9f3ccbb..13ed178 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -138,7 +138,7 @@
   }
 
   // Create box states for tags that are not closed yet.
-  box_states->OnBeginPlaceItems(&line_info.LineStyle(), baseline_type_,
+  box_states->OnBeginPlaceItems(line_info.LineStyle(), baseline_type_,
                                 quirks_mode_);
   for (const NGInlineItem* item : open_items) {
     NGInlineItemResult item_result;
@@ -153,7 +153,7 @@
     const NGInlineBreakToken* break_token) const {
   NGInlineLayoutStateStack rebuilt;
   RebuildBoxStates(line_info, break_token, &rebuilt);
-  rebuilt.OnBeginPlaceItems(&line_info.LineStyle(), baseline_type_,
+  rebuilt.OnBeginPlaceItems(line_info.LineStyle(), baseline_type_,
                             quirks_mode_);
 
   DCHECK(box_states_);
@@ -173,19 +173,15 @@
   // of items, which are needed to compute inline static positions.
   LayoutUnit line_offset_for_text_align = ApplyTextAlign(line_info);
 
-  const ComputedStyle& line_style = line_info->LineStyle();
-  NGLineHeightMetrics line_metrics(line_style, baseline_type_);
-  NGLineHeightMetrics line_metrics_with_leading = line_metrics;
-  line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed());
-
   NGTextFragmentBuilder text_builder(Node(),
                                      ConstraintSpace().GetWritingMode());
 
   // Compute heights of all inline items by placing the dominant baseline at 0.
   // The baseline is adjusted after the height of the line box is computed.
+  const ComputedStyle& line_style = line_info->LineStyle();
   box_states_->SetIsEmptyLine(line_info->IsEmptyLine());
   NGInlineBoxState* box =
-      box_states_->OnBeginPlaceItems(&line_style, baseline_type_, quirks_mode_);
+      box_states_->OnBeginPlaceItems(line_style, baseline_type_, quirks_mode_);
 #if DCHECK_IS_ON()
   if (is_box_states_from_context_)
     CheckBoxStates(*line_info, BreakToken());
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 35356c524..65e05a1 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -249,6 +249,9 @@
       if (builder->ShouldAbort())
         return;
 
+      if (update_layout)
+        ClearInlineFragment(node);
+
     } else if (node->IsAtomicInlineLevel()) {
       if (node->IsLayoutNGListMarker() || node->IsListMarker()) {
         // LayoutNGListItem produces the 'outside' list marker as an inline
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
index 83efd99..82e45844 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
@@ -190,7 +190,9 @@
                     "<span>span</span>",
                     "<span>1234 12345678</span>",
                     "<span style='display: inline-block'>box</span>",
-                    "<img>"));
+                    "<img>",
+                    "<div style='float: left'>float</div>",
+                    "<div style='position: absolute'>abs</div>"));
 
 #define TEST_ITEM_TYPE_OFFSET(item, type, start, end) \
   EXPECT_EQ(NGInlineItem::type, item.Type());         \
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 6b1f5357..7702b04ed 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -30,8 +30,10 @@
                            scoped_refptr<const ComputedStyle> style,
                            WritingMode writing_mode,
                            TextDirection)
-      : NGContainerFragmentBuilder(style, writing_mode, TextDirection::kLtr),
-        node_(node),
+      : NGContainerFragmentBuilder(node,
+                                   style,
+                                   writing_mode,
+                                   TextDirection::kLtr),
         line_box_type_(NGPhysicalLineBoxFragment::kNormalLineBox),
         base_direction_(TextDirection::kLtr) {}
 
@@ -222,8 +224,6 @@
   scoped_refptr<NGLayoutResult> ToLineBoxFragment();
 
  private:
-  NGInlineNode node_;
-
   NGLineHeightMetrics metrics_;
   NGPhysicalLineBoxFragment::NGLineBoxType line_box_type_;
   TextDirection base_direction_;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index c827c5f..80b1dbc 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -295,7 +295,7 @@
 
     // If the available / percentage sizes have changed in a way that may affect
     // layout, we cannot re-use the previous result.
-    if (SizeMayChange(Base::StyleRef(), new_space, old_space))
+    if (SizeMayChange(Base::StyleRef(), new_space, old_space, *cached_result_))
       return nullptr;
   }
 
@@ -396,12 +396,18 @@
   scoped_refptr<NGPaintFragment>* current =
       NGPaintFragment::Find(&paint_fragment_, break_token);
   DCHECK(current);
+  bool has_old = current->get();
   if (fragment) {
     *current = NGPaintFragment::Create(std::move(fragment), offset, break_token,
                                        std::move(*current));
   } else {
     *current = nullptr;
   }
+
+  if (has_old) {
+    // Painting layer needs repaint when a DisplayItemClient is destroyed.
+    ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint();
+  }
 }
 
 template <typename Base>
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 4a442b76..0c82294 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
@@ -237,7 +237,7 @@
       // -dynamic.html
       // TODO(layoutng): See if we can optimize this. When we natively
       // support relative positioning in NG we can probably remove this,
-      box_->SetShouldCheckForPaintInvalidation();
+      box_->SetSubtreeShouldCheckForPaintInvalidation();
 
       // We have to re-set the cached result here, because it is used for
       // LayoutNGMixin::CurrentFragment and therefore has to be up-to-date.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 8efac70..7280a74 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -29,8 +29,10 @@
                        scoped_refptr<const ComputedStyle> style,
                        WritingMode writing_mode,
                        TextDirection direction)
-      : NGContainerFragmentBuilder(std::move(style), writing_mode, direction),
-        node_(node),
+      : NGContainerFragmentBuilder(node,
+                                   std::move(style),
+                                   writing_mode,
+                                   direction),
         box_type_(NGPhysicalFragment::NGBoxType::kNormalBox),
         is_old_layout_root_(false),
         did_break_(false) {
@@ -43,8 +45,10 @@
                        scoped_refptr<const ComputedStyle> style,
                        WritingMode writing_mode,
                        TextDirection direction)
-      : NGContainerFragmentBuilder(std::move(style), writing_mode, direction),
-        node_(nullptr),
+      : NGContainerFragmentBuilder(nullptr,
+                                   std::move(style),
+                                   writing_mode,
+                                   direction),
         box_type_(NGPhysicalFragment::NGBoxType::kNormalBox),
         is_old_layout_root_(false),
         did_break_(false) {
@@ -224,8 +228,6 @@
  private:
   scoped_refptr<NGLayoutResult> ToBoxFragment(WritingMode);
 
-  NGLayoutInputNode node_;
-
   LayoutUnit intrinsic_block_size_;
   NGBoxStrut borders_;
   NGBoxStrut padding_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 22d4bf32..9c35f94 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -60,6 +60,9 @@
   if (child.HasOrthogonalFlowRoots())
     has_orthogonal_flow_roots_ = true;
 
+  if (child.DependsOnPercentageBlockSize())
+    has_depends_on_percentage_block_size_child_ = true;
+
   return AddChild(child.PhysicalFragment(), child_offset);
 }
 
@@ -90,6 +93,10 @@
                              Style().GetWritingMode()))
     has_orthogonal_flow_roots_ = true;
 
+  // We mark all legacy layout nodes as dependent on percentage block-size.
+  if (child->IsOldLayoutRoot())
+    has_depends_on_percentage_block_size_child_ = true;
+
   if (!has_last_resort_break_) {
     if (const auto* token = child->BreakToken()) {
       if (token->IsBlockType() &&
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 63e82a7c..4439244 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -164,6 +164,9 @@
 #endif
 
  protected:
+  friend class NGPhysicalContainerFragment;
+  friend class NGLayoutResult;
+
   // An out-of-flow positioned-candidate is a temporary data structure used
   // within the NGBoxFragmentBuilder.
   //
@@ -188,10 +191,14 @@
         : descendant(descendant), child_offset(child_offset) {}
   };
 
-  NGContainerFragmentBuilder(scoped_refptr<const ComputedStyle> style,
+  NGContainerFragmentBuilder(NGLayoutInputNode node,
+                             scoped_refptr<const ComputedStyle> style,
                              WritingMode writing_mode,
                              TextDirection direction)
-      : NGFragmentBuilder(std::move(style), writing_mode, direction) {}
+      : NGFragmentBuilder(std::move(style), writing_mode, direction),
+        node_(node) {}
+
+  NGLayoutInputNode node_;
 
   LayoutUnit bfc_line_offset_;
   base::Optional<LayoutUnit> bfc_block_offset_;
@@ -222,8 +229,7 @@
   bool is_pushed_by_floats_ = false;
 
   bool has_orthogonal_flow_roots_ = false;
-
-  friend class NGPhysicalContainerFragment;
+  bool has_depends_on_percentage_block_size_child_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 28385668..1eb55b3 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -30,6 +30,7 @@
       is_pushed_by_floats_(builder->is_pushed_by_floats_),
       adjoining_floats_(builder->adjoining_floats_),
       has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
+      depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)),
       status_(kSuccess) {
   DCHECK(physical_fragment) << "Use the other constructor for aborting layout";
   root_fragment_.fragment_ = std::move(physical_fragment);
@@ -47,6 +48,7 @@
       is_pushed_by_floats_(false),
       adjoining_floats_(kFloatTypeNone),
       has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
+      depends_on_percentage_block_size_(false),
       status_(status) {
   DCHECK_NE(status, kSuccess)
       << "Use the other constructor for successful layout";
@@ -67,6 +69,7 @@
       is_pushed_by_floats_(builder->is_pushed_by_floats_),
       adjoining_floats_(builder->adjoining_floats_),
       has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
+      depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)),
       status_(kSuccess) {
   root_fragment_.fragment_ = std::move(physical_fragment);
   oof_positioned_descendants_ = std::move(builder->oof_positioned_descendants_);
@@ -90,10 +93,50 @@
       is_pushed_by_floats_(other.is_pushed_by_floats_),
       adjoining_floats_(other.adjoining_floats_),
       has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_),
+      depends_on_percentage_block_size_(
+          other.depends_on_percentage_block_size_),
       status_(other.status_) {}
 
 // Define the destructor here, so that we can forward-declare more in the
 // header.
 NGLayoutResult::~NGLayoutResult() = default;
 
+bool NGLayoutResult::DependsOnPercentageBlockSize(
+    const NGContainerFragmentBuilder& builder) {
+  NGLayoutInputNode node = builder.node_;
+
+  if (!node || node.IsInline())
+    return builder.has_depends_on_percentage_block_size_child_;
+
+  // NOTE: If an element is OOF positioned, and has top/bottom constraints
+  // which are percentage based, this function will return false.
+  //
+  // This is fine as the top/bottom constraints are computed *before* layout,
+  // and the result is set as a fixed-block-size constraint. (And the caching
+  // logic will never check the result of this function).
+  //
+  // The result of this function still may be used for an OOF positioned
+  // element if it has a percentage block-size however, but this will return
+  // the correct result from below.
+
+  const ComputedStyle& style = builder.Style();
+  if (style.LogicalHeight().IsAuto() &&
+      builder.has_depends_on_percentage_block_size_child_) {
+    // Quirks mode has different %-block-size behaviour, than standards mode.
+    // An arbitrary descendant may depend on the percentage resolution
+    // block-size given.
+    // If this is also an anonymous block we need to mark ourselves dependent
+    // if we have a dependent child.
+    if (node.IsAnonymousBlock() || node.GetDocument().InQuirksMode())
+      return true;
+  }
+
+  if (style.LogicalHeight().IsPercentOrCalc() ||
+      style.LogicalMinHeight().IsPercentOrCalc() ||
+      style.LogicalMaxHeight().IsPercentOrCalc())
+    return true;
+
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index accd3aa..bd6c4df6 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -110,6 +110,12 @@
 
   bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
 
+  // Returns true if we aren't able to re-use this layout result if the
+  // PercentageResolutionBlockSize changes.
+  bool DependsOnPercentageBlockSize() const {
+    return depends_on_percentage_block_size_;
+  }
+
  private:
   friend class NGBoxFragmentBuilder;
   friend class NGLineBoxFragmentBuilder;
@@ -126,6 +132,8 @@
   // default copy constructor will not work because RefCounted can't be copied.
   NGLayoutResult(const NGLayoutResult&) = delete;
 
+  static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);
+
   NGLink root_fragment_;
   Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_;
 
@@ -147,6 +155,7 @@
   unsigned adjoining_floats_ : 2;  // NGFloatTypes
 
   unsigned has_orthogonal_flow_roots_ : 1;
+  unsigned depends_on_percentage_block_size_ : 1;
 
   unsigned status_ : 1;
 };
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 8c580c2..803d589 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
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
@@ -79,17 +80,6 @@
     if (new_space.AvailableSize().block_size !=
         old_space.AvailableSize().block_size)
       return true;
-  } else if (length.IsAuto() || length.IsPercentOrCalc()) {
-    // Note that we check percentage resolution changes for 'auto' values here
-    // (in addition to percent values). The reason is that percentage resolution
-    // block sizes may be passed through auto-sized blocks, in some cases,
-    // e.g. for anonymous blocks, and also in quirks mode.
-    if (new_space.PercentageResolutionBlockSize() !=
-        old_space.PercentageResolutionBlockSize())
-      return true;
-    if (new_space.ReplacedPercentageResolutionBlockSize() !=
-        old_space.ReplacedPercentageResolutionBlockSize())
-      return true;
   }
   return false;
 }
@@ -656,7 +646,8 @@
 
 bool SizeMayChange(const ComputedStyle& style,
                    const NGConstraintSpace& new_space,
-                   const NGConstraintSpace& old_space) {
+                   const NGConstraintSpace& old_space,
+                   const NGLayoutResult& layout_result) {
   DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
   DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
 
@@ -690,6 +681,16 @@
         BlockLengthMayChange(style.LogicalMinHeight(), new_space, old_space) ||
         BlockLengthMayChange(style.LogicalMaxHeight(), new_space, old_space))
       return true;
+    // We only need to check if the PercentageResolutionBlockSizes match if the
+    // layout result has explicitly marked itself as dependent.
+    if (layout_result.DependsOnPercentageBlockSize()) {
+      if (new_space.PercentageResolutionBlockSize() !=
+          old_space.PercentageResolutionBlockSize())
+        return true;
+      if (new_space.ReplacedPercentageResolutionBlockSize() !=
+          old_space.ReplacedPercentageResolutionBlockSize())
+        return true;
+    }
   }
 
   if (new_space.PercentageResolutionInlineSize() !=
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 28581eb..4ab2a8ab 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
@@ -24,6 +24,7 @@
 class NGConstraintSpace;
 class NGBlockNode;
 class NGLayoutInputNode;
+class NGLayoutResult;
 
 // LengthResolvePhase indicates what type of layout pass we are currently in.
 // This changes how lengths are resolved. kIntrinsic must be used during the
@@ -161,7 +162,8 @@
 // computed style and child content remain unchanged.
 bool SizeMayChange(const ComputedStyle&,
                    const NGConstraintSpace& new_space,
-                   const NGConstraintSpace& old_space);
+                   const NGConstraintSpace& old_space,
+                   const NGLayoutResult& layout_result);
 
 // Based on available inline size, CSS computed column-width, CSS computed
 // column-count and CSS used column-gap, return CSS used column-count.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index 8494c388..b05921f 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -157,8 +157,8 @@
   DCHECK(!needs_transform_update_);
 
   if (auto* svg_image_element = ToSVGImageElementOrNull(GetElement())) {
-    if (svg_image_element->IsDefaultIntrinsicSize())
-      media_element_parser_helpers::ReportUnsizedMediaViolation(this);
+    media_element_parser_helpers::ReportUnsizedMediaViolation(
+        this, svg_image_element->IsDefaultIntrinsicSize());
   }
   ClearNeedsLayout();
 }
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
index 07ea5af..083adb94 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context_test.cc
@@ -131,8 +131,8 @@
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(
         *MakeGarbageCollected<FetchClientSettingsObjectImpl>(
             *execution_context_));
-    resource_fetcher_ =
-        MakeGarbageCollected<ResourceFetcher>(*properties, fetch_context_);
+    resource_fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, fetch_context_));
   }
 
   Persistent<ExecutionContext> execution_context_;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 4eca8f9e..ebb69de 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1323,6 +1323,19 @@
   if (reason == InstallNewDocumentReason::kNavigation)
     DidCommitNavigation(global_object_reuse_policy);
 
+  // TODO(yoichio): This is temporary switch to support chrome internal
+  // components migration from the old web APIs.
+  // After completion of the migration, we should remove this.
+  // See crbug.com/911943 for detail.
+  if (url.Protocol() == "chrome-devtools" || url.Protocol() == "chrome") {
+    OriginTrialContext::FromOrCreate(document)->AddFeature("WebComponentsV0");
+    // CrElementsProfileAvatarSelectorFocusTest, or some Web UI tests need this
+    // RuntimeEnabledFeatures on in addition to the above OriginTrialContext
+    // flag.
+    RuntimeEnabledFeatures::SetShadowDOMV0Enabled(true);
+    RuntimeEnabledFeatures::SetCustomElementsV0Enabled(true);
+    RuntimeEnabledFeatures::SetHTMLImportsEnabled(true);
+  }
   // Initializing origin trials might force window proxy initialization,
   // which later triggers CHECK when swapping in via WebFrame::Swap().
   // We can safely omit installing original trials on initial empty document
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index c8684c4c..e2c8b98 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -209,6 +209,7 @@
   }
   void SetHasScrollEventHandlers(LocalFrame*, bool) override {}
   void SetNeedsLowLatencyInput(LocalFrame*, bool) override {}
+  void SetNeedsUnbufferedInputForDebugger(LocalFrame*, bool) override {}
   void RequestUnbufferedInputEvents(LocalFrame*) override {}
   void SetTouchAction(LocalFrame*, TouchAction) override {}
 
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index 91cccdf..901b1f6 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -235,11 +235,9 @@
           ? UTF8Encoding()
           : FormDataEncoder::EncodingFromAcceptCharset(
                 copied_attributes.AcceptCharset(), document.Encoding());
-  FormData* dom_form_data =
-      FormData::Create(data_encoding.EncodingForFormSubmission());
-  bool entry_list_result =
-      form->ConstructEntryList(submit_button, *dom_form_data);
-  DCHECK(entry_list_result);
+  FormData* dom_form_data = form->ConstructEntryList(
+      submit_button, data_encoding.EncodingForFormSubmission());
+  DCHECK(dom_form_data);
 
   scoped_refptr<EncodedFormData> form_data;
   String boundary;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 7767a10..edb82bf 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -261,7 +261,7 @@
   if (Document* document = frame_->GetDocument()) {
     document->Fetcher()->SetDefersLoading(defers);
     if (defers)
-      document->PauseScheduledTasks();
+      document->PauseScheduledTasks(PauseState::kPaused);
     else
       document->UnpauseScheduledTasks();
   }
diff --git a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
index de939305..e941e43 100644
--- a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
@@ -37,6 +37,7 @@
   ControllerServiceWorkerMode GetControllerServiceWorkerMode() const override;
   int64_t ServiceWorkerId() const override;
   bool IsPaused() const override;
+  bool IsDetached() const override { return false; }
   bool IsLoadComplete() const override;
   bool ShouldBlockLoadingMainResource() const override;
   bool ShouldBlockLoadingSubResource() const override;
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
index 7fb7f74d..b163374 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -169,7 +169,8 @@
   auto* fetch_context = MakeGarbageCollected<MockFetchContext>(nullptr);
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
-  fetcher_ = MakeGarbageCollected<ResourceFetcher>(*properties, fetch_context);
+  fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, fetch_context));
   modulator_ = MakeGarbageCollected<ModuleScriptLoaderTestModulator>(
       ToScriptStateForMainWorld(&GetFrame()));
 }
@@ -178,7 +179,8 @@
   auto* fetch_context = MakeGarbageCollected<MockFetchContext>(nullptr);
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
-  fetcher_ = MakeGarbageCollected<ResourceFetcher>(*properties, fetch_context);
+  fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, fetch_context));
   reporting_proxy_ = std::make_unique<MockWorkerReportingProxy>();
   auto creation_params = std::make_unique<GlobalScopeCreationParams>(
       url_, mojom::ScriptType::kModule, "UserAgent",
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index e0761f9..da5b0fb 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -48,7 +48,8 @@
 
   MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
 
   // Fetch to cache a resource.
   ResourceRequest request1(url);
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index e9c60309..9ec0182 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -354,8 +354,8 @@
 
 ResourceFetcher* CreateFetcher() {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  return MakeGarbageCollected<ResourceFetcher>(
-      *properties, MakeGarbageCollected<MockFetchContext>());
+  return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+      *properties, MakeGarbageCollected<MockFetchContext>()));
 }
 
 TEST(ImageResourceTest, MultipartImage) {
@@ -1868,7 +1868,8 @@
   MockFetchContext* context = MakeGarbageCollected<MockFetchContext>(
       page_holder->GetFrame().GetTaskRunner(TaskType::kInternalTest));
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
   auto* scheduler = MakeGarbageCollected<ResourceLoadScheduler>(
       ResourceLoadScheduler::ThrottlingPolicy::kNormal, context);
   ImageResource* image_resource = ImageResource::CreateForTest(test_url);
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index c51bec5..8e04c77 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -701,6 +701,7 @@
   // |last_request_url_| by destroying |assign_on_scope_exit|.
 
   ResourceRequest cross_origin_request(new_request);
+  cross_origin_request.SetOriginalUrl(initial_request_url_);
 
   // Remove any headers that may have been added by the network layer that cause
   // access control to fail.
diff --git a/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h b/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
index d24636d..ac54710b7 100644
--- a/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
@@ -42,6 +42,7 @@
     return -1;
   }
   bool IsPaused() const override;
+  bool IsDetached() const override { return false; }
   bool IsLoadComplete() const override { return false; }
   bool ShouldBlockLoadingMainResource() const override { return false; }
   bool ShouldBlockLoadingSubResource() const override { return false; }
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 3a06e500..23e7bfd 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -294,6 +294,7 @@
       cc::EventListenerClass) const = 0;
   virtual void SetHasScrollEventHandlers(LocalFrame*, bool) = 0;
   virtual void SetNeedsLowLatencyInput(LocalFrame*, bool) = 0;
+  virtual void SetNeedsUnbufferedInputForDebugger(LocalFrame*, bool) = 0;
   virtual void RequestUnbufferedInputEvents(LocalFrame*) = 0;
   virtual void SetTouchAction(LocalFrame*, TouchAction) = 0;
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 07371aa..26adc982 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -987,6 +987,18 @@
     client->SetNeedsLowLatencyInput(needs_low_latency);
 }
 
+void ChromeClientImpl::SetNeedsUnbufferedInputForDebugger(LocalFrame* frame,
+                                                          bool unbuffered) {
+  DCHECK(frame);
+  WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(frame);
+  WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget();
+  if (!widget)
+    return;
+
+  if (WebWidgetClient* client = widget->Client())
+    client->SetNeedsUnbufferedInputForDebugger(unbuffered);
+}
+
 void ChromeClientImpl::RequestUnbufferedInputEvents(LocalFrame* frame) {
   DCHECK(frame);
   WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(frame);
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index fe8bb83..9bd7236e 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -150,6 +150,7 @@
   // appropriate scroll optimizations can be chosen.
   void SetHasScrollEventHandlers(LocalFrame*, bool has_event_handlers) override;
   void SetNeedsLowLatencyInput(LocalFrame*, bool needs_low_latency) override;
+  void SetNeedsUnbufferedInputForDebugger(LocalFrame*, bool immediate) override;
   void RequestUnbufferedInputEvents(LocalFrame*) override;
   void SetTouchAction(LocalFrame*, TouchAction) override;
 
diff --git a/third_party/blink/renderer/core/page/page_widget_delegate.cc b/third_party/blink/renderer/core/page/page_widget_delegate.cc
index 593daa0..50d836f5 100644
--- a/third_party/blink/renderer/core/page/page_widget_delegate.cc
+++ b/third_party/blink/renderer/core/page/page_widget_delegate.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/core/layout/jank_tracker.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/loader/interactive_detector.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
@@ -123,7 +124,7 @@
     if (interactive_detector)
       interactive_detector->HandleForInputDelay(event);
 
-    if (RuntimeEnabledFeatures::JankTrackingEnabled()) {
+    if (origin_trials::JankTrackingEnabled(document)) {
       if (LocalFrameView* view = document->View())
         view->GetJankTracker().NotifyInput(event);
     }
diff --git a/third_party/blink/renderer/core/page/viewport_description.cc b/third_party/blink/renderer/core/page/viewport_description.cc
index 5d8a3c15..ea6df77 100644
--- a/third_party/blink/renderer/core/page/viewport_description.cc
+++ b/third_party/blink/renderer/core/page/viewport_description.cc
@@ -86,7 +86,7 @@
 
 PageScaleConstraints ViewportDescription::Resolve(
     const FloatSize& initial_viewport_size,
-    Length legacy_fallback_width) const {
+    const Length& legacy_fallback_width) const {
   float result_width = kValueAuto;
 
   Length copy_max_width = max_width;
diff --git a/third_party/blink/renderer/core/page/viewport_description.h b/third_party/blink/renderer/core/page/viewport_description.h
index 05d1aa0..64cf5bf9 100644
--- a/third_party/blink/renderer/core/page/viewport_description.h
+++ b/third_party/blink/renderer/core/page/viewport_description.h
@@ -96,7 +96,7 @@
 
   // All arguments are in CSS units.
   PageScaleConstraints Resolve(const FloatSize& initial_viewport_size,
-                               Length legacy_fallback_width) const;
+                               const Length& legacy_fallback_width) const;
 
   // When --use-zoom-for-dsf is enabled, if the type is kFixed, these Length
   // values (i.e., |min_width|, |max_width|, |min_height|, and |max_height|)
diff --git a/third_party/blink/renderer/core/paint/background_image_geometry.cc b/third_party/blink/renderer/core/paint/background_image_geometry.cc
index 82188880..330c43e 100644
--- a/third_party/blink/renderer/core/paint/background_image_geometry.cc
+++ b/third_party/blink/renderer/core/paint/background_image_geometry.cc
@@ -756,12 +756,12 @@
           // an intrinsic ratio or size.
           tile_size_.SetWidth(positioning_area_size.Width());
         } else if (image_intrinsic_size.Height()) {
-          LayoutUnit adjusted_width = image_intrinsic_size.Width() *
-                                      tile_size_.Height() /
-                                      image_intrinsic_size.Height();
+          float adjusted_width = image_intrinsic_size.Width().ToFloat() /
+                                 image_intrinsic_size.Height().ToFloat() *
+                                 tile_size_.Height().ToFloat();
           if (image_intrinsic_size.Width() >= 1 && adjusted_width < 1)
-            adjusted_width = LayoutUnit(1);
-          tile_size_.SetWidth(adjusted_width);
+            adjusted_width = 1;
+          tile_size_.SetWidth(LayoutUnit(adjusted_width));
         }
       } else if (!layer_width.IsAuto() && layer_height.IsAuto()) {
         if (image->ImageHasRelativeSize()) {
@@ -769,12 +769,12 @@
           // an intrinsic ratio or size.
           tile_size_.SetHeight(positioning_area_size.Height());
         } else if (image_intrinsic_size.Width()) {
-          LayoutUnit adjusted_height = image_intrinsic_size.Height() *
-                                       tile_size_.Width() /
-                                       image_intrinsic_size.Width();
+          float adjusted_height = image_intrinsic_size.Height().ToFloat() /
+                                  image_intrinsic_size.Width().ToFloat() *
+                                  tile_size_.Width().ToFloat();
           if (image_intrinsic_size.Height() >= 1 && adjusted_height < 1)
-            adjusted_height = LayoutUnit(1);
-          tile_size_.SetHeight(adjusted_height);
+            adjusted_height = 1;
+          tile_size_.SetHeight(LayoutUnit(adjusted_height));
         }
       } else if (layer_width.IsAuto() && layer_height.IsAuto()) {
         // If both width and height are auto, use the image's intrinsic size.
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 9af5090..92cfa42 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
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/core/layout/layout_video.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
@@ -1305,16 +1306,17 @@
   FloatPoint new_position = FloatPoint(relative_compositing_bounds.Location() -
                                        graphics_layer_parent_location);
   IntSize new_size = relative_compositing_bounds.Size();
+  const LayoutObject& layout_object = GetLayoutObject();
 
   // An iframe's main GraphicsLayer is positioned by the CLM for the <iframe>
   // element in the parent frame's DOM.
-  bool is_iframe_doc = GetLayoutObject().IsLayoutView() &&
-                       !GetLayoutObject().GetFrame()->IsLocalRoot();
+  bool is_iframe_doc =
+      layout_object.IsLayoutView() && !layout_object.GetFrame()->IsLocalRoot();
   if (new_position != old_position && !is_iframe_doc) {
     graphics_layer_->SetPosition(new_position);
 
-    if (RuntimeEnabledFeatures::JankTrackingEnabled()) {
-      LocalFrameView* frame_view = GetLayoutObject().View()->GetFrameView();
+    if (origin_trials::JankTrackingEnabled(&layout_object.GetDocument())) {
+      LocalFrameView* frame_view = layout_object.View()->GetFrameView();
       frame_view->GetJankTracker().NotifyCompositedLayerMoved(
           OwningLayer(), FloatRect(old_position, FloatSize(old_size)),
           FloatRect(new_position, FloatSize(new_size)));
@@ -2824,6 +2826,8 @@
     if (!image->IsBitmapImage())
       return false;
 
+    UseCounter::Count(GetLayoutObject().GetDocument(),
+                      WebFeature::kDirectlyCompositedImage);
     return true;
   }
 
@@ -3437,8 +3441,9 @@
   } else if (IsScrollableAreaLayer(graphics_layer)) {
     PaintScrollableArea(graphics_layer, context, interest_rect);
   }
-  probe::didPaint(owning_layer_.GetLayoutObject().GetFrame(), graphics_layer,
-                  context, LayoutRect(interest_rect));
+  probe::didPaint(owning_layer_.GetLayoutObject().GetFrame(),
+                  graphics_layer->CcLayer(), context,
+                  LayoutRect(interest_rect));
 #if DCHECK_IS_ON()
   if (Page* page = GetLayoutObject().GetFrame()->GetPage())
     page->SetIsPainting(false);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index 8d4fca2..deb9dc7 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -552,11 +552,6 @@
   return fragment;
 }
 
-void NGPaintFragment::DirtyLinesFromChangedChild(LayoutObject* child) {
-  if (child->IsInline())
-    MarkLineBoxesDirtyFor(*child);
-}
-
 bool NGPaintFragment::FlippedLocalVisualRectFor(
     const LayoutObject* layout_object,
     LayoutRect* visual_rect) {
@@ -642,8 +637,22 @@
   return nullptr;
 }
 
+void NGPaintFragment::DirtyLinesFromChangedChild(LayoutObject* child) {
+  // This function should be called on every child that has
+  // |IsInLayoutNGInlineFormattingContext()|, meaning it was once collected into
+  // |NGInlineNode|.
+  //
+  // New LayoutObjects will be handled in the next |CollectInline()|.
+  DCHECK(child && child->IsInLayoutNGInlineFormattingContext());
+
+  if (child->IsInline() || child->IsFloatingOrOutOfFlowPositioned())
+    MarkLineBoxesDirtyFor(*child);
+}
+
 void NGPaintFragment::MarkLineBoxesDirtyFor(const LayoutObject& layout_object) {
-  DCHECK(layout_object.IsInline()) << layout_object;
+  DCHECK(layout_object.IsInline() ||
+         layout_object.IsFloatingOrOutOfFlowPositioned())
+      << layout_object;
 
   // Since |layout_object| isn't in fragment tree, check preceding siblings.
   // Note: Once we reuse lines below dirty lines, we should check next siblings.
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index f19485f..3526a239 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
@@ -134,7 +135,7 @@
 #endif
   }
 
-  if (RuntimeEnabledFeatures::JankTrackingEnabled())
+  if (origin_trials::JankTrackingEnabled(frame_view.GetFrame().GetDocument()))
     frame_view.GetJankTracker().NotifyPrePaintFinished();
   if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled()) {
     frame_view.GetPaintTimingDetector().NotifyPrePaintFinished();
@@ -374,7 +375,7 @@
 
   CompositingLayerPropertyUpdater::Update(object);
 
-  if (RuntimeEnabledFeatures::JankTrackingEnabled()) {
+  if (origin_trials::JankTrackingEnabled(&object.GetDocument())) {
     object.GetFrameView()->GetJankTracker().NotifyObjectPrePaint(
         object, paint_invalidator_context.old_visual_rect,
         *paint_invalidator_context.painting_layer);
diff --git a/third_party/blink/renderer/core/probe/core_probes.pidl b/third_party/blink/renderer/core/probe/core_probes.pidl
index 93177e8..a3e17b50 100644
--- a/third_party/blink/renderer/core/probe/core_probes.pidl
+++ b/third_party/blink/renderer/core/probe/core_probes.pidl
@@ -85,7 +85,7 @@
   void didFireWebGLWarning(Element*);
   void didFireWebGLErrorOrWarning(Element*, const String& message);
   void didResizeMainFrame(LocalFrame*);
-  void didPaint(LocalFrame*, const GraphicsLayer*, GraphicsContext&, const LayoutRect&);
+  void didPaint(LocalFrame*, const cc::Layer*, GraphicsContext&, const LayoutRect&);
   void applyAcceptLanguageOverride(ExecutionContext*, String* acceptLanguage);
   void applyUserAgentOverride(CoreProbeSink*, String* userAgent);
   void didBlockRequest([Keep] ExecutionContext*, const ResourceRequest&, DocumentLoader*, const FetchInitiatorInfo&, ResourceRequestBlockedReason, ResourceType);
diff --git a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
index b26130a4..c274b606 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -1035,6 +1035,10 @@
 }
 
 TEST_P(FrameThrottlingTest, ThrottleSubtreeAtomically) {
+  // TODO(crbug.com/922419): The test is broken for LayoutNG.
+  if (RuntimeEnabledFeatures::LayoutNGEnabled())
+    return;
+
   // Create two nested frames which are throttled.
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest frame_resource("https://example.com/iframe.html", "text/html");
diff --git a/third_party/blink/renderer/core/script/layered_api.cc b/third_party/blink/renderer/core/script/layered_api.cc
index 434f7b75..c75035d 100644
--- a/third_party/blink/renderer/core/script/layered_api.cc
+++ b/third_party/blink/renderer/core/script/layered_api.cc
@@ -26,10 +26,8 @@
 const LayeredAPIResource kLayeredAPIResources[] = {
     {"blank/index.js", IDR_LAYERED_API_BLANK_INDEX_JS},
 
-    {"async-local-storage/index.js",
-     IDR_LAYERED_API_ASYNC_LOCAL_STORAGE_INDEX_JS},
-    {"async-local-storage/idb_utils.js",
-     IDR_LAYERED_API_ASYNC_LOCAL_STORAGE_IDB_UTILS_JS},
+    {"kv-storage/index.js", IDR_LAYERED_API_KV_STORAGE_INDEX_JS},
+    {"kv-storage/idb_utils.js", IDR_LAYERED_API_KV_STORAGE_IDB_UTILS_JS},
 
     {"virtual-scroller/index.js", IDR_LAYERED_API_VIRTUAL_SCROLLER_INDEX_JS},
     {"virtual-scroller/item-source.js",
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/.eslintrc.js b/third_party/blink/renderer/core/script/resources/layered_api/kv-storage/.eslintrc.js
similarity index 100%
rename from third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/.eslintrc.js
rename to third_party/blink/renderer/core/script/resources/layered_api/kv-storage/.eslintrc.js
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/idb_utils.js b/third_party/blink/renderer/core/script/resources/layered_api/kv-storage/idb_utils.js
similarity index 100%
rename from third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/idb_utils.js
rename to third_party/blink/renderer/core/script/resources/layered_api/kv-storage/idb_utils.js
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js b/third_party/blink/renderer/core/script/resources/layered_api/kv-storage/index.js
similarity index 97%
rename from third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js
rename to third_party/blink/renderer/core/script/resources/layered_api/kv-storage/index.js
index 620c6d7..b8a6fa27 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js
+++ b/third_party/blink/renderer/core/script/resources/layered_api/kv-storage/index.js
@@ -25,14 +25,14 @@
 
 if (!self.isSecureContext) {
   throw new DOMException(
-      'Async local storage is only available in secure contexts',
+      'KV Storage is only available in secure contexts',
       'SecurityError');
 }
 
 export class StorageArea {
   constructor(name) {
     _databasePromise.set(this, null);
-    _databaseName.set(this, `async-local-storage:${name}`);
+    _databaseName.set(this, `kv-storage:${name}`);
   }
 
   async set(key, value) {
diff --git a/third_party/blink/renderer/core/style/style_reflection.h b/third_party/blink/renderer/core/style/style_reflection.h
index cb2de278..68dcb1c 100644
--- a/third_party/blink/renderer/core/style/style_reflection.h
+++ b/third_party/blink/renderer/core/style/style_reflection.h
@@ -45,7 +45,7 @@
   bool operator!=(const StyleReflection& o) const { return !(*this == o); }
 
   CSSReflectionDirection Direction() const { return direction_; }
-  Length Offset() const { return offset_; }
+  const Length& Offset() const { return offset_; }
   const NinePieceImage& Mask() const { return mask_; }
 
   void SetDirection(CSSReflectionDirection dir) { direction_ = dir; }
diff --git a/third_party/blink/renderer/core/testing/data/wheel-event-handler.html b/third_party/blink/renderer/core/testing/data/wheel-event-handler.html
index 7e450a4..51e39d0e 100644
--- a/third_party/blink/renderer/core/testing/data/wheel-event-handler.html
+++ b/third_party/blink/renderer/core/testing/data/wheel-event-handler.html
@@ -3,7 +3,7 @@
 <head>
   <script type="text/javascript">
       window.addEventListener('mousewheel', function(event) {
-      });
+      }, {passive: false});
   </script>
 </head>
 
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 01908d9..39e4bd244 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -1698,7 +1698,8 @@
 unsigned Internals::wheelEventHandlerCount(Document* document) const {
   DCHECK(document);
   return EventHandlerCount(*document,
-                           EventHandlerRegistry::kWheelEventBlocking);
+                           EventHandlerRegistry::kWheelEventBlocking) +
+         EventHandlerCount(*document, EventHandlerRegistry::kWheelEventPassive);
 }
 
 unsigned Internals::scrollEventHandlerCount(Document* document) const {
diff --git a/third_party/blink/renderer/core/timing/performance_layout_jank.idl b/third_party/blink/renderer/core/timing/performance_layout_jank.idl
index 1d6f0752..c44a4ac1 100644
--- a/third_party/blink/renderer/core/timing/performance_layout_jank.idl
+++ b/third_party/blink/renderer/core/timing/performance_layout_jank.idl
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // https://docs.google.com/document/d/1723wu4GZa8x8zSK0vb4NYg9xGtyVISEtGSGHcyL1tko/edit
-[RuntimeEnabled=LayoutJankAPI]
+[OriginTrialEnabled=LayoutJankAPI]
 interface PerformanceLayoutJank : PerformanceEntry {
     readonly attribute double fraction;
     serializer = {inherit, attribute};
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index fd7347d..2f0d6aa 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -53,12 +53,11 @@
 // static
 Vector<AtomicString> PerformanceObserver::supportedEntryTypes() {
   Vector<AtomicString> supportedEntryTypes;
-  // TODO(npm): add "element", "event" and "firstInput" when they ship.
-  // Currently, the support for element timing and event timing relies on origin
-  // trials, and thus depends on the execution context. This cannot be queried
-  // from a static method. See https://crbug.com/841224
-  if (RuntimeEnabledFeatures::LayoutJankAPIEnabled())
-    supportedEntryTypes.push_back(performance_entry_names::kLayoutJank);
+  // TODO(npm): add the following entry types once they have shipped:
+  //   "element", "event", "firstInput", "layoutJank"
+  // Some of these are enabled in origin trials, but the origin trial status
+  // depends on the execution context, so it cannot be queried from a static
+  // method. See crbug.com/922195
   supportedEntryTypes.AppendVector(Vector<AtomicString>(
       {performance_entry_names::kLongtask, performance_entry_names::kMark,
        performance_entry_names::kMeasure, performance_entry_names::kNavigation,
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index be787bd..8616fc2 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -433,7 +433,7 @@
 }
 
 void WindowPerformance::AddLayoutJankFraction(double jank_fraction) {
-  DCHECK(RuntimeEnabledFeatures::LayoutJankAPIEnabled());
+  DCHECK(origin_trials::LayoutJankAPIEnabled(GetExecutionContext()));
   PerformanceEntry* entry = PerformanceLayoutJank::Create(jank_fraction);
   NotifyObserversOfEntry(*entry);
 }
diff --git a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
index f1a3f2e..7701400 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
@@ -5,9 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CONTENT_SETTINGS_PROXY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CONTENT_SETTINGS_PROXY_H_
 
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
index 139a445..16801ba 100644
--- a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
+++ b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
@@ -29,7 +29,8 @@
     platform_->AdvanceClockSeconds(1.);  // For non-zero DocumentParserTimings
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
     auto* context = MakeGarbageCollected<MockFetchContext>();
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
     map_ = MakeGarbageCollected<WorkletModuleResponsesMap>();
   }
 
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 fb0f457..6ee2d70 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -355,7 +355,7 @@
 
   DocumentInit init = DocumentInit::Create()
                           .WithContextDocument(GetDocument()->ContextDocument())
-                          .WithURL(response_.CurrentRequestUrl());
+                          .WithURL(response_.ResponseUrl());
   if (is_html)
     response_document_ = HTMLDocument::Create(init);
   else
@@ -540,7 +540,7 @@
 }
 
 String XMLHttpRequest::responseURL() {
-  KURL response_url(response_.CurrentRequestUrl());
+  KURL response_url(response_.ResponseUrl());
   if (!response_url.IsNull())
     response_url.RemoveFragmentIdentifier();
   return response_url.GetString();
diff --git a/third_party/blink/renderer/devtools/front_end/emulation/DeviceModeWrapper.js b/third_party/blink/renderer/devtools/front_end/emulation/DeviceModeWrapper.js
index f125997a..742d0e1 100644
--- a/third_party/blink/renderer/devtools/front_end/emulation/DeviceModeWrapper.js
+++ b/third_party/blink/renderer/devtools/front_end/emulation/DeviceModeWrapper.js
@@ -118,6 +118,12 @@
               });
             });
             const clip = /** @type {!Protocol.Page.Viewport} */ (JSON.parse(result.object.value));
+            const response = await node.domModel().target().pageAgent().invoke_getLayoutMetrics({});
+            const page_zoom = !response[Protocol.Error] && response.visualViewport.zoom || 1;
+            clip.x *= page_zoom;
+            clip.y *= page_zoom;
+            clip.width *= page_zoom;
+            clip.height *= page_zoom;
             Emulation.DeviceModeView._wrapperInstance._captureScreenshot(false, clip);
           }
           captureClip();
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
index 35ecfe80..e5fd320 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorCommon.css
@@ -157,8 +157,17 @@
     color: rgb(17, 85, 204);
 }
 
+button,
+input,
+select {
+    /* Form elements do not automatically inherit font style from ancestors. */
+    font-family: inherit;
+    font-size: inherit;
+}
+
 input {
     background-color: white;
+    color: inherit;
 }
 
 :host-context(.-theme-with-dark-background) input[type="checkbox"]::not(.-theme-preserve) {
@@ -427,6 +436,7 @@
     background-color: #bbdefb;
 }
 
-.-theme-with-dark-background *::selection {
-    background-color: #9e9e9e;;
+.-theme-with-dark-background *::selection,
+:host-context(.-theme-with-dark-background) *::selection {
+    background-color: #9e9e9e;
 }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css b/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css
index c61b1b1..5a57737 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/inspectorStyle.css
@@ -68,7 +68,7 @@
                    0 2px 6px 2px rgba(0, 0, 0, 0.1);
     --divider-color: #525252;
     --focus-ring-inactive-shadow: 0 0 0 1px #5a5a5a;
-    --editor-selection-bg-color: #454545;
+    --editor-selection-bg-color: hsl(207, 88%, 22%);
     --editor-selection-inactive-bg-color: #454545;
 }
 
diff --git a/third_party/blink/renderer/devtools/front_end/ui/textButton.css b/third_party/blink/renderer/devtools/front_end/ui/textButton.css
index 1a1ff1b..b18868b 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/textButton.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/textButton.css
@@ -11,7 +11,7 @@
     border: 1px solid rgba(0, 0, 0, 0.2);
     border-radius: 4px;
     padding: 0px 12px;
-    font-weight: 600;
+    font-weight: 500;
     color: var(--accent-fg-color);
     background-color: #fff;
     flex: none;
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 278a097..6ca117d 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -372,7 +372,6 @@
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
-    "//third_party/blink/public:media_devices_mojo_bindings_blink",
     "//third_party/blink/renderer/core",
     "//third_party/blink/renderer/modules/storage:unit_tests",
     "//third_party/blink/renderer/platform",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
index 2a7d7baf..a9c06509 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
@@ -111,20 +111,19 @@
   }
 }
 
-void AXInlineTextBox::GetWordBoundaries(Vector<AXRange>& words) const {
+void AXInlineTextBox::GetWordBoundaries(Vector<int>& word_starts,
+                                        Vector<int>& word_ends) const {
   if (!inline_text_box_ ||
       inline_text_box_->GetText().ContainsOnlyWhitespaceOrEmpty())
     return;
 
   Vector<AbstractInlineTextBox::WordBoundaries> boundaries;
   inline_text_box_->GetWordBoundaries(boundaries);
-  words.ReserveCapacity(boundaries.size());
+  word_starts.ReserveCapacity(boundaries.size());
+  word_ends.ReserveCapacity(boundaries.size());
   for (const auto& boundary : boundaries) {
-    const AXRange range(
-        AXPosition::CreatePositionInTextObject(*this, boundary.start_index),
-        AXPosition::CreatePositionInTextObject(*this, boundary.end_index));
-    if (range.IsValid())
-      words.push_back(range);
+    word_starts.push_back(boundary.start_index);
+    word_ends.push_back(boundary.end_index);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
index 7ca785d..3514d42 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h
@@ -58,7 +58,8 @@
   String GetName(ax::mojom::NameFrom&,
                  AXObject::AXObjectVector* name_objects) const override;
   void TextCharacterOffsets(Vector<int>&) const override;
-  void GetWordBoundaries(Vector<AXRange>&) const override;
+  void GetWordBoundaries(Vector<int>& word_starts,
+                         Vector<int>& word_ends) const override;
   void GetRelativeBounds(AXObject** out_container,
                          FloatRect& out_bounds_in_container,
                          SkMatrix44& out_container_transform,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index c588bf7..e973564 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -2564,6 +2564,18 @@
     return true;
   }
 
+  if (HasContentEditableAttributeSet()) {
+    ExceptionState exception_state(v8::Isolate::GetCurrent(),
+                                   ExceptionState::kExecutionContext, nullptr,
+                                   nullptr);
+    ToHTMLElement(GetNode())->setInnerText(string, exception_state);
+    if (exception_state.HadException()) {
+      exception_state.ClearException();
+      return false;
+    }
+    return true;
+  }
+
   return false;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 6721817..6f67e81 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1436,10 +1436,7 @@
     uint32_t pos_in_set;
     if (HasAOMPropertyOrARIAAttribute(AOMUIntProperty::kPosInSet, pos_in_set))
       return pos_in_set;
-
-    return AutoPosInSet();
   }
-
   return 0;
 }
 
@@ -1448,88 +1445,10 @@
     int32_t set_size;
     if (HasAOMPropertyOrARIAAttribute(AOMIntProperty::kSetSize, set_size))
       return set_size;
-
-    return AutoSetSize();
   }
-
   return 0;
 }
 
-int AXNodeObject::AutoPosInSet() const {
-  AXObject* parent = ParentObjectUnignored();
-
-  // Do not continue if the children will need updating soon, because
-  // the calculation requires all the siblings to remain stable.
-  if (!parent || parent->NeedsToUpdateChildren())
-    return 0;
-
-  int pos_in_set = 1;
-  const AXObject::AXObjectVector siblings = parent->Children();
-
-  ax::mojom::Role role = RoleValue();
-  int level = HierarchicalLevel();
-  int index_in_parent = IndexInParent();
-
-  for (int index = index_in_parent - 1; index >= 0; index--) {
-    const AXObject* sibling = siblings[index];
-    ax::mojom::Role sibling_role = sibling->RoleValue();
-    if (sibling_role == ax::mojom::Role::kSplitter ||
-        sibling_role == ax::mojom::Role::kGroup)
-      break;  // Set stops at a separator or an optgroup.
-    if (sibling_role != role || sibling->AccessibilityIsIgnored())
-      continue;
-
-    int sibling_level = sibling->HierarchicalLevel();
-    if (sibling_level < level)
-      break;
-
-    if (sibling_level > level)
-      continue;  // Skip subset
-
-    ++pos_in_set;
-  }
-
-  return pos_in_set;
-}
-
-int AXNodeObject::AutoSetSize() const {
-  AXObject* parent = ParentObjectUnignored();
-
-  // Do not continue if the children will need updating soon, because
-  // the calculation requires all the siblings to remain stable.
-  if (!parent || parent->NeedsToUpdateChildren())
-    return 0;
-
-  int set_size = AutoPosInSet();
-  auto siblings = parent->Children();
-
-  ax::mojom::Role role = RoleValue();
-  int level = HierarchicalLevel();
-  int index_in_parent = IndexInParent();
-  int sibling_count = siblings.size();
-
-  for (int index = index_in_parent + 1; index < sibling_count; index++) {
-    const auto sibling = siblings[index];
-    ax::mojom::Role sibling_role = sibling->RoleValue();
-    if (sibling_role == ax::mojom::Role::kSplitter ||
-        sibling_role == ax::mojom::Role::kGroup)
-      break;  // Set stops at a separator or an optgroup.
-    if (sibling_role != role || sibling->AccessibilityIsIgnored())
-      continue;
-
-    int sibling_level = sibling->HierarchicalLevel();
-    if (sibling_level < level)
-      break;
-
-    if (sibling_level > level)
-      continue;  // Skip subset
-
-    ++set_size;
-  }
-
-  return set_size;
-}
-
 String AXNodeObject::AriaInvalidValue() const {
   if (GetInvalidState() == ax::mojom::InvalidState::kOther)
     return GetAOMPropertyOrARIAAttribute(AOMStringProperty::kInvalid);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index b3768b6c..9873147e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -216,11 +216,6 @@
   // Position in set and Size of set
   int PosInSet() const override;
   int SetSize() const override;
-  // Compute the number of siblings that have the same role before |this|,
-  // following rules for counting the number of items in a set.
-  int AutoPosInSet() const;
-  // Compute the number of unignored siblings with the same role as |this|.
-  int AutoSetSize() const;
 
   // Aria-owns.
   void ComputeAriaOwnsChildren(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index e5bd023..e3bae411 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1727,7 +1727,8 @@
 
 void AXObject::TextCharacterOffsets(Vector<int>&) const {}
 
-void AXObject::GetWordBoundaries(Vector<AXRange>&) const {}
+void AXObject::GetWordBoundaries(Vector<int>& word_starts,
+                                 Vector<int>& word_ends) const {}
 
 ax::mojom::DefaultActionVerb AXObject::Action() const {
   Element* action_element = ActionElement();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 5b6720e9..4810b8f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -714,7 +714,8 @@
   // negative values for RTL.
   virtual void TextCharacterOffsets(Vector<int>&) const;
   // The start and end character offset of each word in the object's text.
-  virtual void GetWordBoundaries(Vector<AXRange>&) const;
+  virtual void GetWordBoundaries(Vector<int>& word_starts,
+                                 Vector<int>& word_ends) const;
 
   // Properties of interactive elements.
   ax::mojom::DefaultActionVerb Action() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index c7b72f7..3bda74c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -661,7 +661,7 @@
       DCHECK(child_node) << "AX objects used in AX positions that are valid "
                             "DOM positions should always be connected to their "
                             "DOM nodes.";
-      if (child_node->NodeIndex() == 0) {
+      if (!child_node->previousSibling()) {
         // Creates a |PositionAnchorType::kBeforeChildren| position.
         container_node = child_node->parentNode();
         DCHECK(container_node);
@@ -683,8 +683,7 @@
                                  "connected to their DOM nodes.";
 
       // Check if this is an "after children" position in the DOM as well.
-      if ((last_child_node->NodeIndex() + 1) ==
-          container_node->CountChildren()) {
+      if (!last_child_node->nextSibling()) {
         // Creates a |PositionAnchorType::kAfterChildren| position.
         container_node = last_child_node->parentNode();
         DCHECK(container_node);
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index 3067bd2..9865edad 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -195,6 +195,13 @@
       MakeGarbageCollected<AnimatorDefinition>(isolate, constructor, animate);
 
   animator_definitions_.Set(name, definition);
+  // TODO(yigu): Currently one animator name is synced back per registration.
+  // Eventually all registered names should be synced in batch once a module
+  // completes its loading in the worklet scope. https://crbug.com/920722.
+  if (AnimationWorkletProxyClient* proxy_client =
+          AnimationWorkletProxyClient::From(Clients())) {
+    proxy_client->SynchronizeAnimatorName(name);
+  }
 }
 
 Animator* AnimationWorkletGlobalScope::CreateInstance(
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
index d55db5a3..099c296 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
@@ -44,6 +44,7 @@
   void SetGlobalScope(WorkletGlobalScope*) override {
     did_set_global_scope_ = true;
   }
+  void SynchronizeAnimatorName(const String&) override{};
   bool did_set_global_scope() { return did_set_global_scope_; }
 
  private:
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
index 8599532..e3f052b 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
@@ -38,6 +38,27 @@
   AnimationWorkletMutator::Trace(visitor);
 }
 
+void AnimationWorkletProxyClient::SynchronizeAnimatorName(
+    const String& animator_name) {
+  if (state_ == RunState::kDisposed)
+    return;
+
+  // Animator registration is processed before the loading promise being
+  // resolved which is also done with a posted task (See
+  // WorkletModuleTreeClient::NotifyModuleTreeLoadFinished). Since both are
+  // posted task and a SequencedTaskRunner is used, we are guaranteed that
+  // registered names are synced before resolving the load promise therefore it
+  // is safe to use a post task here.
+  for (auto& mutator_item : mutator_items_) {
+    DCHECK(mutator_item.mutator_runner);
+    PostCrossThreadTask(
+        *mutator_item.mutator_runner, FROM_HERE,
+        CrossThreadBind(
+            &AnimationWorkletMutatorDispatcherImpl::SynchronizeAnimatorName,
+            mutator_item.mutator_dispatcher, animator_name));
+  }
+}
+
 void AnimationWorkletProxyClient::SetGlobalScope(
     WorkletGlobalScope* global_scope) {
   DCHECK(global_scope);
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
index 6de77a3..357e6ab 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
@@ -45,6 +45,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_mutatee_runner);
   void Trace(blink::Visitor*) override;
 
+  virtual void SynchronizeAnimatorName(const String& animator_name);
   virtual void SetGlobalScope(WorkletGlobalScope*);
   void Dispose();
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index d7e30ba..966439c 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -193,12 +193,20 @@
     return nullptr;
   }
 
+  Document& document = keyframe_effects.at(0)->target()->GetDocument();
+  if (!document.GetWorkletAnimationController().IsAnimatorRegistered(
+          animator_name)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "The animator '" + animator_name + "' has not yet been registered.");
+    return nullptr;
+  }
+
   AnimationWorklet* worklet =
       CSSAnimationWorklet::animationWorklet(script_state);
 
   WorkletAnimationId id = worklet->NextWorkletAnimationId();
 
-  Document& document = keyframe_effects.at(0)->target()->GetDocument();
   AnimationTimeline* animation_timeline =
       ConvertAnimationTimeline(document, timeline);
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
index 5503267..f095085 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
+#include "third_party/blink/renderer/core/animation/worklet_animation_controller.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -44,6 +45,7 @@
 WorkletAnimation* CreateWorkletAnimation(
     ScriptState* script_state,
     Element* element,
+    const String& animator_name,
     ScrollTimeline* scroll_timeline = nullptr) {
   AnimationEffectOrAnimationEffectSequence effects;
   AnimationEffect* effect = CreateKeyframeEffect(element);
@@ -55,7 +57,7 @@
 
   ScriptState::Scope scope(script_state);
   DummyExceptionStateForTesting exception_state;
-  return WorkletAnimation::Create(script_state, "WorkletAnimation", effects,
+  return WorkletAnimation::Create(script_state, animator_name, effects,
                                   timeline, std::move(options),
                                   exception_state);
 }
@@ -70,7 +72,14 @@
   void SetUp() override {
     RenderingTest::SetUp();
     element_ = GetDocument().CreateElementForBinding("test");
-    worklet_animation_ = CreateWorkletAnimation(GetScriptState(), element_);
+    // Animator has to be registored before constructing WorkletAnimation. For
+    // unit test this is faked by adding the animator name to
+    // WorkletAnimationController.
+    animator_name_ = "WorkletAnimationTest";
+    GetDocument().GetWorkletAnimationController().SynchronizeAnimatorName(
+        animator_name_);
+    worklet_animation_ =
+        CreateWorkletAnimation(GetScriptState(), element_, animator_name_);
   }
 
   ScriptState* GetScriptState() {
@@ -79,6 +88,7 @@
 
   Persistent<Element> element_;
   Persistent<WorkletAnimation> worklet_animation_;
+  String animator_name_;
 };
 
 TEST_F(WorkletAnimationTest, WorkletAnimationInElementAnimations) {
@@ -103,15 +113,18 @@
 
 TEST_F(WorkletAnimationTest,
        CurrentTimeFromDocumentTimelineIsOffsetByStartTime) {
-  GetDocument().GetAnimationClock().ResetTimeForTesting();
-  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+  // Only expect precision up to 1 microsecond with an additional smaller
+  // component to account for double rounding/conversion error.
+  double error =
+      base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF() + 1e-13;
+
   WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
   base::TimeTicks first_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
   base::TimeTicks second_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4);
 
-  GetDocument().GetAnimationClock().UpdateTime(first_ticks);
+  GetDocument().GetAnimationClock().ResetTimeForTesting(first_ticks);
   DummyExceptionStateForTesting exception_state;
   worklet_animation_->play(exception_state);
   worklet_animation_->UpdateCompositingState();
@@ -124,7 +137,7 @@
       state->TakeWorkletState(id.scope_id);
   EXPECT_NEAR(0, input->added_and_updated_animations[0].current_time, error);
   state.reset(new AnimationWorkletDispatcherInput);
-  GetDocument().GetAnimationClock().UpdateTime(second_ticks);
+  GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
   worklet_animation_->UpdateInputState(state.get());
   input = state->TakeWorkletState(id.scope_id);
   EXPECT_NEAR(123.4, input->updated_animations[0].current_time, error);
@@ -156,15 +169,18 @@
   options->setScrollSource(GetElementById("scroller"));
   ScrollTimeline* scroll_timeline =
       ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
-  WorkletAnimation* worklet_animation =
-      CreateWorkletAnimation(GetScriptState(), element_, scroll_timeline);
+  WorkletAnimation* worklet_animation = CreateWorkletAnimation(
+      GetScriptState(), element_, animator_name_, scroll_timeline);
   WorkletAnimationId id = worklet_animation->GetWorkletAnimationId();
 
   DummyExceptionStateForTesting exception_state;
   worklet_animation->play(exception_state);
   worklet_animation->UpdateCompositingState();
 
-  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+  // Only expect precision up to 1 microsecond with an additional smaller
+  // component to account for double rounding/conversion error.
+  double error =
+      base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF() + 1e-13;
   scrollable_area->SetScrollOffset(ScrollOffset(0, 40), kProgrammaticScroll);
   std::unique_ptr<AnimationWorkletDispatcherInput> state =
       std::make_unique<AnimationWorkletDispatcherInput>();
@@ -182,14 +198,13 @@
 }
 
 TEST_F(WorkletAnimationTest, MainThreadSendsPeekRequestTest) {
-  GetDocument().GetAnimationClock().ResetTimeForTesting();
   WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
   base::TimeTicks first_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
   base::TimeTicks second_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4);
 
-  GetDocument().GetAnimationClock().UpdateTime(first_ticks);
+  GetDocument().GetAnimationClock().ResetTimeForTesting(first_ticks);
   DummyExceptionStateForTesting exception_state;
   worklet_animation_->play(exception_state);
   worklet_animation_->UpdateCompositingState();
@@ -231,7 +246,7 @@
   state.reset(new AnimationWorkletDispatcherInput);
 
   // Input time changes. Need to peek again.
-  GetDocument().GetAnimationClock().UpdateTime(second_ticks);
+  GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
   worklet_animation_->UpdateInputState(state.get());
   input = state->TakeWorkletState(id.scope_id);
   EXPECT_EQ(input->peeked_animations.size(), 1u);
diff --git a/third_party/blink/renderer/modules/battery/battery_manager.cc b/third_party/blink/renderer/modules/battery/battery_manager.cc
index 7b21cf3c..e216d890ee 100644
--- a/third_party/blink/renderer/modules/battery/battery_manager.cc
+++ b/third_party/blink/renderer/modules/battery/battery_manager.cc
@@ -96,12 +96,12 @@
   return BatteryDispatcher::Instance().LatestData();
 }
 
-void BatteryManager::Pause() {
+void BatteryManager::ContextPaused(PauseState) {
   has_event_listener_ = false;
   StopUpdating();
 }
 
-void BatteryManager::Unpause() {
+void BatteryManager::ContextUnpaused() {
   has_event_listener_ = true;
   StartUpdating();
 }
diff --git a/third_party/blink/renderer/modules/battery/battery_manager.h b/third_party/blink/renderer/modules/battery/battery_manager.h
index 9330820..d062efb 100644
--- a/third_party/blink/renderer/modules/battery/battery_manager.h
+++ b/third_party/blink/renderer/modules/battery/battery_manager.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_property.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/frame/platform_event_controller.h"
 #include "third_party/blink/renderer/modules/battery/battery_status.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
@@ -59,8 +59,8 @@
   bool HasLastData() override;
 
   // PausableObject implementation.
-  void Pause() override;
-  void Unpause() override;
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
   void ContextDestroyed(ExecutionContext*) override;
 
   // ScriptWrappable implementation.
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index c67ae96..2ae91d9 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -496,8 +496,19 @@
                                    base::Time response_time,
                                    base::TimeTicks) {
     ServiceWorkerGlobalScope* global_scope = GetServiceWorkerGlobalScope();
+    // |cache_ptr_| can be disconnected when the wrapper of CacheStorage is
+    // gone.
+    if (!cache_->cache_ptr_.is_bound()) {
+      global_scope->DidEndTask(task_id);
+      return;
+    }
+
     scoped_refptr<CachedMetadata> cached_metadata =
         GenerateFullCodeCache(array_buffer);
+    if (!cached_metadata) {
+      global_scope->DidEndTask(task_id);
+      return;
+    }
     cache_->cache_ptr_->SetSideData(
         url_, response_time, cached_metadata->SerializedData(),
         WTF::Bind(
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 93026d8c..a693295 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1324,17 +1324,16 @@
   if (IsDetached())
     return;
 
-  Vector<AXRange> word_boundaries;
-  private_->GetWordBoundaries(word_boundaries);
+  Vector<int> src_starts;
+  Vector<int> src_ends;
+  private_->GetWordBoundaries(src_starts, src_ends);
+  DCHECK_EQ(src_starts.size(), src_ends.size());
 
-  WebVector<int> word_start_offsets(word_boundaries.size());
-  WebVector<int> word_end_offsets(word_boundaries.size());
-  for (wtf_size_t i = 0; i < word_boundaries.size(); ++i) {
-    DCHECK(word_boundaries[i].IsValid());
-    DCHECK_EQ(word_boundaries[i].Start().ContainerObject(),
-              word_boundaries[i].End().ContainerObject());
-    word_start_offsets[i] = word_boundaries[i].Start().TextOffset();
-    word_end_offsets[i] = word_boundaries[i].End().TextOffset();
+  WebVector<int> word_start_offsets(src_starts.size());
+  WebVector<int> word_end_offsets(src_ends.size());
+  for (wtf_size_t i = 0; i < src_starts.size(); ++i) {
+    word_start_offsets[i] = src_starts[i];
+    word_end_offsets[i] = src_ends[i];
   }
 
   starts.Swap(word_start_offsets);
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index d742c70..5952bd1 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -2030,6 +2030,9 @@
 
 void MediaControlsImpl::ElementSizeChangedTimerFired(TimerBase*) {
   ComputeWhichControlsFit();
+
+  // Rerender timeline bar segments when size changed.
+  timeline_->RenderBarSegments();
 }
 
 void MediaControlsImpl::OnLoadingProgress() {
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 00f60af..418f198 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -41,8 +41,4 @@
     "user_media_request.cc",
     "user_media_request.h",
   ]
-
-  deps = [
-    "//third_party/blink/public:media_devices_mojo_bindings_blink",
-  ]
 }
diff --git a/third_party/blink/renderer/modules/mediastream/media_device_info.h b/third_party/blink/renderer/modules/mediastream/media_device_info.h
index 737a37b..d91fa45 100644
--- a/third_party/blink/renderer/modules/mediastream/media_device_info.h
+++ b/third_party/blink/renderer/modules/mediastream/media_device_info.h
@@ -26,7 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_DEVICE_INFO_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_DEVICE_INFO_H_
 
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom-blink.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.h b/third_party/blink/renderer/modules/mediastream/media_devices.h
index aff61a76..59dfeae 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.h
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.h
@@ -7,7 +7,7 @@
 
 #include "base/callback.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom-blink.h"
+#include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h
index 0d837e8..ec2b119 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h
@@ -6,7 +6,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
-#include "third_party/webrtc/api/dtlstransportinterface.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
 
 // The DtlsTransportProxy class takes care of thread-jumping when
 // connecting callbacks from a webrtc::DtlsTransport to a
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
index 7735411..bbe20a5f 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_ICE_TRANSPORT_ADAPTER_H_
 
-#include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
+#include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
index 6907f86..c3e9ade 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_proxy.h
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/ice_transport_adapter_cross_thread_factory.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
+#include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
 
 namespace rtc {
 class Thread;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
index ace4820eb..99214c7 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
 
-#include "third_party/webrtc/rtc_base/sslfingerprint.h"
+#include "third_party/webrtc/rtc_base/ssl_fingerprint.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
index 938aaa6c..ca83498b 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.cc
index f4611fdb..4d52cbc 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory_impl.cc
@@ -7,7 +7,7 @@
 #include "net/third_party/quic/core/quic_packet_writer.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index b45ca2d..1f0ee6d 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -19,7 +19,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index 0746e43b..0e47e54 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -12,9 +12,9 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport_delegate.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
-#include "third_party/webrtc/rtc_base/sslfingerprint.h"
-#include "third_party/webrtc/rtc_base/sslidentity.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
+#include "third_party/webrtc/rtc_base/ssl_fingerprint.h"
+#include "third_party/webrtc/rtc_base/ssl_identity.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter.h b/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter.h
index 7ce530f6..97689998 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h"
-#include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
+#include "third_party/webrtc/p2p/base/p2p_transport_channel.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter_test.cc
index 6eb662cb..2c3785e 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter_test.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/adapters/quic_packet_transport_adapter.h"
 
 #include "net/test/gtest_util.h"
-#include "third_party/webrtc/p2p/base/mockicetransport.h"
+#include "third_party/webrtc/p2p/base/mock_ice_transport.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
index d6ac0d6..c07ad33 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.cc
@@ -33,7 +33,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/bindings/to_v8.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
-#include "third_party/webrtc/rtc_base/sslcertificate.h"
+#include "third_party/webrtc/rtc_base/ssl_certificate.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
index 69dbc5a9..76304d7 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
@@ -40,7 +40,7 @@
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "third_party/webrtc/rtc_base/rtccertificate.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
index 1f067bc..059ad04 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.cc
@@ -21,13 +21,13 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event_init.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/webrtc/api/dtlstransportinterface.h"
-#include "third_party/webrtc/api/jsepicecandidate.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/p2p/base/portallocator.h"
-#include "third_party/webrtc/p2p/base/transportdescription.h"
-#include "third_party/webrtc/pc/iceserverparsing.h"
-#include "third_party/webrtc/pc/webrtcsdp.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
+#include "third_party/webrtc/api/jsep_ice_candidate.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/p2p/base/port_allocator.h"
+#include "third_party/webrtc/p2p/base/transport_description.h"
+#include "third_party/webrtc/pc/ice_server_parsing.h"
+#include "third_party/webrtc/pc/webrtc_sdp.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
index 78f24173..5577610 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtls_transport.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/peerconnection/adapters/dtls_transport_proxy.h"
-#include "third_party/webrtc/api/dtlstransportinterface.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_util.h b/third_party/blink/renderer/modules/peerconnection/rtc_error_util.h
index 0fd2dce..6a61f13 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_error_util.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_util.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_UTIL_H_
 
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/webrtc/api/rtcerror.h"
+#include "third_party/webrtc/api/rtc_error.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
index 32caf50..b3384e21 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport.cc
@@ -22,12 +22,12 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event_init.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_quic_transport.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/webrtc/api/jsepicecandidate.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/p2p/base/portallocator.h"
-#include "third_party/webrtc/p2p/base/transportdescription.h"
-#include "third_party/webrtc/pc/iceserverparsing.h"
-#include "third_party/webrtc/pc/webrtcsdp.h"
+#include "third_party/webrtc/api/jsep_ice_candidate.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/p2p/base/port_allocator.h"
+#include "third_party/webrtc/p2p/base/transport_description.h"
+#include "third_party/webrtc/pc/ice_server_parsing.h"
+#include "third_party/webrtc/pc/webrtc_sdp.h"
 
 namespace blink {
 namespace {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.cc
index a4b6e64..06d3d768 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_transport_test.cc
@@ -15,7 +15,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate_init.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event.h"
-#include "third_party/webrtc/pc/webrtcsdp.h"
+#include "third_party/webrtc/pc/webrtc_sdp.h"
 
 namespace blink {
 namespace {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 2d6d9e4..0d457e6 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -118,10 +118,10 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
-#include "third_party/webrtc/api/dtlstransportinterface.h"
+#include "third_party/webrtc/api/dtls_transport_interface.h"
 #include "third_party/webrtc/api/jsep.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
-#include "third_party/webrtc/pc/sessiondescription.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
+#include "third_party/webrtc/pc/session_description.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index 0e433dd..ef83d51d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -32,7 +32,7 @@
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h"
-#include "third_party/webrtc/api/rtcerror.h"
+#include "third_party/webrtc/api/rtc_error.h"
 #include "v8/include/v8.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
index 2c62b29..30b038c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
@@ -11,7 +11,7 @@
 
 #include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_packet_transport.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_gather_options.h"
-#include "third_party/webrtc/rtc_base/rtccertificategenerator.h"
+#include "third_party/webrtc/rtc_base/rtc_certificate_generator.h"
 
 namespace blink {
 namespace {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index f85bd8c..b540e08 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -15,7 +15,7 @@
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/webrtc/api/rtpparameters.h"
+#include "third_party/webrtc/api/rtp_parameters.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index 1830a0a..1abbf95a 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -15,7 +15,7 @@
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h
index 11777110..0fc44e3 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h
@@ -14,7 +14,7 @@
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/webrtc/api/rtptransceiverinterface.h"
+#include "third_party/webrtc/api/rtp_transceiver_interface.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/permissions/permission_status.cc b/third_party/blink/renderer/modules/permissions/permission_status.cc
index bbac4fbec..8fbd71d 100644
--- a/third_party/blink/renderer/modules/permissions/permission_status.cc
+++ b/third_party/blink/renderer/modules/permissions/permission_status.cc
@@ -59,11 +59,11 @@
   return binding_.is_bound();
 }
 
-void PermissionStatus::Unpause() {
+void PermissionStatus::ContextUnpaused() {
   StartListening();
 }
 
-void PermissionStatus::Pause() {
+void PermissionStatus::ContextPaused(PauseState) {
   StopListening();
 }
 
diff --git a/third_party/blink/renderer/modules/permissions/permission_status.h b/third_party/blink/renderer/modules/permissions/permission_status.h
index 797286c2..0e4d8ad226 100644
--- a/third_party/blink/renderer/modules/permissions/permission_status.h
+++ b/third_party/blink/renderer/modules/permissions/permission_status.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/public/platform/modules/permissions/permission.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -55,8 +55,8 @@
   bool HasPendingActivity() const final;
 
   // PausableObject implementation.
-  void Pause() override;
-  void Unpause() override;
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
   void ContextDestroyed(ExecutionContext*) override;
 
   String state() const;
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability.cc b/third_party/blink/renderer/modules/presentation/presentation_availability.cc
index 5a2be15..eec6565 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability.cc
@@ -76,11 +76,11 @@
   return state_ != State::kInactive;
 }
 
-void PresentationAvailability::Unpause() {
+void PresentationAvailability::ContextUnpaused() {
   SetState(State::kActive);
 }
 
-void PresentationAvailability::Pause() {
+void PresentationAvailability::ContextPaused(PauseState) {
   SetState(State::kSuspended);
 }
 
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability.h b/third_party/blink/renderer/modules/presentation/presentation_availability.h
index bbef7b4..c311012 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability.h
@@ -9,7 +9,7 @@
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/page/page_visibility_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/presentation/presentation_availability_observer.h"
@@ -54,8 +54,8 @@
   bool HasPendingActivity() const final;
 
   // PausableObject implementation.
-  void Pause() override;
-  void Unpause() override;
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
   void ContextDestroyed(ExecutionContext*) override;
 
   // PageVisibilityObserver implementation.
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy.h b/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy.h
index 75ce9b1..99b66ac 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy.h
@@ -5,9 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTENT_SETTINGS_PROXY_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTENT_SETTINGS_PROXY_H_
 
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
-#include "third_party/blink/public/web/worker_content_settings_proxy.mojom-blink.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/vr/vr_display.cc b/third_party/blink/renderer/modules/vr/vr_display.cc
index b31ddbee..c9e0b79 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.cc
+++ b/third_party/blink/renderer/modules/vr/vr_display.cc
@@ -151,9 +151,7 @@
 
 VRDisplay::~VRDisplay() = default;
 
-void VRDisplay::Pause() {}
-
-void VRDisplay::Unpause() {
+void VRDisplay::ContextUnpaused() {
   RequestVSync();
 }
 
diff --git a/third_party/blink/renderer/modules/vr/vr_display.h b/third_party/blink/renderer/modules/vr/vr_display.h
index 03a7faa..998343f 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.h
+++ b/third_party/blink/renderer/modules/vr/vr_display.h
@@ -11,7 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_frame_request_callback.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/modules/vr/vr_display_capabilities.h"
 #include "third_party/blink/renderer/modules/vr/vr_layer_init.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h"
@@ -131,8 +131,7 @@
   bool HasPendingActivity() const final;
 
   // PausableObject:
-  void Pause() override;
-  void Unpause() override;
+  void ContextUnpaused() override;
 
   void OnChanged(device::mojom::blink::VRDisplayInfoPtr, bool is_immersive);
   void OnExitPresent(bool is_immersive);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
index 9a7cbde..e164c124 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
@@ -17,6 +17,7 @@
 namespace blink {
 
 namespace {
+static bool web_audio_device_paused_;
 
 class MockWebAudioDeviceForAudioContext : public WebAudioDevice {
  public:
@@ -27,8 +28,8 @@
 
   void Start() override {}
   void Stop() override {}
-  void Pause() override {}
-  void Resume() override {}
+  void Pause() override { web_audio_device_paused_ = true; }
+  void Resume() override { web_audio_device_paused_ = false; }
   double SampleRate() override { return sample_rate_; }
   int FramesPerBuffer() override { return frames_per_buffer_; }
 
@@ -171,4 +172,17 @@
   platform->RunUntilIdle();
 }
 
+TEST_F(AudioContextTest, ExecutionContextPaused) {
+  AudioContextOptions* options = AudioContextOptions::Create();
+  AudioContext* audio_context =
+      AudioContext::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+
+  audio_context->set_was_audible_for_testing(true);
+  EXPECT_FALSE(web_audio_device_paused_);
+  GetDocument().PausePausableObjects(PauseState::kFrozen);
+  EXPECT_TRUE(web_audio_device_paused_);
+  GetDocument().UnpausePausableObjects();
+  EXPECT_FALSE(web_audio_device_paused_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
index 818b3ac..e024216 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
@@ -47,6 +47,8 @@
 
   virtual void StartRendering() = 0;
   virtual void StopRendering() = 0;
+  virtual void Pause() = 0;
+  virtual void Resume() = 0;
 
   // The render thread needs to be changed after Worklet JS code is loaded by
   // AudioWorklet. This method ensures the switching of render thread and the
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
index 153e8d8..a16b9ac2 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
@@ -1413,7 +1413,12 @@
   auto sample_rate = current_state.sample_rate;
 
   double delta_time = time2 - time1;
-  float k = delta_time > 0 ? 1 / delta_time : 0;
+  DCHECK_GE(delta_time, 0);
+  // Since delta_time is a double, 1/delta_time can easily overflow a float.
+  // Thus, if delta_time is close enough to zero (less than float min), treat it
+  // as zero.
+  float k =
+      delta_time <= std::numeric_limits<float>::min() ? 0 : 1 / delta_time;
   const float value_delta = value2 - value1;
 #if defined(ARCH_CPU_X86_FAMILY)
   if (fill_to_frame > write_index) {
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 33057fd..4a55dfb1 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -176,6 +176,15 @@
   DCHECK_EQ(active_source_nodes_.size(), 0u);
 }
 
+void BaseAudioContext::ContextPaused(PauseState pause_state) {
+  if (pause_state == PauseState::kFrozen)
+    destination()->GetAudioDestinationHandler().Pause();
+}
+
+void BaseAudioContext::ContextUnpaused() {
+  destination()->GetAudioDestinationHandler().Resume();
+}
+
 void BaseAudioContext::ContextDestroyed(ExecutionContext*) {
   destination()->GetAudioDestinationHandler().ContextDestroyed();
   Uninitialize();
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
index 2bc95134..575b605 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
@@ -33,7 +33,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_error_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_success_callback.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
@@ -119,6 +119,8 @@
   }
 
   // Document notification
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
   void ContextDestroyed(ExecutionContext*) override;
   bool HasPendingActivity() const override;
 
diff --git a/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.cc
index 6b587360..ce42fc0c 100644
--- a/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.cc
@@ -126,6 +126,20 @@
   StopPlatformDestination();
 }
 
+void DefaultAudioDestinationHandler::Pause() {
+  DCHECK(IsMainThread());
+  if (platform_destination_) {
+    platform_destination_->Pause();
+  }
+}
+
+void DefaultAudioDestinationHandler::Resume() {
+  DCHECK(IsMainThread());
+  if (platform_destination_) {
+    platform_destination_->Resume();
+  }
+}
+
 void DefaultAudioDestinationHandler::RestartRendering() {
   DCHECK(IsMainThread());
 
diff --git a/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.h
index 96fce28..f693c0c1a 100644
--- a/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.h
+++ b/third_party/blink/renderer/modules/webaudio/default_audio_destination_node.h
@@ -58,6 +58,8 @@
   // For AudioDestinationHandler.
   void StartRendering() override;
   void StopRendering() override;
+  void Pause() override;
+  void Resume() override;
   void RestartRendering() override;
   uint32_t MaxChannelCount() const override;
   double SampleRate() const override;
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
index e854d86..306368f 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
@@ -135,6 +135,14 @@
   NOTREACHED();
 }
 
+void OfflineAudioDestinationHandler::Pause() {
+  NOTREACHED();
+}
+
+void OfflineAudioDestinationHandler::Resume() {
+  NOTREACHED();
+}
+
 void OfflineAudioDestinationHandler::InitializeOfflineRenderThread(
     AudioBuffer* render_target) {
   DCHECK(IsMainThread());
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
index df9ffd4e..dc22545 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
@@ -62,6 +62,8 @@
   // AudioDestinationHandler
   void StartRendering() override;
   void StopRendering() override;
+  void Pause() override;
+  void Resume() override;
   uint32_t MaxChannelCount() const override;
 
   void RestartRendering() override;
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.cc b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
index cd9bc2c..b75ba886 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
@@ -690,11 +690,11 @@
   return channel_ || !event_queue_->IsEmpty();
 }
 
-void DOMWebSocket::Pause() {
+void DOMWebSocket::ContextPaused(PauseState) {
   event_queue_->Pause();
 }
 
-void DOMWebSocket::Unpause() {
+void DOMWebSocket::ContextUnpaused() {
   event_queue_->Unpause();
 
   // If |consumed_buffered_amount_| was updated while the object was paused then
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.h b/third_party/blink/renderer/modules/websockets/dom_websocket.h
index b9e6bc9..e4481bc 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.h
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.h
@@ -37,7 +37,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/pausable_object.h"
+#include "third_party/blink/renderer/core/execution_context/pausable_object.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -124,8 +124,8 @@
 
   // PausableObject functions.
   void ContextDestroyed(ExecutionContext*) override;
-  void Pause() override;
-  void Unpause() override;
+  void ContextPaused(PauseState) override;
+  void ContextUnpaused() override;
 
   // ScriptWrappable functions.
   // Prevent this instance from being collected while it's not in CLOSED
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.h b/third_party/blink/renderer/platform/bindings/callback_function_base.h
index d97c1e5..e13ff14 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.h
@@ -101,9 +101,6 @@
   Member<ScriptState> incumbent_script_state_;
 
   friend class V8PersistentCallbackFunctionBase;
-  friend v8::Local<v8::Value> ToV8(CallbackFunctionBase* callback,
-                                   v8::Local<v8::Object> creation_context,
-                                   v8::Isolate*);
 };
 
 // V8PersistentCallbackFunctionBase retains the underlying v8::Function of a
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
index 9eba0bc4..902d3bad 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -107,10 +107,6 @@
   Member<ScriptState> incumbent_script_state_;
 
   friend class V8PersistentCallbackInterfaceBase;
-  // ToV8 needs to call |CallbackObject| member function.
-  friend v8::Local<v8::Value> ToV8(CallbackInterfaceBase* callback,
-                                   v8::Local<v8::Object> creation_context,
-                                   v8::Isolate*);
 };
 
 // V8PersistentCallbackInterfaceBase retains the underlying v8::Object of a
diff --git a/third_party/blink/renderer/platform/bindings/to_v8.h b/third_party/blink/renderer/platform/bindings/to_v8.h
index be64af69..0037001 100644
--- a/third_party/blink/renderer/platform/bindings/to_v8.h
+++ b/third_party/blink/renderer/platform/bindings/to_v8.h
@@ -60,11 +60,12 @@
 inline v8::Local<v8::Value> ToV8(CallbackInterfaceBase* callback,
                                  v8::Local<v8::Object> creation_context,
                                  v8::Isolate* isolate) {
-  // |creation_context| is intentionally ignored. Callback interface objects
-  // are not wrappers nor clonable. ToV8 on a callback interface object must
-  // be used only when it's the same origin-domain in the same world.
-  DCHECK(!callback || (callback->CallbackRelevantScriptState()->GetContext() ==
-                       creation_context->CreationContext()));
+  // |creation_context| is intentionally ignored. Callback interfaces are not
+  // wrappers nor clonable. ToV8 on a callback interface must be used only when
+  // it's in the same world.
+  DCHECK(!callback ||
+         (&callback->GetWorld() ==
+          &ScriptState::From(creation_context->CreationContext())->World()));
   return callback ? callback->CallbackObject().As<v8::Value>()
                   : v8::Null(isolate).As<v8::Value>();
 }
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index 286717cd..036b902 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -66,8 +66,8 @@
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/webrtc/api/rtpparameters.h"
-#include "third_party/webrtc/p2p/base/portallocator.h"
+#include "third_party/webrtc/api/rtp_parameters.h"
+#include "third_party/webrtc/p2p/base/port_allocator.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
index b739c07..fd75d25 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h"
+
 #include "base/barrier_closure.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/timer/elapsed_timer.h"
@@ -143,6 +144,11 @@
   mutator_map_.erase(mutator);
 }
 
+void AnimationWorkletMutatorDispatcherImpl::SynchronizeAnimatorName(
+    const String& animator_name) {
+  client_->SynchronizeAnimatorName(animator_name);
+}
+
 bool AnimationWorkletMutatorDispatcherImpl::HasMutators() {
   return !mutator_map_.IsEmpty();
 }
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
index 219c509..a1949119 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h
@@ -63,6 +63,8 @@
 
   void SetClient(MutatorClient* client) { client_ = client; }
 
+  void SynchronizeAnimatorName(const String& animator_name);
+
  private:
   class OutputVectorRef;
 
diff --git a/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h b/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
index 8823cf44e..9445cc7 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
+++ b/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
@@ -21,6 +21,7 @@
       std::unique_ptr<AnimationWorkletMutatorDispatcherImpl>);
   ~CompositorMutatorClient() override;
 
+  void SynchronizeAnimatorName(const String& animator_name) override {}
   void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>) override;
 
   // cc::LayerTreeMutator
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 6545f7f..b0797b0 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -90,7 +90,6 @@
       parent_(nullptr),
       mask_layer_(nullptr),
       contents_clipping_mask_layer_(nullptr),
-      paint_count_(0),
       contents_layer_(nullptr),
       contents_layer_id_(0),
       rendering_context3d_(0),
@@ -152,6 +151,18 @@
   CcLayer()->SetIsContainerForFixedPositionLayers(is_container);
 }
 
+void GraphicsLayer::SetCompositingReasons(CompositingReasons reasons) {
+  CcLayer()->set_compositing_reasons(reasons);
+}
+
+CompositingReasons GraphicsLayer::GetCompositingReasons() const {
+  return CcLayer()->compositing_reasons();
+}
+
+void GraphicsLayer::SetOwnerNodeId(int node_id) {
+  CcLayer()->set_owner_node_id(node_id);
+}
+
 void GraphicsLayer::SetParent(GraphicsLayer* layer) {
 #if DCHECK_IS_ON()
   DCHECK(!layer || !layer->HasAncestor(this));
@@ -357,8 +368,6 @@
   if (client_.ShouldThrottleRendering())
     return false;
 
-  IncrementPaintCount();
-
   IntRect new_interest_rect;
   if (!interest_rect) {
     new_interest_rect =
@@ -963,7 +972,7 @@
 
   traced_value->BeginArray("compositing_reasons");
   for (const char* description :
-       CompositingReason::Descriptions(compositing_reasons_))
+       CompositingReason::Descriptions(GetCompositingReasons()))
     traced_value->AppendString(description);
   traced_value->EndArray();
 
@@ -973,8 +982,8 @@
     traced_value->AppendString(description);
   traced_value->EndArray();
 
-  if (owner_node_id_)
-    traced_value->SetInteger("owner_node", owner_node_id_);
+  if (auto node_id = layer_->owner_node_id())
+    traced_value->SetInteger("owner_node", node_id);
 
   if (auto* tracking = GetRasterInvalidationTracking()) {
     tracking->AddToTracedValue(*traced_value);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index f0b10230..80d8ab4 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -89,19 +89,17 @@
 
   GraphicsLayerClient& Client() const { return client_; }
 
-  void SetCompositingReasons(CompositingReasons reasons) {
-    compositing_reasons_ = reasons;
-  }
-  CompositingReasons GetCompositingReasons() const {
-    return compositing_reasons_;
-  }
+  void SetCompositingReasons(CompositingReasons reasons);
+  CompositingReasons GetCompositingReasons() const;
+
   SquashingDisallowedReasons GetSquashingDisallowedReasons() const {
     return squashing_disallowed_reasons_;
   }
   void SetSquashingDisallowedReasons(SquashingDisallowedReasons reasons) {
     squashing_disallowed_reasons_ = reasons;
   }
-  void SetOwnerNodeId(int id) { owner_node_id_ = id; }
+
+  void SetOwnerNodeId(int id);
 
   GraphicsLayer* Parent() const { return parent_; }
   void SetParent(GraphicsLayer*);  // Internal use only.
@@ -227,8 +225,6 @@
   // For hosting this GraphicsLayer in a native layer hierarchy.
   cc::PictureLayer* CcLayer() const;
 
-  int PaintCount() const { return paint_count_; }
-
   // Return a string with a human readable form of the layer tree. If debug is
   // true, pointers for the layers and timing data will be included in the
   // returned string.
@@ -344,8 +340,6 @@
   bool HasAncestor(GraphicsLayer*) const;
 #endif
 
-  void IncrementPaintCount() { ++paint_count_; }
-
   // Helper functions used by settors to keep layer's the state consistent.
   void UpdateChildList();
   void UpdateLayerIsDrawable();
@@ -392,8 +386,6 @@
 
   IntRect contents_rect_;
 
-  int paint_count_;
-
   scoped_refptr<cc::PictureLayer> layer_;
   scoped_refptr<cc::PictureImageLayer> image_layer_;
   IntSize image_size_;
@@ -409,10 +401,8 @@
 
   int rendering_context3d_;
 
-  CompositingReasons compositing_reasons_ = CompositingReason::kNone;
   SquashingDisallowedReasons squashing_disallowed_reasons_ =
       SquashingDisallowedReason::kNone;
-  int owner_node_id_ = 0;
 
   mutable std::unique_ptr<PaintController> paint_controller_;
 
diff --git a/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
index 11bbf637..849372c 100644
--- a/third_party/blink/renderer/platform/graphics/logging_canvas.cc
+++ b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -342,7 +342,7 @@
 }
 
 String StringForSkPaintFlags(const SkPaint& paint) {
-  if (!paint.getFlags())
+  if (!paint.isAntiAlias() && !paint.isDither())
     return "none";
   String flags_string = "";
   AppendFlagToString(&flags_string, paint.isAntiAlias(), "AntiAlias");
diff --git a/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.cc b/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.cc
index 378ec6a..37d3c6c 100644
--- a/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.cc
+++ b/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.cc
@@ -15,6 +15,11 @@
   mutator_->SetClient(this);
 }
 
+void MainThreadMutatorClient::SynchronizeAnimatorName(
+    const String& animator_name) {
+  delegate_->SynchronizeAnimatorName(animator_name);
+}
+
 void MainThreadMutatorClient::SetMutationUpdate(
     std::unique_ptr<AnimationWorkletOutput> output_state) {
   delegate_->SetMutationUpdate(std::move(output_state));
diff --git a/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.h b/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.h
index 127d8f3..a008984 100644
--- a/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.h
+++ b/third_party/blink/renderer/platform/graphics/main_thread_mutator_client.h
@@ -20,6 +20,7 @@
       std::unique_ptr<AnimationWorkletMutatorDispatcherImpl>);
   ~MainThreadMutatorClient() override = default;
 
+  void SynchronizeAnimatorName(const String& animator_name) override;
   void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) override;
   void SetDelegate(MutatorClient* client);
   AnimationWorkletMutatorDispatcherImpl* Mutator() { return mutator_.get(); }
diff --git a/third_party/blink/renderer/platform/graphics/mutator_client.h b/third_party/blink/renderer/platform/graphics/mutator_client.h
index b4b5845..7247c27 100644
--- a/third_party/blink/renderer/platform/graphics/mutator_client.h
+++ b/third_party/blink/renderer/platform/graphics/mutator_client.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutators_state.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -14,6 +15,7 @@
  public:
   virtual ~MutatorClient() = default;
 
+  virtual void SynchronizeAnimatorName(const String& animator_name) = 0;
   virtual void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) = 0;
 };
 
diff --git a/third_party/blink/renderer/platform/heap/name_trait_test.cc b/third_party/blink/renderer/platform/heap/name_trait_test.cc
index 4e4a43b..fac0052b 100644
--- a/third_party/blink/renderer/platform/heap/name_trait_test.cc
+++ b/third_party/blink/renderer/platform/heap/name_trait_test.cc
@@ -4,8 +4,8 @@
 
 #include <string.h>
 
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
-
 #include "third_party/blink/renderer/platform/heap/name_traits.h"
 
 namespace blink {
@@ -29,10 +29,20 @@
 
 }  // namespace
 
+TEST(NameTraitTest, InternalNamesHiddenInOfficialBuild) {
+#if defined(OFFICIAL_BUILD)
+  EXPECT_TRUE(NameTrait<ClassWithoutName>::HideInternalName());
+#endif
+}
+
 TEST(NameTraitTest, DefaultName) {
   ClassWithoutName no_name;
   const char* name = NameTrait<ClassWithoutName>::GetName(&no_name);
-  EXPECT_EQ(0, strcmp(name, "InternalNode"));
+  if (NameTrait<ClassWithoutName>::HideInternalName()) {
+    EXPECT_EQ(0, strcmp(name, "InternalNode"));
+  } else {
+    EXPECT_NE(nullptr, strstr(name, "ClassWithoutName"));
+  }
 }
 
 TEST(NameTraitTest, CustomName) {
diff --git a/third_party/blink/renderer/platform/heap/name_traits.h b/third_party/blink/renderer/platform/heap/name_traits.h
index d1261815..941fc88b 100644
--- a/third_party/blink/renderer/platform/heap/name_traits.h
+++ b/third_party/blink/renderer/platform/heap/name_traits.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_NAME_TRAITS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_NAME_TRAITS_H_
 
+#include "build/build_config.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
 
 namespace blink {
 
@@ -15,6 +17,14 @@
   STATIC_ONLY(NameTrait);
 
  public:
+  static constexpr bool HideInternalName() {
+#if defined(OFFICIAL_BUILD) || !(defined(COMPILER_GCC) || defined(__clang__))
+    return true;
+#else
+    return false;
+#endif
+  }
+
   static const char* GetName(const void* obj) {
     return GetNameFor(static_cast<const T*>(obj));
   }
@@ -24,7 +34,34 @@
     return wrapper_tracable->NameInHeapSnapshot();
   }
 
-  static const char* GetNameFor(...) { return "InternalNode"; }
+  static const char* GetNameFor(...) {
+    // For non-official builds construct the name of a type from a compiler
+    // intrinsic.
+    //
+    // Do not include such type information in official builds to
+    // (a) safe binary size on string literals, and
+    // (b) avoid exposing internal types until a proper DevTools frontend
+    //     implementation is present.
+#if defined(OFFICIAL_BUILD) || !(defined(COMPILER_GCC) || defined(__clang__))
+    return "InternalNode";
+#else
+    DCHECK(!HideInternalName());
+    static const char* leaky_class_name = nullptr;
+    if (leaky_class_name)
+      return leaky_class_name;
+
+    // Parsing string of structure:
+    //   const char *WTF::GetStringWithTypeName<TYPE>() [T = TYPE]
+    // Note that this only works on clang or GCC builds.
+    const std::string raw(WTF::GetStringWithTypeName<T>());
+    const auto start_pos = raw.rfind("T = ") + 4;
+    DCHECK(std::string::npos != start_pos);
+    const auto len = raw.length() - start_pos - 1;
+    const std::string name = raw.substr(start_pos, len).c_str();
+    leaky_class_name = strcpy(new char[name.length() + 1], name.c_str());
+    return leaky_class_name;
+#endif
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/OWNERS b/third_party/blink/renderer/platform/loader/OWNERS
index 0e5b601..1c34e1e 100644
--- a/third_party/blink/renderer/platform/loader/OWNERS
+++ b/third_party/blink/renderer/platform/loader/OWNERS
@@ -3,7 +3,7 @@
 mkwst@chromium.org
 toyoshim@chromium.org
 yhirano@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
 
 # TEAM: loading-dev@chromium.org
 # COMPONENT: Blink>Loader
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.cc
index 030a057..b84a3ce 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -120,4 +121,19 @@
   return std::make_unique<CachedMetadataSenderImpl>(response, code_cache_type);
 }
 
+bool ShouldUseIsolatedCodeCache(mojom::RequestContextType request_context,
+                                const ResourceResponse& response) {
+  return RuntimeEnabledFeatures::IsolatedCodeCacheEnabled() &&
+         // Service worker script has its own code cache.
+         request_context != mojom::RequestContextType::SERVICE_WORKER &&
+         // Also, we only support code cache for other service worker provided
+         // resources when a direct pass-through fetch handler is used.  If the
+         // service worker synthesizes a new Response or provides a Response
+         // fetched from a different URL, then do not use the code cache.
+         // Also, responses coming from cache storage use a separate code
+         // cache mechanism.
+         (!response.WasFetchedViaServiceWorker() ||
+          response.IsServiceWorkerPassThrough());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
index 6d1ab18..80a609d0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
 
 #include <stdint.h>
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -34,6 +35,10 @@
   virtual bool IsServedFromCacheStorage() = 0;
 };
 
+// Returns whether we should use isolated code cache for a particular response.
+PLATFORM_EXPORT bool ShouldUseIsolatedCodeCache(mojom::RequestContextType,
+                                                const ResourceResponse&);
+
 // Handler class for caching operations.
 class CachedMetadataHandler
     : public GarbageCollectedFinalized<CachedMetadataHandler> {
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index 382a508..a3c0756 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -226,8 +226,6 @@
     return Platform::Current()->CreateCodeCacheLoader();
   }
 
-  virtual bool IsDetached() const { return false; }
-
   // Obtains FrameScheduler instance that is used in the attached frame.
   // May return nullptr if a frame is not attached or detached.
   virtual FrameScheduler* GetFrameScheduler() const { return nullptr; }
@@ -263,6 +261,10 @@
   virtual void DispatchNetworkQuiet() {}
 
  protected:
+  // This is needed to make FetchContext cleanup smoother.
+  // TODO(yhirano): Remove this.
+  virtual bool IsDetached() const { return false; }
+
   // This is needed to make FetchContext cleanup smoother. Do not use this
   // function for other purposes.
   ResourceFetcher* GetFetcher() { return fetcher_; }
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
index 79dded4..4a7cd2b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -121,7 +121,8 @@
         MakeGarbageCollected<TestResourceFetcherProperties>(security_origin_);
     properties->SetShouldBlockLoadingMainResource(true);
     properties->SetShouldBlockLoadingSubResource(true);
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
   }
   void TearDown() override {
     GetMemoryCache()->EvictResources();
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
index 57a150d..131a9dd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -109,8 +109,8 @@
     global_memory_cache_ = ReplaceMemoryCacheForTesting(
         MemoryCache::Create(platform_->test_task_runner()));
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-    fetcher_ = MakeGarbageCollected<ResourceFetcher>(
-        *properties, MakeGarbageCollected<MockFetchContext>());
+    fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
+        *properties, MakeGarbageCollected<MockFetchContext>()));
   }
 
   void TearDown() override {
diff --git a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
index 1302a5b..7f6867a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
@@ -36,6 +36,7 @@
     return 0;
   }
   bool IsPaused() const override { return false; }
+  bool IsDetached() const override { return true; }
   bool IsLoadComplete() const override { return true; }
   bool ShouldBlockLoadingMainResource() const override { return true; }
   bool ShouldBlockLoadingSubResource() const override { return true; }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index ad4ef5d..a9c4a3b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -435,6 +435,7 @@
   bool IsPaused() const override {
     return properties_ ? properties_->IsPaused() : paused_;
   }
+  bool IsDetached() const override { return !properties_; }
   bool IsLoadComplete() const override {
     return properties_ ? properties_->IsLoadComplete() : load_complete_;
   }
@@ -546,7 +547,11 @@
     scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
         params.Options().initiator_info.name, CurrentTimeTicks(),
         resource->GetType() == ResourceType::kMainResource);
-    info->SetInitialURL(resource->GetResourceRequest().Url());
+    // TODO(yoav): Setting to the original URL is only needed until Out-of-Blink
+    // CORS lands: https://crbug.com/736308
+    info->SetInitialURL(resource->GetResourceRequest().GetOriginalUrl().IsNull()
+                            ? resource->GetResourceRequest().Url()
+                            : resource->GetResourceRequest().GetOriginalUrl());
     ResourceResponse final_response = resource->GetResponse();
     final_response.SetResourceLoadTiming(nullptr);
     info->SetFinalResponse(final_response);
@@ -1689,7 +1694,10 @@
           resource_timing_info_map_.Take(resource)) {
     if (resource->GetResponse().IsHTTP() &&
         resource->GetResponse().HttpStatusCode() < 400) {
-      info->SetInitialURL(resource->GetResourceRequest().Url());
+      info->SetInitialURL(
+          resource->GetResourceRequest().GetOriginalUrl().IsNull()
+              ? resource->GetResourceRequest().Url()
+              : resource->GetResourceRequest().GetOriginalUrl());
       info->SetFinalResponse(resource->GetResponse());
       info->SetLoadFinishTime(finish_time);
       // encodedDataLength == -1 means "not available".
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index c82437a..cc27c7e 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -106,12 +106,8 @@
 
  public:
   // ResourceFetcher creators are responsible for setting consistent objects
-  // in ResourceFetcherInit or ResourceFetcher arguments to ensure correctness
-  // of this ResourceFetcher.
+  // in ResourceFetcherInit to ensure correctness of this ResourceFetcher.
   explicit ResourceFetcher(const ResourceFetcherInit&);
-  ResourceFetcher(const ResourceFetcherProperties& properties,
-                  FetchContext* context)
-      : ResourceFetcher(ResourceFetcherInit(properties, context)) {}
   virtual ~ResourceFetcher();
   virtual void Trace(blink::Visitor*);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
index 083b170..dafb16d 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
@@ -58,6 +58,11 @@
   // https://html.spec.whatwg.org/C/webappapis.html#pause
   virtual bool IsPaused() const = 0;
 
+  // Returns whether this global context is detached. Note that in some cases
+  // the loading pipeline continues working after detached (e.g., for fetch()
+  // operations with "keepalive" specified).
+  virtual bool IsDetached() const = 0;
+
   // Returns whether the main resource for this global context is loaded.
   virtual bool IsLoadComplete() const = 0;
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 1aec857..8f79f60 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -122,9 +122,9 @@
   KURL secure_url("https://secureorigin.test/image.png");
   // Try to request a url. The request should fail, and a resource in an error
   // state should be returned, and no resource should be present in the cache.
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit(
       *MakeGarbageCollected<NullResourceFetcherProperties>(),
-      &FetchContext::NullInstance(platform_->test_task_runner()));
+      &FetchContext::NullInstance(platform_->test_task_runner())));
   ResourceRequest resource_request(secure_url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -142,8 +142,8 @@
 
 TEST_F(ResourceFetcherTest, UseExistingResource) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
@@ -183,7 +183,8 @@
 
   // Fetch the cached resource. The request to DispatchWillSendRequest should
   // preserve the ad bit.
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
   ResourceRequest resource_request(url);
   resource_request.SetIsAdResource();
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
@@ -218,7 +219,8 @@
   resource->FinishForTest();
   ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
 
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -248,7 +250,8 @@
       MakeGarbageCollected<TestResourceFetcherProperties>(source_origin);
   MockFetchContext* context = CreateFetchContext();
 
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
 
   KURL url("http://127.0.0.1:8000/foo.html");
   Resource* resource =
@@ -273,8 +276,8 @@
 
 TEST_F(ResourceFetcherTest, VaryResource) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
@@ -312,9 +315,9 @@
     EXPECT_EQ(GetResource(), resource);
     auto* properties =
         MakeGarbageCollected<TestResourceFetcherProperties>(source_origin_);
-    MockFetchContext* context = MakeGarbageCollected<MockFetchContext>(nullptr);
-    auto* fetcher2 =
-        MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
+    auto* fetcher2 = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
     ResourceRequest resource_request2(GetResource()->Url());
     resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache);
     FetchParameters fetch_params2(resource_request2);
@@ -351,8 +354,8 @@
 
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(source_origin);
-  ResourceFetcher* fetcher1 =
-      MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  ResourceFetcher* fetcher1 = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
   ResourceRequest request1(url);
   request1.SetHTTPHeaderField(http_names::kCacheControl, "no-cache");
   FetchParameters fetch_params1(request1);
@@ -371,8 +374,8 @@
 #endif
 TEST_F(ResourceFetcherTest, MAYBE_DontReuseMediaDataUrl) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   ResourceRequest request(KURL("data:text/html,foo"));
   request.SetRequestContext(mojom::RequestContextType::VIDEO);
   request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
@@ -442,8 +445,8 @@
   RegisterMockedURLLoad(url);
 
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -479,8 +482,8 @@
 
   void Request(const WebString& url) {
     auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-    auto* fetcher =
-        MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context_));
     ResourceRequest resource_request(url);
     resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
     FetchParameters fetch_params(resource_request);
@@ -538,8 +541,8 @@
   RegisterMockedURLLoad(url);
 
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -555,8 +558,8 @@
   RegisterMockedURLLoad(url);
 
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(mojom::RequestContextType::PING);
   FetchParameters fetch_params(resource_request);
@@ -567,8 +570,8 @@
 
 TEST_F(ResourceFetcherTest, PreloadResourceTwice) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -596,8 +599,8 @@
 
 TEST_F(ResourceFetcherTest, LinkPreloadResourceAndUse) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -634,8 +637,8 @@
 
 TEST_F(ResourceFetcherTest, PreloadMatchWithBypassingCache) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
 
@@ -659,10 +662,10 @@
 TEST_F(ResourceFetcherTest, CrossFramePreloadMatchIsNotAllowed) {
   auto* properties1 = MakeGarbageCollected<TestResourceFetcherProperties>();
   auto* properties2 = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties1, CreateFetchContext());
-  ResourceFetcher* fetcher2 =
-      MakeGarbageCollected<ResourceFetcher>(*properties2, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties1, CreateFetchContext()));
+  ResourceFetcher* fetcher2 = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties2, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -687,8 +690,8 @@
 
 TEST_F(ResourceFetcherTest, RepetitiveLinkPreloadShouldBeMerged) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -721,8 +724,8 @@
 
 TEST_F(ResourceFetcherTest, RepetitiveSpeculativePreloadShouldBeMerged) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -756,8 +759,8 @@
 
 TEST_F(ResourceFetcherTest, SpeculativePreloadShouldBePromotedToLinkePreload) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -812,7 +815,8 @@
   resource->ResponseReceived(response, nullptr);
   resource->FinishForTest();
 
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
   ResourceRequest resource_request(url);
   resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
   FetchParameters fetch_params(resource_request);
@@ -826,10 +830,10 @@
 TEST_F(ResourceFetcherTest, LinkPreloadResourceMultipleFetchersAndMove) {
   auto* properties1 = MakeGarbageCollected<TestResourceFetcherProperties>();
   auto* properties2 = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties1, CreateFetchContext());
-  ResourceFetcher* fetcher2 =
-      MakeGarbageCollected<ResourceFetcher>(*properties2, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties1, CreateFetchContext()));
+  ResourceFetcher* fetcher2 = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties2, CreateFetchContext()));
 
   KURL url("http://127.0.0.1:8000/foo.png");
   RegisterMockedURLLoad(url);
@@ -861,8 +865,8 @@
 #endif
 TEST_F(ResourceFetcherTest, MAYBE_ContentTypeDataURL) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
   FetchParameters fetch_params{ResourceRequest("data:text/testmimetype,foo")};
   Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
   ASSERT_TRUE(resource);
@@ -883,8 +887,8 @@
   RegisterMockedURLLoadWithCustomResponse(url, response);
 
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   // Main resource case.
   {
@@ -918,7 +922,8 @@
   auto* properties =
       MakeGarbageCollected<TestResourceFetcherProperties>(source_origin);
   MockFetchContext* context = CreateFetchContext();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context));
 
   KURL url("http://127.0.0.1:8000/foo.html");
   FetchParameters fetch_params{ResourceRequest(url)};
@@ -962,8 +967,8 @@
 
 TEST_F(ResourceFetcherTest, CachedResourceShouldNotCrashByNullURL) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(*properties, CreateFetchContext());
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, CreateFetchContext()));
 
   // Make sure |cached_resources_map_| is not empty, so that HashMap lookup
   // won't take a fast path.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
index 4d69ec6..5867229 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -67,7 +67,8 @@
     properties->SetShouldBlockLoadingMainResource(true);
     properties->SetShouldBlockLoadingSubResource(true);
     auto* context = MakeGarbageCollected<MockFetchContext>();
-    MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
     scheduler_ = MakeGarbageCollected<ResourceLoadScheduler>(
         ResourceLoadScheduler::ThrottlingPolicy::kTight, context);
     Scheduler()->SetOutstandingLimitForTesting(1);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index 05713f4..36a33aa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/loader/mixed_content_autoupgrade_status.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
@@ -786,7 +787,7 @@
                                request.GetUkmSourceId(), recorder.get());
   }
 
-  if (Context().IsDetached()) {
+  if (fetcher_->GetProperties().IsDetached()) {
     // If the fetch context is already detached, we don't need further signals,
     // so let's cancel the request.
     HandleError(
@@ -808,18 +809,7 @@
   const ResourceResponse& response = web_url_response.ToResourceResponse();
 
   should_use_isolated_code_cache_ =
-      RuntimeEnabledFeatures::IsolatedCodeCacheEnabled() &&
-      // Service worker script has its own code cache.
-      request_context != mojom::RequestContextType::SERVICE_WORKER &&
-      // And also, resources which are served from CacheStorage via service
-      // workers have its own code cache.
-      response.CacheStorageCacheName().IsNull() &&
-      // Also, we only support code cache for other service worker provided
-      // resources when a direct pass-through fetch handler is used.  If the
-      // service worker synthesizes a new Response or provides a Response
-      // fetched from a different URL, then do not use the code cache.
-      (!response.WasFetchedViaServiceWorker() ||
-       response.IsServiceWorkerPassThrough());
+      ShouldUseIsolatedCodeCache(request_context, response);
 
   // Perform 'nosniff' checks against the original response instead of the 304
   // response for a successful revalidation.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
index 2cbebfe..bffddae 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc
@@ -162,7 +162,8 @@
 
 TEST_F(ResourceLoaderDefersLoadingTest, CodeCacheFetchCheckDefers) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context_));
 
   ResourceRequest request;
   request.SetURL(test_url_);
@@ -190,7 +191,8 @@
       }));
 
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context_));
 
   ResourceRequest request;
   request.SetURL(test_url_);
@@ -208,7 +210,8 @@
 
 TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToFalse) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context_));
 
   ResourceRequest request;
   request.SetURL(test_url_);
@@ -230,7 +233,8 @@
 
 TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToTrue) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context_));
 
   ResourceRequest request;
   request.SetURL(test_url_);
@@ -256,7 +260,8 @@
 
 TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersMultipleTimes) {
   auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>();
-  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context_);
+  auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+      ResourceFetcherInit(*properties, context_));
 
   ResourceRequest request;
   request.SetURL(test_url_);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index 98550ba..eabe8a8 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -161,7 +161,8 @@
         MakeGarbageCollected<TestResourceFetcherProperties>(origin);
     FetchContext* context = MakeGarbageCollected<MockFetchContext>(
         nullptr, std::make_unique<TestWebURLLoaderFactory>());
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
 
     ResourceRequest request;
     request.SetURL(test.url);
@@ -201,7 +202,8 @@
         MakeGarbageCollected<TestResourceFetcherProperties>(origin);
     FetchContext* context = MakeGarbageCollected<MockFetchContext>(
         nullptr, std::make_unique<TestWebURLLoaderFactory>());
-    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(*properties, context);
+    auto* fetcher = MakeGarbageCollected<ResourceFetcher>(
+        ResourceFetcherInit(*properties, context));
 
     ResourceRequest request;
     request.SetURL(foo_url_);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 7f7ff43..06b45a3a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -150,6 +150,14 @@
   url_ = url;
 }
 
+const KURL& ResourceRequest::GetOriginalUrl() const {
+  return original_url_;
+}
+
+void ResourceRequest::SetOriginalUrl(const KURL& url) {
+  original_url_ = url;
+}
+
 void ResourceRequest::RemoveUserAndPassFromURL() {
   if (url_.User().IsEmpty() && url_.Pass().IsEmpty())
     return;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index 8b39e06..f4ac6b19 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -95,6 +95,9 @@
   const KURL& Url() const;
   void SetURL(const KURL&);
 
+  const KURL& GetOriginalUrl() const;
+  void SetOriginalUrl(const KURL&);
+
   void RemoveUserAndPassFromURL();
 
   mojom::FetchCacheMode GetCacheMode() const;
@@ -439,6 +442,7 @@
   bool NeedsHTTPOrigin() const;
 
   KURL url_;
+  KURL original_url_;
   // TimeDelta::Max() represents the default timeout on platforms that have one.
   base::TimeDelta timeout_interval_;
   KURL site_for_cookies_;
diff --git a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
index e40af55..4c7f7fa9 100644
--- a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
@@ -39,6 +39,7 @@
     return 0;
   }
   bool IsPaused() const override { return false; }
+  bool IsDetached() const override { return false; }
   bool IsLoadComplete() const override { return load_complete_; }
   bool ShouldBlockLoadingMainResource() const override {
     return should_block_loading_main_resource_;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h b/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h
index 597cb1e..590402c 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h
@@ -33,7 +33,7 @@
 
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
-#include "third_party/webrtc/api/rtcerror.h"
+#include "third_party/webrtc/api/rtc_error.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index dc1ffd3..0c6f40f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -648,6 +648,7 @@
       // between animation frames (see crbug.com/581518).
       name: "JankTracking",
       implied_by: ["LayoutJankAPI", "JankTrackingSweepLine"],
+      origin_trial_feature_name: "LayoutJankAPI",
       status: "experimental",
     },
     {
@@ -670,7 +671,8 @@
       // Exposes layout jank fractions to Javascript. See explainer:
       // http://bit.ly/lsm-explainer.
       name: "LayoutJankAPI",
-      status: "test",
+      origin_trial_feature_name: "LayoutJankAPI",
+      status: "experimental",
     },
     {
       name: "LayoutNG",
@@ -967,6 +969,7 @@
     },
     {
       name: "PassiveDocumentWheelEventListeners",
+      status: "stable",
     },
     {
       name: "PassPaintVisualRectToCompositor",
@@ -1039,7 +1042,7 @@
     },
     {
       name: "PreloadImageSrcSet",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "Presentation",
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
index 763e642..164262e 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
@@ -8,7 +8,7 @@
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
-#include "third_party/webrtc/api/peerconnectioninterface.h"
+#include "third_party/webrtc/api/peer_connection_interface.h"
 
 namespace blink {
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 652f8fd..75a40704 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -176,6 +176,7 @@
 
             # Chromium geometry types.
             'gfx::Point',
+            'gfx::Point3F',
             'gfx::Rect',
             'gfx::RectF',
             'gfx::Size',
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index b6fd70e..038fb9bd 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -331,6 +331,11 @@
 crbug.com/670246 [ Win ] fast/text/variable-fonts/variable-mac-system-font.html [ WontFix ]
 crbug.com/670246 [ Mac10.10 ] fast/text/variable-fonts/variable-mac-system-font.html [ WontFix ]
 
+# FontCache masking issue only testable on Mac.
+crbug.com/921029 [ Linux ] fast/text/unique-vs-family-match.html [ WontFix ]
+crbug.com/921029 [ Android ] fast/text/unique-vs-family-match.html [ WontFix ]
+crbug.com/921029 [ Win ] fast/text/unique-vs-family-match.html [ WontFix ]
+
 # prefer_compositing_to_lcd_text causes things to get composited regardless of their opaqueness, causing the test to fail
 crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-opaque-to-transparent.html [ WontFix ]
 crbug.com/381840 virtual/prefer_compositing_to_lcd_text/compositing/overflow/overflow-scroll-background-transparent-to-opaque.html [ WontFix ]
@@ -2209,3 +2214,4 @@
 external/wpt/html/semantics/forms/the-input-element/event-select-manual.html [ WontFix ]
 external/wpt/html/editing/dnd/the-datatransferitem-interface/getAsString-manual.html [ WontFix ]
 external/wpt/payment-request/billing-address-changed-manual.https.html [ WontFix ]
+external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html [ WontFix ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 809b598..ebabae9 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -1551,7 +1551,7 @@
 crbug.com/878650 [ Release ] inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js [ Slow ]
 crbug.com/878650 [ Release ] inspector-protocol/heap-profiler/heap-snapshot-with-no-detached-iframe.js [ Slow ]
 
-crbug.com/878995 [ Win ] external/wpt/async-local-storage/key-types.tentative.https.html [ Slow ]
+crbug.com/878995 [ Win ] external/wpt/kv-storage/key-types.tentative.https.html [ Slow ]
 crbug.com/888225 external/wpt/svg/idlharness.window.html [ Slow ]
 
 crbug.com/893015 [ Linux ] http/tests/fetch/chromium/response-array-buffer-gc-crash.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 5573b77..d4259ab 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -82,7 +82,6 @@
 crbug.com/908841 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Timeout ]
 
 # The following fail only on Mac.
-crbug.com/891427 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-desktop.html [ Pass Failure Timeout Crash ]
 crbug.com/891427 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Pass Failure Timeout Crash ]
 crbug.com/891427 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Pass Failure Timeout Crash ]
 crbug.com/891427 [ Mac ] virtual/user-activation-v2/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Pass Failure Timeout Crash ]
@@ -306,8 +305,6 @@
 crbug.com/680635 external/wpt/offscreen-canvas/text/2d.text.font.parse.invalid.html [ Skip ]
 crbug.com/680635 external/wpt/offscreen-canvas/text/2d.text.font.parse.invalid.worker.html [ Skip ]
 
-crbug.com/920907 [ Mac ] virtual/threaded/printing/offscreencanvas-webgl-printing.html [ Pass Failure ]
-
 # With removing fling handling logic from renderer Layouttests that are using event sender to send fling events are failing.
 crbug.com/857490 fast/events/touch/gesture/pad-gesture-cancel.html [ Skip ]
 crbug.com/857490 fast/events/touch/gesture/pad-gesture-fling.html [ Skip ]
@@ -385,8 +382,7 @@
 crbug.com/887000 virtual/prefer_compositing_to_lcd_text/scrollbars/hidden-scrollbars-invisible.html [ Failure ]
 crbug.com/882975 virtual/threaded/fast/events/pinch/gesture-pinch-zoom-prevent-in-handler.html [ Failure Pass ]
 crbug.com/882975 virtual/threaded/fast/events/pinch/scroll-visual-viewport-send-boundary-events.html [ Failure Pass ]
-### See crbug.com/891427 comment near the top of this file:
-###crbug.com/882975 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-desktop.html [ Failure Pass ]
+crbug.com/882975 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-desktop.html [ Failure Pass Crash ]
 crbug.com/884239 virtual/threaded/animations/animationworklet/worklet-animation-local-time-undefined.html [ Failure ]
 # Subpixel rounding differences that are incorrect.
 crbug.com/836886 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scaled-overflow.html [ Failure ]
@@ -5297,7 +5293,8 @@
 # Sheriff 2018-06-11
 crbug.com/850202 [ Linux ] http/tests/devtools/network/network-filters.js [ Pass Failure Timeout ]
 
-crbug.com/879270 http/tests/performance-timing/element-timing/supported-element-type.html [ Failure ]
+crbug.com/922195 http/tests/performance-timing/element-timing/supported-element-type.html [ Failure ]
+crbug.com/922195 http/tests/performance-timing/layout-jank/supported-layout-type.html [ Failure ]
 
 crbug.com/853360 [ Mac ] fast/css/input-search-padding.html [ Failure ]
 crbug.com/853360 [ Mac ] fast/forms/calendar-picker/calendar-picker-appearance-ar.html [ Failure ]
@@ -5912,3 +5909,24 @@
 crbug.com/921151 [ Linux ] http/tests/security/mixedContent/insecure-iframe-with-hsts.https.html [ Failure Pass ]
 crbug.com/921151 [ Linux ] virtual/outofblink-cors/http/tests/security/mixedContent/insecure-iframe-with-hsts.https.html [ Failure Pass ]
 crbug.com/921151 [ Linux ] virtual/outofblink-cors-ns/http/tests/security/mixedContent/insecure-iframe-with-hsts.https.html [ Failure Pass ]
+
+# Writable Files tests depend on mojo interfaces that are only exposed behind a flag.
+crbug.com/922735 fast/filesystem/writable-files/ [ Skip ]
+
+# These fail when landing valid changes to Mojo bindings dispatch timing. This
+# seems to be due to the layout test framework taking a snapshot too soon, as
+# viewing the layout test contents manually (in e.g. Content Shell) consistently
+# displays the expected output, whereas the actual output captured by the layout
+# test runner consistently does not.
+crbug.com/921719 printing/offscreencanvas-2d-printing.html  [ Skip ]
+crbug.com/921719 printing/offscreencanvas-webgl-printing.html  [ Skip ]
+crbug.com/921719 scrollbars/mock-scrollbars.html [ Skip ]
+crbug.com/921719 virtual/layout_ng_experimental/printing/offscreencanvas-2d-printing.html [ Skip ]
+crbug.com/921719 virtual/layout_ng_experimental/printing/offscreencanvas-webgl-printing.html [ Skip ]
+crbug.com/921719 virtual/prefer_compositing_to_lcd_text/scrollbars/mock-scrollbars.html [ Skip ]
+crbug.com/921719 virtual/threaded/printing/offscreencanvas-2d-printing.html [ Skip ]
+crbug.com/921719 virtual/threaded/printing/offscreencanvas-webgl-printing.html [ Skip ]
+
+# Sheriff 2019-01-15
+# Fails on Linux Tests (dbg) bot.
+crbug.com/922508 [ Linux Debug ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/aria-setsize-posinset.html b/third_party/blink/web_tests/accessibility/aria-setsize-posinset.html
index 3337455..60e964b 100644
--- a/third_party/blink/web_tests/accessibility/aria-setsize-posinset.html
+++ b/third_party/blink/web_tests/accessibility/aria-setsize-posinset.html
@@ -22,39 +22,35 @@
   }, "Tests that aria-posinset and aria-setSize for a simple list are exposed to accessibility correctly");
 </script>
 
-<div id="listbox" role="listbox">
-  <div role="option"></div>
-  <div role="option"></div>
-  <div role="option"></div>
-  <div role="option"></div>
-</div>
+<form id ="form">
+  <input type="radio" name="meals" value="breakfast">
+  <input type="radio" name="meals" value="lunch">
+  <input type="radio" name="meals" value="dinner">
+</form>
 
 <script>
   test(function(t){
-    var listbox = accessibilityController.accessibleElementById("listbox");
-
-    assert_equals(listbox.childAtIndex(0).posInSet,1);
-    assert_equals(listbox.childAtIndex(1).posInSet,2);
-    assert_equals(listbox.childAtIndex(2).posInSet,3);
-    assert_equals(listbox.childAtIndex(3).posInSet,4);
-
-    assert_equals(listbox.childAtIndex(0).setSize,4);
-    assert_equals(listbox.childAtIndex(1).setSize,4);
-    assert_equals(listbox.childAtIndex(2).setSize,4);
-    assert_equals(listbox.childAtIndex(3).setSize,4);
-  }, "Tests that aria-posinset and aria-setSize for a listbox are exposed to accessibility correctly");
+    var form = accessibilityController.accessibleElementById("form");
+    // aria-posinset and aria-setSize can be implicitly caluclated for input type of radio.
+    assert_equals(form.childAtIndex(0).posInSet,1);
+    assert_equals(form.childAtIndex(1).posInSet,2);
+    assert_equals(form.childAtIndex(2).posInSet,3);
+    assert_equals(form.childAtIndex(0).setSize,3);
+    assert_equals(form.childAtIndex(1).setSize,3);
+    assert_equals(form.childAtIndex(2).setSize,3);
+  }, "Tests that aria-posinset and aria-setSize for native radio inputs are exposed to accessibility correctly");
 </script>
 
 <div id="radiogroup" role="radiogroup">
   <hr>
-  <div id="radio1" role="radio">Radio 1</div>
+  <div id="radio1" role="radio" aria-setSize="3" aria-posinset="2">Radio 1</div>
 </div>
 
 <script>
   test(function(t){
     var radio1 = accessibilityController.accessibleElementById("radio1");
-    assert_equals(radio1.posInSet,1);
-    assert_equals(radio1.setSize,1);
+    assert_equals(radio1.posInSet,2);
+    assert_equals(radio1.setSize,3);
   },"Tests that aria-posinset and aria-setSize for a radio are exposed to accessibility correctly");
 </script>
 
diff --git a/third_party/blink/web_tests/accessibility/aria-tree.html b/third_party/blink/web_tests/accessibility/aria-tree.html
deleted file mode 100644
index ab7b454..0000000
--- a/third_party/blink/web_tests/accessibility/aria-tree.html
+++ /dev/null
@@ -1,111 +0,0 @@
-<!DOCTYPE HTML>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-
-<ul id="manual-props" role="tree">
-  <li role="none">
-    <span id="manual-item" role="treeitem"
-          aria-level="2" aria-setsize="5" aria-posinset="3">
-      Oranges
-    </span>
-  </li>
-</ul>
-
-<ul id="auto-props" role="tree">
-  <li role="treeitem">
-    Veggies
-    <ul role="group">
-      <li role="treeitem">
-          Green beans
-      </li>
-      <li role="treeitem">
-        Onions
-        <ul role="group">
-          <li role="treeitem">
-            Green
-          </li>
-          <li role="treeitem" aria-hidden="true">
-            Hidden
-          </li>
-          <li id="auto-item" role="treeitem">
-            Vidalia
-          </li>
-          <li role="treeitem">
-            Yellow
-          </li>
-        </ul>
-      </li>
-    </ul>
-  </li>
-</ul>
-
-<ul id="auto-set-props-flat" role="tree">
-  <li role="treeitem" aria-level="1">
-    Cats
-  </li>
-  <li role="treeitem" aria-level="2">
-    Hypoallegenic
-  </li>
-  <li role="treeitem" aria-level="3">
-    Siberian
-  </li>
-  <li role="treeitem" aria-level="3">
-    Sphynx
-  </li>
-  <li role="treeitem" aria-level="2">
-    Siamese
-  </li>
-  <li role="treeitem" id="flat-auto-item" aria-level="2">
-    Calico
-  </li>
-  <li role="treeitem" aria-level="1">
-    Fish
-  </li>
-</ul>
-
-<script>
-function axElementById(id) {
-    return accessibilityController.accessibleElementById(id);
-}
-
-test(function(t) {
-    var manualItem = axElementById("manual-item");
-    assert_equals(manualItem.hierarchicalLevel, 2);
-}, "A manual tree item exposes its level property");
-
-test(function(t) {
-    var manualItem = axElementById("manual-item");
-    assert_equals(manualItem.posInSet, 3);
-}, "A manual tree item exposes its posinset property");
-
-test(function(t) {
-    var manualItem = axElementById("manual-item");
-    assert_equals(manualItem.setSize, 5);
-}, "A manual tree item exposes its setsize property");
-
-test(function(t) {
-    var autoItem = axElementById("auto-item");
-    assert_equals(autoItem.hierarchicalLevel, 3);
-}, "An auto tree item computes its level property");
-
-test(function(t) {
-    var autoItem = axElementById("auto-item");
-    assert_equals(autoItem.posInSet, 2);
-}, "An auto tree item computes its posinset property");
-
-test(function(t) {
-    var autoItem = axElementById("auto-item");
-    assert_equals(autoItem.setSize, 3);
-}, "An auto tree item computes its setsize property");
-
-test(function(t) {
-    var flatAutoItem = axElementById("flat-auto-item");
-    assert_equals(flatAutoItem.setSize, 3);
-}, "A flatauto tree item with levels computes its setsize property");
-
-test(function(t) {
-    var flatAutoItem = axElementById("flat-auto-item");
-    assert_equals(flatAutoItem.posInSet, 3);
-}, "A flat auto tree item with levels computes its posinset property");
-
-</script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-dispose.html b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-dispose.html
index b3ad00d8..ceda8af 100644
--- a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-dispose.html
+++ b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-dispose.html
@@ -18,6 +18,12 @@
 }
 </style>
 
+<script id="simple_animate" type="text/worklet">
+registerAnimator("test_animator", class {
+  animate(currentTime, effect) {}
+});
+</script>
+
 <div id="box"></div>
 <div id="scroller">
   <div id="contents"></div>
@@ -28,29 +34,33 @@
 // landed. The root of the crash was assuming a WeakMember from a static object
 // would still be alive during a USING_PRE_FINALIZER method, which is not true.
 async_test(t => {
-  const box = document.getElementById('box');
-  const effect = new KeyframeEffect(box, null);
+  runInAnimationWorklet(
+   document.getElementById('simple_animate').textContent
+  ).then(_ => {
+    const box = document.getElementById('box');
+    const effect = new KeyframeEffect(box, null);
 
-  let scroller = document.getElementById('scroller');
-  let timeline = new ScrollTimeline({
-    scrollSource: scroller,
-    timeRange: 1000,
-    orientation: 'block'
+    let scroller = document.getElementById('scroller');
+    let timeline = new ScrollTimeline({
+      scrollSource: scroller,
+      timeRange: 1000,
+      orientation: 'block'
+    });
+    // Creating the animation will cause the scroller to be registered as being
+    // used in an attached ScrollTimeline.
+    let animation = new WorkletAnimation('test_animator', effect, timeline, {});
+
+    // Now free up everything at once.
+    scroller.remove();
+    animation.cancel();
+    scroller = undefined;
+    timeline = undefined;
+    animation = undefined;
+
+    requestAnimationFrame(t.step_func_done(() => {
+      // Force GC - this shouldn't crash.
+      gc();
+    }));
   });
-  // Creating the animation will cause the scroller to be registered as being
-  // used in an attached ScrollTimeline.
-  let animation = new WorkletAnimation('test_animator', effect, timeline, {});
-
-  // Now free up everything at once.
-  scroller.remove();
-  animation.cancel();
-  scroller = undefined;
-  timeline = undefined;
-  animation = undefined;
-
-  requestAnimationFrame(t.step_func_done(() => {
-    // Force GC - this shouldn't crash.
-    gc();
-  }));
 }, 'Disposing of an attached ScrollTimeline should not crash');
 </script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/worklet-animation-creation.html b/third_party/blink/web_tests/animations/animationworklet/worklet-animation-creation.html
index 7c37302..c40513b2 100644
--- a/third_party/blink/web_tests/animations/animationworklet/worklet-animation-creation.html
+++ b/third_party/blink/web_tests/animations/animationworklet/worklet-animation-creation.html
@@ -1,4 +1,7 @@
 <!DOCTYPE html>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+
 <style>
 .scroller {
   height: 100px;
@@ -11,14 +14,6 @@
 }
 </style>
 
-<div id='element'></div>
-<div id='element2'></div>
-<div class='scroller'>
-  <div class='content'></div>
-</div>
-
-<script src='../../resources/testharness.js'></script>
-<script src='../../resources/testharnessreport.js'></script>
 <script>
 function CreateKeyframeEffect(element) {
   return new KeyframeEffect(
@@ -30,47 +25,67 @@
       { duration: 3000, fill: 'forwards' }
   );
 }
+</script>
+<script id="simple_animate" type="text/worklet">
+registerAnimator("test-animator", class {
+  animate(currentTime, effect) {}
+});
+</script>
 
-test(function() {
+<div id='element'></div>
+<div id='element2'></div>
+<div class='scroller'>
+  <div class='content'></div>
+</div>
+
+<script src="resources/animation-worklet-tests.js"></script>
+<script>
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let effect = CreateKeyframeEffect(document.querySelector('#element'));
   let workletAnimation = new WorkletAnimation(
-      'my-animation', effect);
+      'test-animator', effect);
   assert_equals(workletAnimation.playState, 'idle');
   assert_equals(workletAnimation.timeline, document.timeline);
 }, 'WorkletAnimation creation without timeline should use default documentation timeline');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let effect = CreateKeyframeEffect(document.querySelector('#element'));
   let workletAnimation = new WorkletAnimation(
-      'my-animation', effect, document.timeline);
+      'test-animator', effect, document.timeline);
   assert_equals(workletAnimation.playState, 'idle');
 }, 'WorkletAnimation creation with timeline should work');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let effect = CreateKeyframeEffect(document.querySelector('#element'));
   let options = { my_param: 'foo', my_other_param: true };
   let workletAnimation = new WorkletAnimation(
-      'my-animation', effect, document.timeline, options);
+      'test-animator', effect, document.timeline, options);
   assert_equals(workletAnimation.playState, 'idle');
 }, 'WorkletAnimation creation with timeline and options should work');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let effect = CreateKeyframeEffect(document.querySelector('#element'));
   let scroller = document.querySelector('.scroller');
   let scrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: 100, orientation: 'inline' });
   let workletAnimation = new WorkletAnimation(
-      'my-animation', effect, scrollTimeline);
+      'test-animator', effect, scrollTimeline);
   assert_equals(workletAnimation.playState, 'idle');
 }, 'ScrollTimeline is a valid timeline for a WorkletAnimation');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let constructorFunc = function() { new WorkletAnimation(
-      'my-animation', []); };
+      'test-animator', []); };
   assert_throws('NotSupportedError', constructorFunc);
 }, 'If there are no effects specified, object construction should fail');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   let effect = CreateKeyframeEffect(document.querySelector('#element'));
 
   let otherDoc = document.implementation.createHTMLDocument();
@@ -79,21 +94,31 @@
   let otherEffect = CreateKeyframeEffect(otherElement);
 
   let constructorFunc = function() { new WorkletAnimation(
-      'my-animation', [ effect, otherEffect ]); };
+      'test-animator', [ effect, otherEffect ]); };
   assert_throws('NotSupportedError', constructorFunc);
 }, 'If the effects are from different documents, object construction should fail');
 
-test(function() {
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
   // TODO(crbug.com/781816): Allow KeyframeEffects with no target in AnimationWorklet.
   let effect = CreateKeyframeEffect(null);
   let effect2 = CreateKeyframeEffect(document.querySelector('#element'));
 
   let constructorFunc = function() { new WorkletAnimation(
-      'my-animation', [ effect, effect2 ]); };
+      'test-animator', [ effect, effect2 ]); };
   assert_throws('NotSupportedError', constructorFunc);
 
   let constructorFunc2 = function() { new WorkletAnimation(
-      'my-animation', [ effect2, effect ]); };
+      'test-animator', [ effect2, effect ]); };
   assert_throws('NotSupportedError', constructorFunc2);
 }, 'If an effect has no target, object construction should fail');
+
+promise_test(async t => {
+  await runInAnimationWorklet(document.getElementById('simple_animate').textContent);
+  let effect = CreateKeyframeEffect(document.querySelector('#element'));
+  let constructorFunc = function() {
+      new WorkletAnimation('unregistered-animator', effect);
+  };
+  assert_throws('InvalidStateError', constructorFunc);
+}, 'Constructing worklet animation for unregisested animator should throw');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/consecutive-calls.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/consecutive-calls.html
index 987f7a7..3538630 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/consecutive-calls.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/consecutive-calls.html
@@ -6,44 +6,51 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  function assert_expected_events(events) {
-    assert_equals(events.length, 4);
-    assert_equals(events[0], 'chooser-opened(file://)');
-    assert_equals(events[1], 'discovering');
-    let idsByName = new AddDeviceEventSet();
-    idsByName.assert_add_device_event(events[2]);
-    assert_true(idsByName.has('Heart Rate Device'));
-    assert_equals(events[3], 'discovery-idle');
-    return idsByName;
-  };
+bluetooth_test(
+    () => {
+      function assert_expected_events(events) {
+        assert_equals(events.length, 4);
+        assert_equals(events[0], 'chooser-opened(file://)');
+        assert_equals(events[1], 'discovering');
+        let idsByName = new AddDeviceEventSet();
+        idsByName.assert_add_device_event(events[2]);
+        assert_true(idsByName.has('Heart Rate Device'));
+        assert_equals(events[3], 'discovery-idle');
+        return idsByName;
+      };
 
-  setBluetoothManualChooser(true);
-  // Open a chooser.
-  let firstRequestDevicePromise = setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => assert_promise_rejects_with_message(
-        requestDeviceWithTrustedClick({filters: [{services: ['heart_rate']}]}),
-        new DOMException('User cancelled the requestDevice() chooser.',
-                         'NotFoundError')));
+      setBluetoothManualChooser(true);
+      // Open a chooser.
+      let firstRequestDevicePromise =
+          setBluetoothFakeAdapter('HeartRateAdapter')
+              .then(
+                  () => assert_promise_rejects_with_message(
+                      requestDeviceWithTrustedClick(
+                          {filters: [{services: ['heart_rate']}]}),
+                      new DOMException(
+                          'User cancelled the requestDevice() chooser.',
+                          'NotFoundError')));
 
-  return getBluetoothManualChooserEvents(4)
-    .then(assert_expected_events)
-    .then(() => {
-      // Open another chooser.
-      let secondRequestDevicePromise =
-        requestDeviceWithTrustedClick({filters: [{services: ['heart_rate']}]})
-          .then(device => assert_equals(device.constructor.name,
-                                        'BluetoothDevice'));
       return getBluetoothManualChooserEvents(4)
-        .then(assert_expected_events)
-        .then(idsByName => {
-          sendBluetoothManualChooserEvent(
-            'selected', idsByName.get('Heart Rate Device'));
-          return Promise.all([
-            firstRequestDevicePromise,
-            secondRequestDevicePromise]);
-        });
-    });
-}, 'A second call to requestDevice with the chooser opened will close the ' +
-   'previous chooser.');
+          .then(assert_expected_events)
+          .then(() => {
+            // Open another chooser.
+            let secondRequestDevicePromise =
+                requestDeviceWithTrustedClick(
+                    {filters: [{services: ['heart_rate']}]})
+                    .then(
+                        device => assert_equals(
+                            device.constructor.name, 'BluetoothDevice'));
+            return getBluetoothManualChooserEvents(4)
+                .then(assert_expected_events)
+                .then(idsByName => {
+                  sendBluetoothManualChooserEvent(
+                      'selected', idsByName.get('Heart Rate Device'));
+                  return Promise.all(
+                      [firstRequestDevicePromise, secondRequestDevicePromise]);
+                });
+          });
+    },
+    'A second call to requestDevice with the chooser opened will close the ' +
+        'previous chooser.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/device-removed.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/device-removed.html
index a745eb9c..6e099c9 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/device-removed.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/device-removed.html
@@ -6,32 +6,38 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  setBluetoothManualChooser(true);
+bluetooth_test(
+    () => {
+      setBluetoothManualChooser(true);
 
-  let requestDevicePromise =
-    setBluetoothFakeAdapter('DevicesRemovedAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['glucose']},
-                  {services: ['heart_rate']}]}));
-  return assert_promise_rejects_with_message(
-    getBluetoothManualChooserEvents(5).then(events => {
-      assert_equals(events[0], 'chooser-opened(file://)');
-      let idsByName = new AddDeviceEventSet();
-      idsByName.assert_add_device_event(events[1]);
-      assert_true(idsByName.has('Connected Heart Rate Device'), events[1]);
-      assert_equals(events[2], 'discovering');
-      idsByName.assert_add_device_event(events[3]);
-      assert_true(idsByName.has('New Glucose Device'));
-      assert_equals(events[4], 'discovery-idle');
-      sendBluetoothManualChooserEvent(
-        'selected', idsByName.get('New Glucose Device'));
-      return requestDevicePromise;
-    }), new DOMException('User selected a device that doesn\'t exist anymore.',
-                         'NotFoundError'));
-  // TODO(crbug.com/654649): Chooser should receive a signal that the device
-  // was removed and grey out/disable the row.
-}, 'Devices are added and removed. Chooser should not get a signal that the ' +
-   'device was removed and promise should be rejected when a removed device ' +
-   'is selected.');
+      let requestDevicePromise =
+          setBluetoothFakeAdapter('DevicesRemovedAdapter')
+              .then(() => requestDeviceWithTrustedClick({
+                      filters:
+                          [{services: ['glucose']}, {services: ['heart_rate']}]
+                    }));
+      return assert_promise_rejects_with_message(
+          getBluetoothManualChooserEvents(5).then(events => {
+            assert_equals(events[0], 'chooser-opened(file://)');
+            let idsByName = new AddDeviceEventSet();
+            idsByName.assert_add_device_event(events[1]);
+            assert_true(
+                idsByName.has('Connected Heart Rate Device'), events[1]);
+            assert_equals(events[2], 'discovering');
+            idsByName.assert_add_device_event(events[3]);
+            assert_true(idsByName.has('New Glucose Device'));
+            assert_equals(events[4], 'discovery-idle');
+            sendBluetoothManualChooserEvent(
+                'selected', idsByName.get('New Glucose Device'));
+            return requestDevicePromise;
+          }),
+          new DOMException(
+              'User selected a device that doesn\'t exist anymore.',
+              'NotFoundError'));
+      // TODO(crbug.com/654649): Chooser should receive a signal that the device
+      // was removed and grey out/disable the row.
+    },
+    'Devices are added and removed. Chooser should not get a signal that the ' +
+        'device was removed and promise should be rejected when a removed device ' +
+        'is selected.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-chooser-test.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-chooser-test.html
index ce66e76..a8ab899d 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-chooser-test.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-chooser-test.html
@@ -7,16 +7,15 @@
 <script>
 'use strict';
 // TODO(https://crbug.com/719826): This is a temporary test to try the
-// FakeBluetoothChooser API as it is implemented. This test should be delete after
-// the feature is completed. The implementation details can be found in the design
-// document.
+// FakeBluetoothChooser API as it is implemented. This test should be delete
+// after the feature is completed. The implementation details can be found in
+// the design document.
 // https://docs.google.com/document/d/1XFl_4ZAgO8ddM6U53A9AfUuZeWgJnlYD5wtbXqEpzeg
 const test_desc = 'Ensure that the FakeBluetoothChooser API works correctly.';
 
-bluetooth_test(() => navigator.bluetooth.test.simulateCentral({
-  state: 'powered-on'
-})
-    .then(() => navigator.bluetooth.test.getManualChooser())
-    .then(chooser => assert_true(typeof chooser !== 'undefined')),
+bluetooth_test(
+    () => navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
+              .then(() => navigator.bluetooth.test.getManualChooser())
+              .then(chooser => assert_true(typeof chooser !== 'undefined')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-simulate-advertisement-received-test.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-simulate-advertisement-received-test.html
index d3908803e..1420d001 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-simulate-advertisement-received-test.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/fake-bluetooth-simulate-advertisement-received-test.html
@@ -13,8 +13,12 @@
     'correctly.';
 const company_id = '224';
 const data = new TextEncoder().encode('foo');
-const manufacturerDataMap = {[company_id]: data};
-const serviceDataMap = {[health_thermometer.uuid]: data};
+const manufacturerDataMap = {
+  [company_id]: data
+};
+const serviceDataMap = {
+  [health_thermometer.uuid]: data
+};
 const scanRecord = {
   name: 'Health Thermometer',
   uuids: ['generic_access', health_thermometer.uuid],
@@ -30,17 +34,19 @@
   scanRecord: {},
 };
 
-bluetooth_test(() => navigator.bluetooth.test.simulateCentral({
-  state: 'powered-on'
-})
-    .then(_ => fake_central = _)
-    // Test that scanRecord fields are indeed optional.
-    .then(() => fake_central.simulateAdvertisementReceived(scanResult))
-    .then(fake_peripheral => assert_true(
-        fake_peripheral.address === '09:09:09:09:09:09'))
-    // Test the scanRecord fields.
-    .then(() => {
-      scanResult.scanRecord = scanRecord;
-      return fake_central.simulateAdvertisementReceived(scanResult);
-    }), test_desc);
+bluetooth_test(
+    () =>
+        navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
+            .then(_ => fake_central = _)
+            // Test that scanRecord fields are indeed optional.
+            .then(() => fake_central.simulateAdvertisementReceived(scanResult))
+            .then(
+                fake_peripheral => assert_true(
+                    fake_peripheral.address === '09:09:09:09:09:09'))
+            // Test the scanRecord fields.
+            .then(() => {
+              scanResult.scanRecord = scanRecord;
+              return fake_central.simulateAdvertisementReceived(scanResult);
+            }),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/multiple-matching-devices.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/multiple-matching-devices.html
index 62e3199..df346434 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/multiple-matching-devices.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/multiple-matching-devices.html
@@ -9,26 +9,26 @@
 bluetooth_test(() => {
   setBluetoothManualChooser(true);
   let requestDevicePromise =
-    setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['glucose']},
-                  {services: ['heart_rate']}]
-      }));
+      setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
+          .then(() => requestDeviceWithTrustedClick({
+                  filters: [{services: ['glucose']}, {services: ['heart_rate']}]
+                }));
   return getBluetoothManualChooserEvents(5)
-    .then(events => {
-      assert_equals(events.length, 5, events);
-      assert_equals(events[0], 'chooser-opened(file://)');
-      assert_equals(events[1], 'discovering');
-      let idsByName = new AddDeviceEventSet();
-      for (let addedDevice of [events[2], events[3]]) {
-        idsByName.assert_add_device_event(addedDevice);
-      }
-      assert_true(idsByName.has('Heart Rate Device'));
-      assert_true(idsByName.has('Glucose Device'));
-      assert_equals(events[4], 'discovery-idle');
-      sendBluetoothManualChooserEvent(
-        'selected', idsByName.get('Glucose Device'));
-      return requestDevicePromise;
-    }).then(device => assert_equals(device.name, 'Glucose Device'));
+      .then(events => {
+        assert_equals(events.length, 5, events);
+        assert_equals(events[0], 'chooser-opened(file://)');
+        assert_equals(events[1], 'discovering');
+        let idsByName = new AddDeviceEventSet();
+        for (let addedDevice of [events[2], events[3]]) {
+          idsByName.assert_add_device_event(addedDevice);
+        }
+        assert_true(idsByName.has('Heart Rate Device'));
+        assert_true(idsByName.has('Glucose Device'));
+        assert_equals(events[4], 'discovery-idle');
+        sendBluetoothManualChooserEvent(
+            'selected', idsByName.get('Glucose Device'));
+        return requestDevicePromise;
+      })
+      .then(device => assert_equals(device.name, 'Glucose Device'));
 }, 'The chooser includes all devices.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-all-types.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-all-types.html
index 0007d9cd..04dcb22 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-all-types.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-all-types.html
@@ -10,15 +10,14 @@
   setBluetoothManualChooser(true);
 
   let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [
-          {services: ['heart_rate']},
-          {services: ['battery_service']},
-          {services: ['tx_power']},
-          {services: ['generic_access']},
-          {services: ['glucose']}]
-      }));
+      setBluetoothFakeAdapter('DeviceEventAdapter')
+          .then(() => requestDeviceWithTrustedClick({
+                  filters: [
+                    {services: ['heart_rate']}, {services: ['battery_service']},
+                    {services: ['tx_power']}, {services: ['generic_access']},
+                    {services: ['glucose']}
+                  ]
+                }));
   return getBluetoothManualChooserEvents(7).then(events => {
     assert_equals(events[0], 'chooser-opened(file://)');
     let idsByName = new AddDeviceEventSet();
@@ -30,11 +29,11 @@
     idsByName.assert_add_device_event(events[5]);
     assert_true(idsByName.has('New Glucose Device'), 'Glucose Device');
     assert_true(idsByName.has('Changing Battery Device'), 'Battery Device');
-    assert_true(idsByName.has('Discovery Generic Access Device',
-                              'Generic Access Device'));
+    assert_true(idsByName.has(
+        'Discovery Generic Access Device', 'Generic Access Device'));
     assert_equals(events[6], 'discovery-idle');
     sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('Connected Heart Rate Device'));
+        'selected', idsByName.get('Connected Heart Rate Device'));
     return requestDevicePromise;
   });
 });
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-connected-devices.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-connected-devices.html
index dbde6ae43..54d36e3 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-connected-devices.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-connected-devices.html
@@ -9,10 +9,10 @@
 bluetooth_test(() => {
   setBluetoothManualChooser(true);
 
-  let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['heart_rate']}]}));
+  let requestDevicePromise = setBluetoothFakeAdapter('DeviceEventAdapter')
+                                 .then(() => requestDeviceWithTrustedClick({
+                                         filters: [{services: ['heart_rate']}]
+                                       }));
   return getBluetoothManualChooserEvents(4).then(events => {
     assert_equals(events[0], 'chooser-opened(file://)');
     let idsByName = new AddDeviceEventSet();
@@ -21,7 +21,7 @@
     assert_equals(events[2], 'discovering');
     assert_equals(events[3], 'discovery-idle');
     sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('Connected Heart Rate Device'));
+        'selected', idsByName.get('Connected Heart Rate Device'));
     return requestDevicePromise;
   });
 });
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html
index 6c682d2..105c2b2 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html
@@ -9,10 +9,10 @@
 bluetooth_test(() => {
   setBluetoothManualChooser(true);
 
-  let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['glucose']}]}));
+  let requestDevicePromise = setBluetoothFakeAdapter('DeviceEventAdapter')
+                                 .then(
+                                     () => requestDeviceWithTrustedClick(
+                                         {filters: [{services: ['glucose']}]}));
   return getBluetoothManualChooserEvents(4).then(events => {
     assert_equals(events[0], 'chooser-opened(file://)');
     assert_equals(events[1], 'discovering');
@@ -21,7 +21,7 @@
     assert_true(idsByName.has('New Glucose Device'));
     assert_equals(events[3], 'discovery-idle');
     sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('New Glucose Device'));
+        'selected', idsByName.get('New Glucose Device'));
     return requestDevicePromise;
   });
 });
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-changed.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-changed.html
index 02d1a2fa..8ca980ee 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-changed.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-changed.html
@@ -10,9 +10,10 @@
   setBluetoothManualChooser(true);
 
   let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['battery_service']}]}));
+      setBluetoothFakeAdapter('DeviceEventAdapter')
+          .then(
+              () => requestDeviceWithTrustedClick(
+                  {filters: [{services: ['battery_service']}]}));
   return getBluetoothManualChooserEvents(4).then(events => {
     assert_equals(events[0], 'chooser-opened(file://)');
     assert_equals(events[1], 'discovering');
@@ -21,7 +22,7 @@
     assert_true(idsByName.has('Changing Battery Device'));
     assert_equals(events[3], 'discovery-idle');
     sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('Changing Battery Device'));
+        'selected', idsByName.get('Changing Battery Device'));
     return requestDevicePromise;
   });
 });
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-services-discovered.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-services-discovered.html
index 78b444c..6dadb3f 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-services-discovered.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-services-discovered.html
@@ -10,9 +10,10 @@
   setBluetoothManualChooser(true);
 
   let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['generic_access']}]}));
+      setBluetoothFakeAdapter('DeviceEventAdapter')
+          .then(
+              () => requestDeviceWithTrustedClick(
+                  {filters: [{services: ['generic_access']}]}));
   return getBluetoothManualChooserEvents(4).then(events => {
     assert_equals(events[0], 'chooser-opened(file://)');
     assert_equals(events[1], 'discovering');
@@ -21,7 +22,7 @@
     assert_true(idsByName.has('Discovery Generic Access Device'));
     assert_equals(events[3], 'discovery-idle');
     sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('Discovery Generic Access Device'));
+        'selected', idsByName.get('Discovery Generic Access Device'));
     return requestDevicePromise;
   });
 });
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-finds-new-device.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-finds-new-device.html
index 9f27308..1bdd329 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-finds-new-device.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-finds-new-device.html
@@ -11,34 +11,37 @@
 
   // Open the chooser, looking for a Heart Rate device.
   let requestDevicePromise =
-    setBluetoothFakeAdapter('SecondDiscoveryFindsHeartRateAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['heart_rate']}]
-      }));
+      setBluetoothFakeAdapter('SecondDiscoveryFindsHeartRateAdapter')
+          .then(
+              () => requestDeviceWithTrustedClick(
+                  {filters: [{services: ['heart_rate']}]}));
 
   // The adapter finds nothing, so we just see discovery start and stop.
-  return getBluetoothManualChooserEvents(3).then(events => {
-    assert_array_equals(events,
-                        ['chooser-opened(file://)',
-                         'discovering',
-                         'discovery-idle',
-                        ]);
+  return getBluetoothManualChooserEvents(3)
+      .then(events => {
+        assert_array_equals(events, [
+          'chooser-opened(file://)',
+          'discovering',
+          'discovery-idle',
+        ]);
 
-    // On the second discovery, the adapter finds the Heart Rate device.
-    sendBluetoothManualChooserEvent('rescan', '');
-    return getBluetoothManualChooserEvents(3);
-  }).then(events => {
-    assert_equals(events.length, 3, events);
-    assert_equals(events[0], 'discovering', 'events[0]');
-    let idsByName = new AddDeviceEventSet();
-    idsByName.assert_add_device_event(events[1]);
-    assert_true(idsByName.has('Heart Rate Device'));
-    assert_equals(events[2], 'discovery-idle');
+        // On the second discovery, the adapter finds the Heart Rate device.
+        sendBluetoothManualChooserEvent('rescan', '');
+        return getBluetoothManualChooserEvents(3);
+      })
+      .then(events => {
+        assert_equals(events.length, 3, events);
+        assert_equals(events[0], 'discovering', 'events[0]');
+        let idsByName = new AddDeviceEventSet();
+        idsByName.assert_add_device_event(events[1]);
+        assert_true(idsByName.has('Heart Rate Device'));
+        assert_equals(events[2], 'discovery-idle');
 
-    // Select it and let the test complete.
-    sendBluetoothManualChooserEvent('selected',
-                                               idsByName.get('Heart Rate Device'));
-    return requestDevicePromise;
-  }).then(device => assert_equals(device.name, 'Heart Rate Device'));
+        // Select it and let the test complete.
+        sendBluetoothManualChooserEvent(
+            'selected', idsByName.get('Heart Rate Device'));
+        return requestDevicePromise;
+      })
+      .then(device => assert_equals(device.name, 'Heart Rate Device'));
 }, 'The chooser can restart the BT scan.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-includes-previous-device.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-includes-previous-device.html
index 54ca460..ebd47e6 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-includes-previous-device.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/restart-scan-includes-previous-device.html
@@ -10,38 +10,41 @@
   setBluetoothManualChooser(true);
 
   // Open the chooser, looking for a Heart Rate device.
-  let requestDevicePromise =
-    setBluetoothFakeAdapter('DeviceEventAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['heart_rate']}]
-      }));
+  let requestDevicePromise = setBluetoothFakeAdapter('DeviceEventAdapter')
+                                 .then(() => requestDeviceWithTrustedClick({
+                                         filters: [{services: ['heart_rate']}]
+                                       }));
 
   // The connected Heart Rate Device is added to the chooser.
-  return getBluetoothManualChooserEvents(4).then(events => {
-    assert_equals(events.length, 4, events);
-    assert_equals(events[0], 'chooser-opened(file://)');
-    let idsByName = new AddDeviceEventSet();
-    idsByName.assert_add_device_event(events[1]);
-    assert_true(idsByName.has('Connected Heart Rate Device'));
-    assert_equals(events[2], 'discovering');
-    assert_equals(events[3], 'discovery-idle');
+  return getBluetoothManualChooserEvents(4)
+      .then(events => {
+        assert_equals(events.length, 4, events);
+        assert_equals(events[0], 'chooser-opened(file://)');
+        let idsByName = new AddDeviceEventSet();
+        idsByName.assert_add_device_event(events[1]);
+        assert_true(idsByName.has('Connected Heart Rate Device'));
+        assert_equals(events[2], 'discovering');
+        assert_equals(events[3], 'discovery-idle');
 
-    // After restarting a scan the connected device should be added to the
-    // chooser.
-    sendBluetoothManualChooserEvent('rescan', '');
-    return getBluetoothManualChooserEvents(3);
-  }).then(events => {
-    let idsByName = new AddDeviceEventSet();
-    assert_equals(events.length, 3, events);
-    idsByName.assert_add_device_event(events[0]);
-    assert_true(idsByName.has('Connected Heart Rate Device'));
-    assert_equals(events[1], 'discovering', events[1]);
-    assert_equals(events[2], 'discovery-idle');
+        // After restarting a scan the connected device should be added to the
+        // chooser.
+        sendBluetoothManualChooserEvent('rescan', '');
+        return getBluetoothManualChooserEvents(3);
+      })
+      .then(events => {
+        let idsByName = new AddDeviceEventSet();
+        assert_equals(events.length, 3, events);
+        idsByName.assert_add_device_event(events[0]);
+        assert_true(idsByName.has('Connected Heart Rate Device'));
+        assert_equals(events[1], 'discovering', events[1]);
+        assert_equals(events[2], 'discovery-idle');
 
-    // Select it and let the test complete.
-    sendBluetoothManualChooserEvent(
-      'selected', idsByName.get('Connected Heart Rate Device'));
-    return requestDevicePromise;
-  }).then(device => assert_equals(device.name, 'Connected Heart Rate Device'));
+        // Select it and let the test complete.
+        sendBluetoothManualChooserEvent(
+            'selected', idsByName.get('Connected Heart Rate Device'));
+        return requestDevicePromise;
+      })
+      .then(
+          device => assert_equals(device.name, 'Connected Heart Rate Device'));
 }, 'The chooser shows previously connected devices.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/correct-filters.html b/third_party/blink/web_tests/bluetooth/requestDevice/correct-filters.html
index 3ec2773..224a6230 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/correct-filters.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/correct-filters.html
@@ -12,11 +12,13 @@
   // that this requestDevice() call tells the platform to scan for only devices
   // that include the Battery, Glucose, or Heart Rate services.
   return setBluetoothFakeAdapter('ScanFilterCheckingAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['battery_service']},
-                {services: ['glucose', 'heart_rate']}],
-      // The optionalServices shouldn't affect the platform's scan.
-      optionalServices: ['generic_access']
-    }));
+      .then(() => requestDeviceWithTrustedClick({
+              filters: [
+                {services: ['battery_service']},
+                {services: ['glucose', 'heart_rate']}
+              ],
+              // The optionalServices shouldn't affect the platform's scan.
+              optionalServices: ['generic_access']
+            }));
 }, 'Filters restrict the platform\'s Bluetooth scan.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/device-iframe.https.html b/third_party/blink/web_tests/bluetooth/requestDevice/device-iframe.https.html
index 3885941..9c4ed5c0 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/device-iframe.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/device-iframe.https.html
@@ -16,7 +16,7 @@
   });
 }
 
-promise_test(async(t) => {
+promise_test(async (t) => {
   let iframe = document.createElement('iframe');
   let iframeBluetoothObject = await createIframe(iframe);
   document.body.removeChild(iframe);
@@ -25,5 +25,5 @@
   GCController.collect();
   return callWithTrustedClick(iframeBluetoothObject.requestDevice)
       .catch(err => assert_equals(err.name, 'ReferenceError'));
-  }, 'detaching from iframe invalidates reference to the iframe bluetooth object');
+}, 'detaching from iframe invalidates reference to the iframe bluetooth object');
 </script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/discovery-fails-to-start.html b/third_party/blink/web_tests/bluetooth/requestDevice/discovery-fails-to-start.html
index 62f6a96..8bfa68f 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/discovery-fails-to-start.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/discovery-fails-to-start.html
@@ -11,21 +11,20 @@
   setBluetoothManualChooser(true);
   let requestDevicePromise =
       setBluetoothFakeAdapter('FailStartDiscoveryAdapter')
-        .then(() => requestDeviceWithTrustedClick({
-          filters: [{services: ['generic_access']}]}));
-  return getBluetoothManualChooserEvents(3)
-    .then(events => {
-      assert_array_equals(events,
-                          ['chooser-opened(file://)',
-                           'discovering',
-                           'discovery-failed-to-start'],
-                          events);
-      sendBluetoothManualChooserEvent('cancelled', '');
-      return assert_promise_rejects_with_message(
+          .then(
+              () => requestDeviceWithTrustedClick(
+                  {filters: [{services: ['generic_access']}]}));
+  return getBluetoothManualChooserEvents(3).then(events => {
+    assert_array_equals(
+        events,
+        ['chooser-opened(file://)', 'discovering', 'discovery-failed-to-start'],
+        events);
+    sendBluetoothManualChooserEvent('cancelled', '');
+    return assert_promise_rejects_with_message(
         requestDevicePromise,
-        new DOMException('User cancelled the requestDevice() chooser.',
-                         'NotFoundError'),
+        new DOMException(
+            'User cancelled the requestDevice() chooser.', 'NotFoundError'),
         'The adapter failed to start a discovery session.');
-    });
+  });
 }, 'Discovery session fails to start.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/filter-does-not-match.html b/third_party/blink/web_tests/bluetooth/requestDevice/filter-does-not-match.html
index e6fc97c..3312b19 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/filter-does-not-match.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/filter-does-not-match.html
@@ -6,7 +6,6 @@
 <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
-
 let matching_services = [heart_rate.uuid];
 let matching_name = 'Heart Rate Device';
 let matching_namePrefix = 'Heart';
@@ -15,86 +14,89 @@
 let non_matching_name = 'Some Device';
 let non_matching_namePrefix = 'Some';
 
-let test_specs = [{
-  filters: [{
-    services: non_matching_services,
-    name: non_matching_name,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: matching_services,
-    name: non_matching_name,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-    name: matching_name,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: matching_services,
-    name: matching_name,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-    name: non_matching_name,
-    namePrefix: matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: matching_services,
-    name: non_matching_name,
-    namePrefix: matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-    name: matching_name,
-    namePrefix: matching_namePrefix
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-    name: non_matching_name,
-  }]
-}, {
-  filters: [{
-    services: non_matching_services,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    name: non_matching_name,
-  }]
-}, {
-  filters: [{
-    name: non_matching_name,
-    namePrefix: non_matching_namePrefix
-  }]
-}, {
-  filters: [{
-    namePrefix: non_matching_namePrefix
-  }]
-}];
+let test_specs = [
+  {
+    filters: [{
+      services: non_matching_services,
+      name: non_matching_name,
+      namePrefix: non_matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: non_matching_name,
+      namePrefix: non_matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: matching_name,
+      namePrefix: non_matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: matching_name,
+      namePrefix: non_matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: non_matching_name,
+      namePrefix: matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: non_matching_name,
+      namePrefix: matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: matching_name,
+      namePrefix: matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: non_matching_name,
+    }]
+  },
+  {
+    filters:
+        [{services: non_matching_services, namePrefix: non_matching_namePrefix}]
+  },
+  {
+    filters: [{
+      name: non_matching_name,
+    }]
+  },
+  {filters: [{name: non_matching_name, namePrefix: non_matching_namePrefix}]},
+  {filters: [{namePrefix: non_matching_namePrefix}]}
+];
 
 bluetooth_test(t => {
-  return setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
-    .then(() => {
-      let test_promises = Promise.resolve();
-      test_specs.forEach(args => {
-        test_promises = test_promises.then(() => promise_rejects(
-          t, 'NotFoundError', requestDeviceWithTrustedClick(args)));
-      });
-      return test_promises;
+  return setBluetoothFakeAdapter('GlucoseHeartRateAdapter').then(() => {
+    let test_promises = Promise.resolve();
+    test_specs.forEach(args => {
+      test_promises = test_promises.then(
+          () => promise_rejects(
+              t, 'NotFoundError', requestDeviceWithTrustedClick(args)));
     });
+    return test_promises;
+  });
 }, 'If at least one filter member doesn\'t match the promise must reject.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-prefix-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-prefix-filter.html
index 1fc10d7..add8fb1a 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-prefix-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-prefix-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('EmptyNameHeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        namePrefix: 'a',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{namePrefix: 'a', services: ['heart_rate']}]})));
 }, 'An empty name device is not matched by a filter with a namePrefix.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-wrong-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-wrong-filter.html
index 3cf9f1b..48fd1376 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-wrong-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-name-wrong-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('EmptyNameHeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        name: 'a',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{name: 'a', services: ['heart_rate']}]})));
 }, 'An empty name device is not matched by a filter with a name.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-service-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-service-filter.html
index 3d6bb5c7..28e787d 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-service-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-device-from-service-filter.html
@@ -7,13 +7,14 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  return setBluetoothFakeAdapter('EmptyNameHeartRateAdapter')
-  .then(() => requestDeviceWithTrustedClick({
-    filters: [{services: ['heart_rate']}]}))
-  .then(device => {
-    assert_equals(device.name, '');
-  })
-}, 'An empty name device can be obtained by advertised service UUID.');
+bluetooth_test(
+    () => {return setBluetoothFakeAdapter('EmptyNameHeartRateAdapter')
+               .then(
+                   () => requestDeviceWithTrustedClick(
+                       {filters: [{services: ['heart_rate']}]}))
+               .then(device => {
+                 assert_equals(device.name, '');
+               })},
+    'An empty name device can be obtained by advertised service UUID.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-filter.html
index f37419b1..3ff2dac 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-empty-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('HeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        name: '',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{name: '', services: ['heart_rate']}]})));
 }, 'A named device is not matched by a filter with an empty name.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-empty-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-empty-filter.html
index 19ff4e65..7224d33 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-empty-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-empty-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        name: '',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{name: '', services: ['heart_rate']}]})));
 }, 'An unnamed device can not be obtained by empty name filter.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-prefix-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-prefix-filter.html
index 6d8eed6..444d66a3 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-prefix-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-prefix-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        namePrefix: 'a',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{namePrefix: 'a', services: ['heart_rate']}]})));
 }, 'An unnamed device can not be obtained by namePrefix filter.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-wrong-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-wrong-filter.html
index 1f76c78..ebcd49d 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-wrong-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-name-wrong-filter.html
@@ -9,12 +9,11 @@
 'use strict';
 bluetooth_test(t => {
   return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
-  .then(() => promise_rejects(
-    t, 'NotFoundError', requestDeviceWithTrustedClick({
-      filters: [{
-        name: 'a',
-        services: ['heart_rate']
-      }]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{name: 'a', services: ['heart_rate']}]})));
 }, 'An unnamed device can not be obtained by name filter.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-service-filter.html b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-service-filter.html
index 3a212cd..52c7e6c3 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-service-filter.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/name-missing-device-from-service-filter.html
@@ -7,13 +7,14 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
-  .then(() => requestDeviceWithTrustedClick({
-    filters: [{services: ['heart_rate']}]}))
-  .then(device => {
-      assert_equals(device.name, null);
-  })
-}, 'An unnamed device can be obtained by advertised service UUID.');
+bluetooth_test(
+    () => {return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
+               .then(
+                   () => requestDeviceWithTrustedClick(
+                       {filters: [{services: ['heart_rate']}]}))
+               .then(device => {
+                 assert_equals(device.name, null);
+               })},
+    'An unnamed device can be obtained by advertised service UUID.');
 </script>
 
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/no-devices.html b/third_party/blink/web_tests/bluetooth/requestDevice/no-devices.html
index ccf75cf..60786d2 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/no-devices.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/no-devices.html
@@ -9,10 +9,13 @@
 'use strict';
 bluetooth_test(() => {
   return setBluetoothFakeAdapter('EmptyAdapter')
-    .then(() => assert_promise_rejects_with_message(
-      requestDeviceWithTrustedClick({filters: [{services: ['generic_access']}]}),
-      new DOMException('User cancelled the requestDevice() chooser.',
-                       'NotFoundError'),
-      'No Bluetooth devices in range.'));
+      .then(
+          () => assert_promise_rejects_with_message(
+              requestDeviceWithTrustedClick(
+                  {filters: [{services: ['generic_access']}]}),
+              new DOMException(
+                  'User cancelled the requestDevice() chooser.',
+                  'NotFoundError'),
+              'No Bluetooth devices in range.'));
 }, 'Reject with NotFoundError if there are no devices around.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/radio-off.html b/third_party/blink/web_tests/bluetooth/requestDevice/radio-off.html
index 9e42ad8..763ee2c 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/radio-off.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/radio-off.html
@@ -10,21 +10,19 @@
 bluetooth_test(() => {
   setBluetoothManualChooser(true);
   let requestDevicePromise =
-    navigator.bluetooth.test.simulateCentral({state: 'powered-off'})
-      .then(() => requestDeviceWithTrustedClick({
-        filters: [{services: ['generic_access']}]}));
-  return getBluetoothManualChooserEvents(2)
-    .then(events => {
-      assert_array_equals(events,
-                          ['chooser-opened(file://)',
-                           'adapter-disabled'],
-                          events);
-      sendBluetoothManualChooserEvent('cancelled', '');
-      return assert_promise_rejects_with_message(
+      navigator.bluetooth.test.simulateCentral({state: 'powered-off'})
+          .then(
+              () => requestDeviceWithTrustedClick(
+                  {filters: [{services: ['generic_access']}]}));
+  return getBluetoothManualChooserEvents(2).then(events => {
+    assert_array_equals(
+        events, ['chooser-opened(file://)', 'adapter-disabled'], events);
+    sendBluetoothManualChooserEvent('cancelled', '');
+    return assert_promise_rejects_with_message(
         requestDevicePromise,
-        new DOMException('User cancelled the requestDevice() chooser.',
-                         'NotFoundError'),
+        new DOMException(
+            'User cancelled the requestDevice() chooser.', 'NotFoundError'),
         'Bluetooth adapter is not powered.');
-    });
+  });
 }, 'Reject with NotFoundError if the BT radio is off.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-fails.html b/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-fails.html
index 4728d3c..2bce867 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-fails.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-fails.html
@@ -12,8 +12,10 @@
   // support both services to pass the filter, and neither has a Battery
   // service.
   return setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
-    .then(() => promise_rejects(
-      t, 'NotFoundError', requestDeviceWithTrustedClick({
-        filters: [{services: ['heart_rate', 'battery_service']}]})));
+      .then(
+          () => promise_rejects(
+              t, 'NotFoundError',
+              requestDeviceWithTrustedClick(
+                  {filters: [{services: ['heart_rate', 'battery_service']}]})));
 }, 'Too-strict filters do prevent matching.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-succeeds.html b/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-succeeds.html
index f199e05a..d885d51 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-succeeds.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/single-filter-two-services-succeeds.html
@@ -9,8 +9,9 @@
 'use strict';
 bluetooth_test(() => {
   return setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['glucose', 'tx_power']}]
-    })).then(device => assert_equals(device.name, 'Glucose Device'));
+      .then(
+          () => requestDeviceWithTrustedClick(
+              {filters: [{services: ['glucose', 'tx_power']}]}))
+      .then(device => assert_equals(device.name, 'Glucose Device'));
 }, 'Filter with 2 services returns a matching device.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/two-filters.html b/third_party/blink/web_tests/bluetooth/requestDevice/two-filters.html
index 4808e29..0bec24e 100644
--- a/third_party/blink/web_tests/bluetooth/requestDevice/two-filters.html
+++ b/third_party/blink/web_tests/bluetooth/requestDevice/two-filters.html
@@ -9,9 +9,10 @@
 'use strict';
 bluetooth_test(() => {
   return setBluetoothFakeAdapter('GlucoseHeartRateAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['battery_service']},
-                {services: ['heart_rate']}]
-    })).then(device => assert_equals(device.name, 'Heart Rate Device'));
+      .then(() => requestDeviceWithTrustedClick({
+              filters:
+                  [{services: ['battery_service']}, {services: ['heart_rate']}]
+            }))
+      .then(device => assert_equals(device.name, 'Heart Rate Device'));
 }, 'An extra filter doesn\'t prevent matching.');
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/accept-all-with-filter-throws.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/accept-all-with-filter-throws.https.html
index 34cf12c5..eb18fdf 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/accept-all-with-filter-throws.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/accept-all-with-filter-throws.https.html
@@ -7,17 +7,18 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-scanning-helpers.js"></script>
 <script>
 'use strict';
-const test_desc = "requestLEScan scan options should have exactly one of " +
-    "'filters' or 'acceptAllAdvertisements:true'. Reject with TypeError if not.";
+const test_desc = 'requestLEScan scan options should have exactly one of ' +
+    '\'filters\' or \'acceptAllAdvertisements:true\'. Reject with TypeError if not.';
 const expected = new DOMException(
-    "Failed to execute 'requestLEScan' on 'Bluetooth': " +
-    "Either 'filters' should be present or " +
-    "'acceptAllAdvertisements' should be true, but not both.",
+    'Failed to execute \'requestLEScan\' on \'Bluetooth\': ' +
+        'Either \'filters\' should be present or ' +
+        '\'acceptAllAdvertisements\' should be true, but not both.',
     new TypeError());
 
 bluetooth_test(() => {
-    return assert_promise_rejects_with_message(
-         requestLEScanWithTrustedClick({filters: [], acceptAllAdvertisements: true}),
-         expected);
-  }, test_desc);
+  return assert_promise_rejects_with_message(
+      requestLEScanWithTrustedClick(
+          {filters: [], acceptAllAdvertisements: true}),
+      expected);
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.html
new file mode 100644
index 0000000..ced558f
--- /dev/null
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<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 src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
+<script src="../../external/wpt/bluetooth/resources/bluetooth-scanning-helpers.js"></script>
+<script>
+'use strict';
+const test_desc = 'Attempt to connect to scan result.';
+
+bluetooth_test(async (t) => {
+  const fake_central =
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+
+  const fake_device_address = '09:09:09:09:09:09';
+
+  const fake_device = await fake_central.simulatePreconnectedPeripheral({
+    address: fake_device_address,
+    name: 'Some Device',
+    knownServiceUUIDs: [],
+  });
+
+  await callWithTrustedClick(async () => {
+    await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
+  });
+
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
+
+  let scan = {
+    deviceAddress: fake_device_address,
+    rssi: 100,
+    scanRecord: {
+      txPower: 20,
+    },
+  };
+
+  fake_central.simulateAdvertisementReceived(scan);
+
+  const expected =
+      new DOMException('GATT operation not authorized.', 'SecurityError');
+
+  await eventWatcher.wait_for(['advertisementreceived']).then(async (e) => {
+    assert_promise_rejects_with_message(e.device.gatt.connect(), expected);
+  })
+}, test_desc);
+</script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html
index 5b026e6..6ab4f00 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/basic-scan.https.html
@@ -11,23 +11,22 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
   await callWithTrustedClick(async () => {
-    let scan = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
+    let scan = await navigator.bluetooth.requestLEScan(
+        {acceptAllAdvertisements: true});
     assert_equals(scan.constructor.name, 'BluetoothLEScan');
     assert_true(scan.acceptAllAdvertisements)
   });
 
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for('advertisementreceived').then(e => {
     verifyBluetoothAdvertisingEvent(e);
   });
   fake_central.simulateAdvertisementReceived(scanResult);
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/device-ids-match.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/device-ids-match.https.html
new file mode 100644
index 0000000..73bd091
--- /dev/null
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/device-ids-match.https.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<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 src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
+<script src="../../external/wpt/bluetooth/resources/bluetooth-scanning-helpers.js"></script>
+<script>
+'use strict';
+const test_desc = 'Device address have the same device id.';
+
+bluetooth_test(async (t) => {
+  const fake_central =
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+
+  await callWithTrustedClick(async () => {
+    await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
+  });
+
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
+
+  let scan = {
+    deviceAddress: '09:09:09:09:09:09',
+    rssi: 100,
+    scanRecord: {
+      txPower: 20,
+    },
+  };
+
+  let actual_device_id;
+
+  fake_central.simulateAdvertisementReceived(scan);
+  await eventWatcher.wait_for(['advertisementreceived']).then(e => {
+    actual_device_id = e.device.id;
+  });
+
+  fake_central.simulateAdvertisementReceived(scan);
+  await eventWatcher.wait_for(['advertisementreceived']).then(e => {
+    assert_equals(e.device.id, actual_device_id);
+  });
+
+  scan.deviceAddress = 'FF:FF:FF:FF:FF:FF'
+  fake_central.simulateAdvertisementReceived(scan);
+  await eventWatcher.wait_for(['advertisementreceived']).then(e => {
+    assert_not_equals(e.device.id, actual_device_id);
+  });
+}, test_desc);
+</script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html
index c05d065..8ddc05b 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/doesnt-consume-user-gesture.https.html
@@ -8,15 +8,21 @@
 'use strict';
 const test_desc = 'requestLEScan calls do not consume user gestures.';
 
-bluetooth_test(() => navigator.bluetooth.test.simulateCentral({ state: 'powered-on' })
-      .then(() => callWithTrustedClick(() => {
-      let first = navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
-      let second = navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
-      return Promise.all([
-        first.then(scan => assert_equals(
-          scan.constructor.name, 'BluetoothLEScan')),
-        second.then(scan => assert_equals(
-          scan.constructor.name, 'BluetoothLEScan')),
-      ]);
-    })), test_desc);
+bluetooth_test(
+    () => navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
+              .then(() => callWithTrustedClick(() => {
+                      let first = navigator.bluetooth.requestLEScan(
+                          {acceptAllAdvertisements: true});
+                      let second = navigator.bluetooth.requestLEScan(
+                          {acceptAllAdvertisements: true});
+                      return Promise.all([
+                        first.then(
+                            scan => assert_equals(
+                                scan.constructor.name, 'BluetoothLEScan')),
+                        second.then(
+                            scan => assert_equals(
+                                scan.constructor.name, 'BluetoothLEScan')),
+                      ]);
+                    })),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html
index c523e58..aceb7e4 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/le-not-supported.https.html
@@ -6,13 +6,15 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-const test_desc = 'requestLEScan reject with NotFoundError if Bluetooth is not supported.';
-const expected = new DOMException('Bluetooth Low Energy not available.',
-    'NotFoundError');
+const test_desc =
+    'requestLEScan reject with NotFoundError if Bluetooth is not supported.';
+const expected =
+    new DOMException('Bluetooth Low Energy not available.', 'NotFoundError');
 
-bluetooth_test(() => navigator.bluetooth.test.setLESupported(false)
-    .then(() => assert_promise_rejects_with_message(
-        requestLEScanWithTrustedClick({acceptAllAdvertisements: true}),
-        expected, 'Bluetooth Low Energy is not supported.')),
+bluetooth_test(
+    () => navigator.bluetooth.test.setLESupported(false).then(
+        () => assert_promise_rejects_with_message(
+            requestLEScanWithTrustedClick({acceptAllAdvertisements: true}),
+            expected, 'Bluetooth Low Energy is not supported.')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html
index 6591008c..ef0de4b7 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/multiple-scan.https.html
@@ -14,11 +14,13 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
   await callWithTrustedClick(async () => {
-    scan = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
-    scan2 = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
+    scan = await navigator.bluetooth.requestLEScan(
+        {acceptAllAdvertisements: true});
+    scan2 = await navigator.bluetooth.requestLEScan(
+        {acceptAllAdvertisements: true});
   });
 
   assert_true(scan.active);
@@ -28,15 +30,13 @@
   assert_false(scan2.active);
 
   // We should still get an event because we called requestLEScan twice.
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for('advertisementreceived').then(e => {
     verifyBluetoothAdvertisingEvent(e);
   });
   fake_central.simulateAdvertisementReceived(scanResult);
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html
index a4c9b41..1c2bc5d 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/radio-not-present.https.html
@@ -6,13 +6,17 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-const test_desc = 'requestLEScan reject with NotFoundError if there is no BT radio present.';
-const expected = new DOMException('Bluetooth adapter not available.',
-    'NotFoundError');
+const test_desc =
+    'requestLEScan reject with NotFoundError if there is no BT radio present.';
+const expected =
+    new DOMException('Bluetooth adapter not available.', 'NotFoundError');
 
-bluetooth_test(() => navigator.bluetooth.test.simulateCentral({state: 'absent'})
-    .then(() => assert_promise_rejects_with_message(
-        requestLEScanWithTrustedClick({acceptAllAdvertisements: true}),
-        expected, 'Bluetooth adapter is not present.')),
+bluetooth_test(
+    () => navigator.bluetooth.test.simulateCentral({state: 'absent'})
+              .then(
+                  () => assert_promise_rejects_with_message(
+                      requestLEScanWithTrustedClick(
+                          {acceptAllAdvertisements: true}),
+                      expected, 'Bluetooth adapter is not present.')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-iframe.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-iframe.https.html
index 2a5c01b1..101a6a7 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-iframe.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-iframe.https.html
@@ -16,7 +16,7 @@
   });
 }
 
-promise_test(async(t) => {
+promise_test(async (t) => {
   let iframe = document.createElement('iframe');
   let iframeBluetoothObject = await createIframe(iframe);
   document.body.removeChild(iframe);
@@ -25,5 +25,5 @@
   GCController.collect();
   return callWithTrustedClick(iframeBluetoothObject.requestLEScan)
       .catch(err => assert_equals(err.name, 'ReferenceError'));
-  }, 'detaching from iframe invalidates reference to the iframe bluetooth object');
+}, 'detaching from iframe invalidates reference to the iframe bluetooth object');
 </script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-options.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-options.https.html
index fb0dfc2..e003aa7 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-options.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-options.https.html
@@ -11,18 +11,19 @@
 
 bluetooth_test(async () => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
   await callWithTrustedClick(async () => {
-
-    const expected = new DOMException('Failed to execute \'requestLEScan\' on ' +
-        '\'Bluetooth\': \'filters\' member must be non-empty to find any devices.',
+    const expected = new DOMException(
+        'Failed to execute \'requestLEScan\' on ' +
+            '\'Bluetooth\': \'filters\' member must be non-empty to find any devices.',
         new TypeError());
 
     assert_promise_rejects_with_message(
         navigator.bluetooth.requestLEScan({filters: []}), expected)
 
-    let scan = await navigator.bluetooth.requestLEScan({acceptAllAdvertisements: true});
+    let scan = await navigator.bluetooth.requestLEScan(
+        {acceptAllAdvertisements: true});
     assert_true(scan.acceptAllAdvertisements)
     scan.stop();
 
@@ -32,23 +33,22 @@
     assert_equals(scan.filters[0].name, 'Health Thermometer')
     scan.stop();
 
-    scan = await navigator.bluetooth.requestLEScan(
-        {acceptAllAdvertisements: false,
-         filters: [{name: 'Health Thermometer'}]});
+    scan = await navigator.bluetooth.requestLEScan({
+      acceptAllAdvertisements: false,
+      filters: [{name: 'Health Thermometer'}]
+    });
     assert_false(scan.acceptAllAdvertisements)
     assert_equals(scan.filters[0].name, 'Health Thermometer')
     scan.stop();
 
     scan = await navigator.bluetooth.requestLEScan(
-        {acceptAllAdvertisements: true,
-         keepRepeatedDevices: true});
+        {acceptAllAdvertisements: true, keepRepeatedDevices: true});
     assert_true(scan.acceptAllAdvertisements)
     assert_true(scan.keepRepeatedDevices)
     scan.stop();
 
     scan = await navigator.bluetooth.requestLEScan(
-        {acceptAllAdvertisements: true,
-         keepRepeatedDevices: false});
+        {acceptAllAdvertisements: true, keepRepeatedDevices: false});
     assert_true(scan.acceptAllAdvertisements)
     assert_false(scan.keepRepeatedDevices)
     scan.stop();
@@ -59,11 +59,13 @@
     assert_array_equals(scan.filters[0].services, [health_uuid]);
     scan.stop();
 
-    scan = await navigator.bluetooth.requestLEScan(
-        {filters: [{name: 'Some Device', services: [health_uuid]},
-                   {name: 'I love pancakes'},
-                   {namePrefix: 'How now brown '},
-                   {services: ['generic_access', 'human_interface_device']}]})
+    scan = await navigator.bluetooth.requestLEScan({
+      filters: [
+        {name: 'Some Device', services: [health_uuid]},
+        {name: 'I love pancakes'}, {namePrefix: 'How now brown '},
+        {services: ['generic_access', 'human_interface_device']}
+      ]
+    })
 
     assert_equals(scan.filters[0].name, 'Some Device')
     assert_array_equals(scan.filters[0].services, [health_uuid]);
@@ -71,12 +73,11 @@
     assert_equals(scan.filters[2].namePrefix, 'How now brown ')
     // 00001800-0000-1000-8000-00805f9b34fb == generic_access
     // 00001812-0000-1000-8000-00805f9b34fb == human_interface_device
-    assert_array_equals(scan.filters[3].services, 
-        ['00001800-0000-1000-8000-00805f9b34fb',
-         '00001812-0000-1000-8000-00805f9b34fb']);
+    assert_array_equals(scan.filters[3].services, [
+      '00001800-0000-1000-8000-00805f9b34fb',
+      '00001812-0000-1000-8000-00805f9b34fb'
+    ]);
     scan.stop();
   });
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-multiple-filters.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-multiple-filters.https.html
index 7d7b9fd..369f669 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-multiple-filters.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-multiple-filters.https.html
@@ -11,15 +11,14 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
-  await callWithTrustedClick(async () => {
-    await navigator.bluetooth.requestLEScan({filters: [{namePrefix: 'Health'},
-      {services: [health_uuid]}]})
-  });
+  await callWithTrustedClick(
+      async () => {await navigator.bluetooth.requestLEScan(
+          {filters: [{namePrefix: 'Health'}, {services: [health_uuid]}]})});
 
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let scan = {
     deviceAddress: '09:09:09:09:09:09',
@@ -49,7 +48,5 @@
     assert_equals(e.name, 'Health Thermometer');
     assert_array_equals(e.uuids, []);
   });
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-and-uuid-filter.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-and-uuid-filter.https.html
index 3f044c9a..7262653 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-and-uuid-filter.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-and-uuid-filter.https.html
@@ -7,21 +7,23 @@
 <script src="../../external/wpt/bluetooth/resources/bluetooth-scanning-helpers.js"></script>
 <script>
 'use strict';
-const test_desc = 'requestLEScan with a filter that looks for both a name AND a uuid';
+const test_desc =
+    'requestLEScan with a filter that looks for both a name AND a uuid';
 let scan_result;
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
-  await callWithTrustedClick(async () => {
-    scan_result = await navigator.bluetooth.requestLEScan({filters: [{name: 'Some Device', services: [health_uuid]}]})
-  });
+  await callWithTrustedClick(
+      async () => {
+          scan_result = await navigator.bluetooth.requestLEScan(
+              {filters: [{name: 'Some Device', services: [health_uuid]}]})});
   assert_equals(scan_result.filters[0].name, 'Some Device')
   assert_array_equals(scan_result.filters[0].services, [health_uuid]);
 
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for(['advertisementreceived']).then(e => {
     assert_equals(e.name, 'Some Device');
@@ -49,7 +51,5 @@
   fake_central.simulateAdvertisementReceived(scan);
 
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-filter.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-filter.https.html
index 237257df4..d06ec8d7 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-filter.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-filter.https.html
@@ -12,15 +12,15 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
-  await callWithTrustedClick(async () => {
-    scan_result = await navigator.bluetooth.requestLEScan({filters: [{name: 'Health Thermometer'}]})
-  });
+  await callWithTrustedClick(
+      async () => {scan_result = await navigator.bluetooth.requestLEScan(
+                       {filters: [{name: 'Health Thermometer'}]})});
   assert_equals(scan_result.filters[0].name, 'Health Thermometer')
-  
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for('advertisementreceived').then(e => {
     assert_equals(e.name, 'Health Thermometer');
@@ -35,14 +35,12 @@
   };
 
   // This scan has a name that we're not looking for.
-  scan.scanRecord.name = "Not A Health Thermometer"
+  scan.scanRecord.name = 'Not A Health Thermometer'
   fake_central.simulateAdvertisementReceived(scan);
 
   // Our filter is looking for this name
   scan.scanRecord.name = 'Health Thermometer';
   fake_central.simulateAdvertisementReceived(scan);
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-prefix-filter.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-prefix-filter.https.html
index 8b98b27..8713c0e 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-prefix-filter.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-name-prefix-filter.https.html
@@ -12,15 +12,15 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
-  await callWithTrustedClick(async () => {
-    scan_result = await navigator.bluetooth.requestLEScan({filters: [{namePrefix: 'Health'}]})
-  });
+  await callWithTrustedClick(
+      async () => {scan_result = await navigator.bluetooth.requestLEScan(
+                       {filters: [{namePrefix: 'Health'}]})});
   assert_equals(scan_result.filters[0].namePrefix, 'Health')
 
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for('advertisementreceived').then(e => {
     assert_equals(e.name, 'Health Thermometer');
@@ -42,7 +42,5 @@
   scan.scanRecord.name = 'Health Thermometer';
   fake_central.simulateAdvertisementReceived(scan);
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-service-uuid-filter.https.html b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-service-uuid-filter.https.html
index c40683de..7009a1b 100644
--- a/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-service-uuid-filter.https.html
+++ b/third_party/blink/web_tests/bluetooth/requestLEScan/scan-with-service-uuid-filter.https.html
@@ -12,21 +12,22 @@
 
 bluetooth_test(async (t) => {
   const fake_central =
-    await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
+      await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
 
-  await callWithTrustedClick(async () => {
-    scan_result = await navigator.bluetooth.requestLEScan({filters: [{services: [health_uuid]}]})
-  });
+  await callWithTrustedClick(
+      async () => {scan_result = await navigator.bluetooth.requestLEScan(
+                       {filters: [{services: [health_uuid]}]})});
   assert_array_equals(scan_result.filters[0].services, [health_uuid]);
 
-  const eventWatcher = new EventWatcher(t,
-    navigator.bluetooth, ['advertisementreceived']);
+  const eventWatcher =
+      new EventWatcher(t, navigator.bluetooth, ['advertisementreceived']);
 
   let promise = eventWatcher.wait_for('advertisementreceived').then(e => {
     // Note that the simulated scan used 'generic_access' which is defined by
-    // https://www.bluetooth.com/specifications/gatt/services. WebBluetooth returns
-    // the actual uuid value which is used below.
-    assert_array_equals(e.uuids, ["00001800-0000-1000-8000-00805f9b34fb", health_uuid]);
+    // https://www.bluetooth.com/specifications/gatt/services. WebBluetooth
+    // returns the actual uuid value which is used below.
+    assert_array_equals(
+        e.uuids, ['00001800-0000-1000-8000-00805f9b34fb', health_uuid]);
   });
 
   let scan = {
@@ -44,7 +45,5 @@
   scan.scanRecord.uuids = ['generic_access', health_uuid];
   fake_central.simulateAdvertisementReceived(scan);
   return promise;
-
 }, test_desc);
-
 </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 9f0cc4c..3f2d014 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -139,6 +139,12 @@
      {}
     ]
    ],
+   "clipboard-apis/async-write-image-read-image-manual.https.html": [
+    [
+     "/clipboard-apis/async-write-image-read-image-manual.https.html",
+     {}
+    ]
+   ],
    "clipboard-apis/async-write-text-read-dttext-manual.https.html": [
     [
      "/clipboard-apis/async-write-text-read-dttext-manual.https.html",
@@ -4777,6 +4783,12 @@
      {}
     ]
    ],
+   "html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html": [
+    [
+     "/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html",
+     {}
+    ]
+   ],
    "html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html": [
     [
      "/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-direction-down-manual.html",
@@ -39587,6 +39599,18 @@
      {}
     ]
    ],
+   "css/css-display/display-inline-dynamic-001.html": [
+    [
+     "/css/css-display/display-inline-dynamic-001.html",
+     [
+      [
+       "/css/css-display/display-inline-dynamic-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-display/select-4-option-optgroup-display-none.html": [
     [
      "/css/css-display/select-4-option-optgroup-display-none.html",
@@ -106307,6 +106331,18 @@
      {}
     ]
    ],
+   "portals/portals-rendering.html": [
+    [
+     "/portals/portals-rendering.html",
+     [
+      [
+       "/portals/references/portals-rendering.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "quirks/historical/list-item-bullet-size.html": [
     [
      "/quirks/historical/list-item-bullet-size.html",
@@ -115182,26 +115218,6 @@
      {}
     ]
    ],
-   "async-local-storage/META.yml": [
-    [
-     {}
-    ]
-   ],
-   "async-local-storage/helpers/als-tests.js": [
-    [
-     {}
-    ]
-   ],
-   "async-local-storage/helpers/class-assert.js": [
-    [
-     {}
-    ]
-   ],
-   "async-local-storage/helpers/equality-asserters.js": [
-    [
-     {}
-    ]
-   ],
    "audio-output/META.yml": [
     [
      {}
@@ -115692,6 +115708,11 @@
      {}
     ]
    ],
+   "clipboard-apis/resources/greenbox.png": [
+    [
+     {}
+    ]
+   ],
    "common/META.yml": [
     [
      {}
@@ -126967,6 +126988,11 @@
      {}
     ]
    ],
+   "css/css-display/display-inline-dynamic-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-display/select-4-option-optgroup-display-none-ref.html": [
     [
      {}
@@ -139902,6 +139928,11 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/interaction-with-tree-abiding-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-shadow-parts/support/shadow-helper.js": [
     [
      {}
@@ -151702,11 +151733,21 @@
      {}
     ]
    ],
+   "css/mediaqueries/prefers-reduced-motion-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/mediaqueries/reference/ref-green-body.xht": [
     [
      {}
     ]
    ],
+   "css/mediaqueries/resources/matchmedia-utils.js": [
+    [
+     {}
+    ]
+   ],
    "css/mediaqueries/support/media_queries_iframe.html": [
     [
      {}
@@ -173057,6 +173098,26 @@
      {}
     ]
    ],
+   "kv-storage/META.yml": [
+    [
+     {}
+    ]
+   ],
+   "kv-storage/helpers/class-assert.js": [
+    [
+     {}
+    ]
+   ],
+   "kv-storage/helpers/equality-asserters.js": [
+    [
+     {}
+    ]
+   ],
+   "kv-storage/helpers/kvs-tests.js": [
+    [
+     {}
+    ]
+   ],
    "lifecycle/META.yml": [
     [
      {}
@@ -175977,6 +176038,11 @@
      {}
     ]
    ],
+   "portals/references/portals-rendering.html": [
+    [
+     {}
+    ]
+   ],
    "portals/resources/portal-activate-event-portal.html": [
     [
      {}
@@ -175987,6 +176053,11 @@
      {}
     ]
    ],
+   "portals/resources/portals-rendering-portal.html": [
+    [
+     {}
+    ]
+   ],
    "portals/resources/postmessage-referrer.sub.html": [
     [
      {}
@@ -179442,6 +179513,11 @@
      {}
     ]
    ],
+   "resource-timing/resources/blank_page_green.htm.headers": [
+    [
+     {}
+    ]
+   ],
    "resource-timing/resources/blue.png": [
     [
      {}
@@ -183062,6 +183138,11 @@
      {}
     ]
    ],
+   "service-workers/service-worker/resources/xhr-iframe.html": [
+    [
+     {}
+    ]
+   ],
    "service-workers/service-worker/resources/xsl-base-url-iframe.xml": [
     [
      {}
@@ -183122,6 +183203,11 @@
      {}
     ]
    ],
+   "service-workers/service-worker/xhr-response-url.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "service-workers/specgen.json": [
     [
      {}
@@ -187512,11 +187598,6 @@
      {}
     ]
    ],
-   "webstorage/event_initstorageevent-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "webstorage/event_setattribute.js": [
     [
      {}
@@ -200234,48 +200315,6 @@
      {}
     ]
    ],
-   "async-local-storage/api-surface.tentative.https.html": [
-    [
-     "/async-local-storage/api-surface.tentative.https.html",
-     {}
-    ]
-   ],
-   "async-local-storage/key-types.tentative.https.html": [
-    [
-     "/async-local-storage/key-types.tentative.https.html",
-     {}
-    ]
-   ],
-   "async-local-storage/non-secure-context-dynamic-import.tentative.html": [
-    [
-     "/async-local-storage/non-secure-context-dynamic-import.tentative.html",
-     {}
-    ]
-   ],
-   "async-local-storage/non-secure-context-import-statement.tentative.html": [
-    [
-     "/async-local-storage/non-secure-context-import-statement.tentative.html",
-     {}
-    ]
-   ],
-   "async-local-storage/non-secure-context-script-element.tentative.html": [
-    [
-     "/async-local-storage/non-secure-context-script-element.tentative.html",
-     {}
-    ]
-   ],
-   "async-local-storage/storage-smoke-test.tentative.https.html": [
-    [
-     "/async-local-storage/storage-smoke-test.tentative.https.html",
-     {}
-    ]
-   ],
-   "async-local-storage/undefined-value.https.html": [
-    [
-     "/async-local-storage/undefined-value.https.html",
-     {}
-    ]
-   ],
    "audio-output/idlharness.https.window.js": [
     [
      "/audio-output/idlharness.https.window.html",
@@ -211074,6 +211113,12 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/interaction-with-tree-abiding.html": [
+    [
+     "/css/css-shadow-parts/interaction-with-tree-abiding.html",
+     {}
+    ]
+   ],
    "css/css-shadow-parts/invalidation-change-exportparts-forward.html": [
     [
      "/css/css-shadow-parts/invalidation-change-exportparts-forward.html",
@@ -218308,6 +218353,12 @@
      {}
     ]
    ],
+   "css/mediaqueries/prefers-reduced-motion.html": [
+    [
+     "/css/mediaqueries/prefers-reduced-motion.html",
+     {}
+    ]
+   ],
    "css/mediaqueries/test_media_queries.html": [
     [
      "/css/mediaqueries/test_media_queries.html",
@@ -247114,6 +247165,48 @@
      {}
     ]
    ],
+   "kv-storage/api-surface.https.html": [
+    [
+     "/kv-storage/api-surface.https.html",
+     {}
+    ]
+   ],
+   "kv-storage/key-types.https.html": [
+    [
+     "/kv-storage/key-types.https.html",
+     {}
+    ]
+   ],
+   "kv-storage/non-secure-context-dynamic-import.html": [
+    [
+     "/kv-storage/non-secure-context-dynamic-import.html",
+     {}
+    ]
+   ],
+   "kv-storage/non-secure-context-import-statement.html": [
+    [
+     "/kv-storage/non-secure-context-import-statement.html",
+     {}
+    ]
+   ],
+   "kv-storage/non-secure-context-script-element.html": [
+    [
+     "/kv-storage/non-secure-context-script-element.html",
+     {}
+    ]
+   ],
+   "kv-storage/storage-smoke-test.https.html": [
+    [
+     "/kv-storage/storage-smoke-test.https.html",
+     {}
+    ]
+   ],
+   "kv-storage/undefined-value.https.html": [
+    [
+     "/kv-storage/undefined-value.https.html",
+     {}
+    ]
+   ],
    "lifecycle/freeze.html": [
     [
      "/lifecycle/freeze.html",
@@ -247894,6 +247987,12 @@
      {}
     ]
    ],
+   "mediacapture-record/MediaRecorder-pause-resume.html": [
+    [
+     "/mediacapture-record/MediaRecorder-pause-resume.html",
+     {}
+    ]
+   ],
    "mediacapture-record/MediaRecorder-stop.html": [
     [
      "/mediacapture-record/MediaRecorder-stop.html",
@@ -272158,6 +272257,12 @@
      {}
     ]
    ],
+   "resource-timing/redirects.sub.html": [
+    [
+     "/resource-timing/redirects.sub.html",
+     {}
+    ]
+   ],
    "resource-timing/resource-timing-level1.sub.html": [
     [
      "/resource-timing/resource-timing-level1.sub.html",
@@ -272292,12 +272397,6 @@
      {}
     ]
    ],
-   "resource-timing/resource_redirects.html": [
-    [
-     "/resource-timing/resource_redirects.html",
-     {}
-    ]
-   ],
    "resource-timing/resource_reparenting.html": [
     [
      "/resource-timing/resource_reparenting.html",
@@ -274374,6 +274473,12 @@
      {}
     ]
    ],
+   "service-workers/service-worker/xhr-response-url.https.html": [
+    [
+     "/service-workers/service-worker/xhr-response-url.https.html",
+     {}
+    ]
+   ],
    "service-workers/service-worker/xsl-base-url.https.html": [
     [
      "/service-workers/service-worker/xsl-base-url.https.html",
@@ -275164,6 +275269,12 @@
      {}
     ]
    ],
+   "storage/estimate-usage-details-service-workers.https.tentative.window.js": [
+    [
+     "/storage/estimate-usage-details-service-workers.https.tentative.window.html",
+     {}
+    ]
+   ],
    "storage/estimate-usage-details.https.tentative.any.js": [
     [
      "/storage/estimate-usage-details.https.tentative.any.html",
@@ -280700,6 +280811,12 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html": [
+    [
+     "/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-audioparam-interface/audioparam-connect-audioratesignal.html": [
     [
      "/webaudio/the-audio-api/the-audioparam-interface/audioparam-connect-audioratesignal.html",
@@ -303415,50 +303532,6 @@
    "55e8b9871e794c944f329e0e9df6ec140124c660",
    "testharness"
   ],
-  "async-local-storage/META.yml": [
-   "1bbe9e5ac609aa33914ad79d4af7cb2fdf45b9c7",
-   "support"
-  ],
-  "async-local-storage/api-surface.tentative.https.html": [
-   "927871a8961c44bd8adfee1b82aadc9f514962a7",
-   "testharness"
-  ],
-  "async-local-storage/helpers/als-tests.js": [
-   "28087abf811dacda1ca00d81fe1306f97dde88ef",
-   "support"
-  ],
-  "async-local-storage/helpers/class-assert.js": [
-   "31b25cab9f2d88d8df59a0b4ecb35eef3765e380",
-   "support"
-  ],
-  "async-local-storage/helpers/equality-asserters.js": [
-   "ad4623c179d75c8d4ce8b1fa8503f943bf6a7c77",
-   "support"
-  ],
-  "async-local-storage/key-types.tentative.https.html": [
-   "c3985b7711f77818260f08635d35ce3da553178b",
-   "testharness"
-  ],
-  "async-local-storage/non-secure-context-dynamic-import.tentative.html": [
-   "9270f6c82fa2018d1d6c3a199cbe5f6ca2403b56",
-   "testharness"
-  ],
-  "async-local-storage/non-secure-context-import-statement.tentative.html": [
-   "879729696dbb6a767530d77fbd94af0b42afe6b4",
-   "testharness"
-  ],
-  "async-local-storage/non-secure-context-script-element.tentative.html": [
-   "feeddafc8daa02556eb0c5fe068dbde1d45642da",
-   "testharness"
-  ],
-  "async-local-storage/storage-smoke-test.tentative.https.html": [
-   "f978480ff2b80ba5f892c2a2c429e882d655574d",
-   "testharness"
-  ],
-  "async-local-storage/undefined-value.https.html": [
-   "c76c32f950704e42c06d498c4c8614dfbabb2aae",
-   "testharness"
-  ],
   "audio-output/META.yml": [
    "b6a7d4d06259117af8fb843f6a8d252bac01a8f3",
    "support"
@@ -304764,7 +304837,7 @@
    "testharness"
   ],
   "clipboard-apis/async-navigator-clipboard-basics.https.html": [
-   "11ed88c3fa2172c2ca0e635cb7f263f99388ca3a",
+   "ea0ca2902c5ab4643a1d72f554ce21ef1780a4ad",
    "testharness"
   ],
   "clipboard-apis/async-write-dttext-read-dttext-manual.https.html": [
@@ -304775,6 +304848,10 @@
    "1b178696f17ca0bc1bac0b7e1c285ae8d52d5360",
    "manual"
   ],
+  "clipboard-apis/async-write-image-read-image-manual.https.html": [
+   "ac7fb0863e75a1a33451033db054d2bf812d8450",
+   "manual"
+  ],
   "clipboard-apis/async-write-text-read-dttext-manual.https.html": [
    "9f524b93d719b7b94cfcede77948d507bc0d4b57",
    "manual"
@@ -304799,6 +304876,10 @@
    "4131a41bff6fd6bf5fb22fa805aea219ec7f72aa",
    "manual"
   ],
+  "clipboard-apis/resources/greenbox.png": [
+   "6e555e3b197ce2f448f59d61e1488a0175490145",
+   "support"
+  ],
   "common/META.yml": [
    "594c8b170f5043a944fdd4de17d4bd65eba242fb",
    "support"
@@ -326100,7 +326181,7 @@
    "reftest"
   ],
   "css/css-backgrounds/background-image-first-line.html": [
-   "c8dee7050b6241279d5f462263b9fdead50004fc",
+   "206ff68d17360ee30768651eaab874f0a6c2996e",
    "reftest"
   ],
   "css/css-backgrounds/background-image-none-gradient-repaint.html": [
@@ -331927,6 +332008,14 @@
    "3d1dcb020df129dd10d66bb4b04d62c3c280cfb6",
    "reftest"
   ],
+  "css/css-display/display-inline-dynamic-001-ref.html": [
+   "8b5f5015f9ebc818cbf48666773440a359e1d740",
+   "support"
+  ],
+  "css/css-display/display-inline-dynamic-001.html": [
+   "7df697f940d4a20d26c92fa40266ed1f67d4cd19",
+   "reftest"
+  ],
   "css/css-display/display-list-item-height-after-dom-change.html": [
    "f8d6e85cee2325f3ae51a950a276430d26c04189",
    "testharness"
@@ -351379,6 +351468,14 @@
    "2dfd4b0510a758c73bf8ac8291088d39077578d7",
    "testharness"
   ],
+  "css/css-shadow-parts/interaction-with-tree-abiding-expected.txt": [
+   "129e043be1aa49fcd14064a1a70f7b4fe09e374e",
+   "support"
+  ],
+  "css/css-shadow-parts/interaction-with-tree-abiding.html": [
+   "c11da7d12dea91306b79d141613ad6563dffb18f",
+   "testharness"
+  ],
   "css/css-shadow-parts/invalidation-change-exportparts-forward.html": [
    "1e319deb633cdb765ea4613eb7c2b8c6dd0e615a",
    "testharness"
@@ -365396,7 +365493,7 @@
    "testharness"
   ],
   "css/css-typed-om/interfaces-expected.txt": [
-   "0211c266922ad2ab120157f9c33c3de2c2a1b974",
+   "caef6ac0dd647e3ffcf987044e7b84740aa4bb69",
    "support"
   ],
   "css/css-typed-om/interfaces.html": [
@@ -378700,11 +378797,19 @@
    "reftest"
   ],
   "css/mediaqueries/prefers-color-scheme-expected.txt": [
-   "600af2aac02524bf8604714923dd3417df96c418",
+   "333ce42031903d3d01646f1679946f35a9942516",
    "support"
   ],
   "css/mediaqueries/prefers-color-scheme.html": [
-   "705a7ca47f87692e0b888cf4cdcd4523304395c9",
+   "c6ca45ab17201466e01006cab78331a0765cc6c8",
+   "testharness"
+  ],
+  "css/mediaqueries/prefers-reduced-motion-expected.txt": [
+   "00fbe2cebee7d39150d68444528cb9e3c2a8f1de",
+   "support"
+  ],
+  "css/mediaqueries/prefers-reduced-motion.html": [
+   "2d9f88474598bcaa0c988639473821171f08b094",
    "testharness"
   ],
   "css/mediaqueries/reference/ref-green-body.xht": [
@@ -378727,6 +378832,10 @@
    "b0f3765358d98906ce8e7cbbdf9743d357e7f2f9",
    "reftest"
   ],
+  "css/mediaqueries/resources/matchmedia-utils.js": [
+   "544f68c0400e4966c9124a9f481c6e12bf985f36",
+   "support"
+  ],
   "css/mediaqueries/support/media_queries_iframe.html": [
    "890eb6c46113afbaf1dc88188c52a791623590a8",
    "support"
@@ -386496,7 +386605,7 @@
    "testharness"
   ],
   "dom/interfaces_exclude=Node-expected.txt": [
-   "41fe5fad5348f067d6775f565a2233662c6093a0",
+   "e5d121860dcb77cc0fc0f8cb9c18ffbc5edc8986",
    "support"
   ],
   "dom/lists/DOMTokenList-Iterable.html": [
@@ -392420,7 +392529,7 @@
    "testharness"
   ],
   "fetch/api/idl.any-expected.txt": [
-   "b013c5c01cf6101d4a24aed21b7933fbae0ea904",
+   "8bf325d7fd92e68889470ac6d61d474f49ac56d1",
    "support"
   ],
   "fetch/api/idl.any.js": [
@@ -392428,15 +392537,15 @@
    "testharness"
   ],
   "fetch/api/idl.any.serviceworker-expected.txt": [
-   "7687f61f03d86d480149000d2956626d00f5530c",
+   "600ca7227b59f7cc1cf99ac568db81447c16b972",
    "support"
   ],
   "fetch/api/idl.any.sharedworker-expected.txt": [
-   "76f58a4b25d6c90e0e51041d60ef8309fcead095",
+   "7f9da6eeccc591484330b525f8195e03f78057a0",
    "support"
   ],
   "fetch/api/idl.any.worker-expected.txt": [
-   "76f58a4b25d6c90e0e51041d60ef8309fcead095",
+   "7f9da6eeccc591484330b525f8195e03f78057a0",
    "support"
   ],
   "fetch/api/policies/csp-blocked-worker-expected.txt": [
@@ -398788,7 +398897,7 @@
    "testharness"
   ],
   "html/dom/interfaces.https_exclude=(Document_Window_HTML._)-expected.txt": [
-   "87ecff0ec04ddaa5ea63cad3a1a194d5d0ca3784",
+   "8f45c85b0b486715fc0cc77fe0c3678228e72fa0",
    "support"
   ],
   "html/dom/interfaces.https_include=(Document_Window)-expected.txt": [
@@ -402415,6 +402524,10 @@
    "c8bdaafdb89555c6708f0d84c3d41e437840be5b",
    "testharness"
   ],
+  "html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html": [
+   "73a3ff667ea8a450cf737890f44d2bfdbb848e98",
+   "manual"
+  ],
   "html/editing/the-hidden-attribute/hidden-1-ref.html": [
    "7346ce919d210a740104ca5e82264bed8377c2d0",
    "support"
@@ -407308,7 +407421,7 @@
    "support"
   ],
   "html/semantics/embedded-content/the-img-element/Image-constructor.html": [
-   "aa838ec8774e2bcaac360bf8ed929e0facc7fa3e",
+   "38b6765bb4984255aaf1bd039b9a9262e85728bc",
    "testharness"
   ],
   "html/semantics/embedded-content/the-img-element/adoption.html": [
@@ -414860,7 +414973,7 @@
    "support"
   ],
   "interfaces/dom.idl": [
-   "503c391102a23db22ae0042c299b23702c004458",
+   "1788faae5207bb7a21b23771bf2b0e978ea49a69",
    "support"
   ],
   "interfaces/encoding.idl": [
@@ -415148,7 +415261,7 @@
    "support"
   ],
   "interfaces/user-timing.idl": [
-   "723b063156a07aa7524f3dab1496b46e51b1e9f8",
+   "da8a0acd6738745fc2a724213e404c2f364329de",
    "support"
   ],
   "interfaces/vibration.idl": [
@@ -415475,6 +415588,50 @@
    "5a4beba7e45830e72c28d460495e977a64a8bc7a",
    "support"
   ],
+  "kv-storage/META.yml": [
+   "bf4a1e6e8b44bc78cf7a624a12b254cbac2f25a5",
+   "support"
+  ],
+  "kv-storage/api-surface.https.html": [
+   "65452f55be044aa5ec722da26717f527ee81a4e3",
+   "testharness"
+  ],
+  "kv-storage/helpers/class-assert.js": [
+   "31b25cab9f2d88d8df59a0b4ecb35eef3765e380",
+   "support"
+  ],
+  "kv-storage/helpers/equality-asserters.js": [
+   "ad4623c179d75c8d4ce8b1fa8503f943bf6a7c77",
+   "support"
+  ],
+  "kv-storage/helpers/kvs-tests.js": [
+   "0ffe71fad780f599a11d915d3b3512c95844f7bd",
+   "support"
+  ],
+  "kv-storage/key-types.https.html": [
+   "0dc930258f8b554c6cae4398df3dba930dcdf03c",
+   "testharness"
+  ],
+  "kv-storage/non-secure-context-dynamic-import.html": [
+   "6ccbf84ba1dc6acd4931da279c887635b7f8a771",
+   "testharness"
+  ],
+  "kv-storage/non-secure-context-import-statement.html": [
+   "fda02aadf14fcb9be859c091df3a2ac1d2c56994",
+   "testharness"
+  ],
+  "kv-storage/non-secure-context-script-element.html": [
+   "66802b1254eda87e1a966d6f835676ecba942cbe",
+   "testharness"
+  ],
+  "kv-storage/storage-smoke-test.https.html": [
+   "df6fd8c8181f579df03972d25fe9a91b2354380f",
+   "testharness"
+  ],
+  "kv-storage/undefined-value.https.html": [
+   "89da5d5c44f353bc1f5f93eaeaf3acd89eee386c",
+   "testharness"
+  ],
   "lifecycle/META.yml": [
    "c1fcbca4c1f8366da1dd9eb91bc9427edeef1153",
    "support"
@@ -416583,8 +416740,12 @@
    "58b9c08ecab0ff8872748f5215743379876ea5ef",
    "testharness"
   ],
+  "mediacapture-record/MediaRecorder-pause-resume.html": [
+   "39a8883664550af1a48ae965d4789a0d6df97d28",
+   "testharness"
+  ],
   "mediacapture-record/MediaRecorder-stop.html": [
-   "8e05fc5491d8da7aaddefea20fae36d705c75bce",
+   "5dd9aa637305f1ac65208b0e5f956831b8c22cb4",
    "testharness"
   ],
   "mediacapture-record/OWNERS": [
@@ -427416,7 +427577,7 @@
    "testharness"
   ],
   "performance-timeline/idlharness.any-expected.txt": [
-   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "0b65efbd20ed67e8fa96eb7400a1ef5c47ddc350",
    "support"
   ],
   "performance-timeline/idlharness.any.js": [
@@ -427424,15 +427585,15 @@
    "testharness"
   ],
   "performance-timeline/idlharness.any.serviceworker-expected.txt": [
-   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "0b65efbd20ed67e8fa96eb7400a1ef5c47ddc350",
    "support"
   ],
   "performance-timeline/idlharness.any.sharedworker-expected.txt": [
-   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "0b65efbd20ed67e8fa96eb7400a1ef5c47ddc350",
    "support"
   ],
   "performance-timeline/idlharness.any.worker-expected.txt": [
-   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "0b65efbd20ed67e8fa96eb7400a1ef5c47ddc350",
    "support"
   ],
   "performance-timeline/performanceentry-tojson.any.js": [
@@ -428036,7 +428197,7 @@
    "support"
   ],
   "portals/portal-activate-event.html": [
-   "ed5602667bb9c898291d81ab2171b99137827a87",
+   "ac1505d2a5b2fe1df083eae75893483e025a2ad7",
    "testharness"
   ],
   "portals/portals-host-null.html": [
@@ -428047,12 +428208,24 @@
    "0386272f441a0c2e19452821968a624d3ab16700",
    "testharness"
   ],
+  "portals/portals-rendering.html": [
+   "8683a38326d96f5e180d3a2042aba652b8c0fa03",
+   "reftest"
+  ],
+  "portals/references/portals-rendering.html": [
+   "4a8414ab5656593811772c3728e4ee83eb034457",
+   "support"
+  ],
   "portals/resources/portal-activate-event-portal.html": [
-   "d0eebcd12e65193024f6444af922b7503e76d8ec",
+   "b2759c3701aaba4f5887a8b90bf4ee30e8153661",
    "support"
   ],
   "portals/resources/portal-activate-event-window.html": [
-   "1994dc3fad11e8b3ad9e8dc71f4aa96f06baa389",
+   "cf09caebc0ff9ac38facde84075a7af5be19fd48",
+   "support"
+  ],
+  "portals/resources/portals-rendering-portal.html": [
+   "1b6f23f512da5bb7d1c7b5b85e48277470d2e146",
    "support"
   ],
   "portals/resources/postmessage-referrer.sub.html": [
@@ -428064,7 +428237,7 @@
    "support"
   ],
   "preload/OWNERS": [
-   "46f12faf7bbd6de21f2faf0613bd77ee31110d52",
+   "39fb049e7802ebd79d9b838013d1555eb2f1c0c2",
    "support"
   ],
   "preload/avoid-delaying-onload-link-preload.html": [
@@ -428108,19 +428281,19 @@
    "support"
   ],
   "preload/link-header-preload-nonce.html": [
-   "2db6eef569f1576a3d405c7f86f6eed8be10c9e2",
+   "51b2224864d5a47c9642ded613efcfc702eb0f9d",
    "testharness"
   ],
   "preload/link-header-preload-nonce.html.headers": [
-   "a3c41d2386ac10576497e3080ff1e6d97dccf6b8",
+   "a54b69378e9cc785ee02ec4a37694f90c366403b",
    "support"
   ],
   "preload/link-header-preload-srcset.tentative.html": [
-   "70d34e525c1feb541fefd277f1ae06a6a5888a08",
+   "9eb8ac4e00d20b9eb48ffd3a981b33b7604246f8",
    "testharness"
   ],
   "preload/link-header-preload-srcset.tentative.html.headers": [
-   "1e4e9b85cac9a70ef01c994631402a5d452cad98",
+   "906de0c95aeb58e24d1b7e779f1ed99de40d7af1",
    "support"
   ],
   "preload/link-header-preload.html": [
@@ -437807,6 +437980,10 @@
    "63f9e06e19083a6d956af0d5916455cd7e91b89c",
    "testharness"
   ],
+  "resource-timing/redirects.sub.html": [
+   "0e3f405e14b7af9f0b2a4fe5ce916ed1e47534de",
+   "testharness"
+  ],
   "resource-timing/resource-timing-expected.txt": [
    "e8973024c18b7b05f08107fd2ac414e3d6c4e1b2",
    "support"
@@ -437903,10 +438080,6 @@
    "89b5874978c97cd25e31da263d07c9f91c69bbfa",
    "testharness"
   ],
-  "resource-timing/resource_redirects.html": [
-   "606662afda4401dd0dccbac2b71b66f3b1961e6f",
-   "testharness"
-  ],
   "resource-timing/resource_reparenting.html": [
    "7d4947fa7703d13a5adb465ff5eebbb4456cace9",
    "testharness"
@@ -437959,6 +438132,10 @@
    "b8a1947b77e25ac6b0d100c75932e8c0a67d846f",
    "support"
   ],
+  "resource-timing/resources/blank_page_green.htm.headers": [
+   "cb762eff806849df46dc758ef7b98b63f27f54c9",
+   "support"
+  ],
   "resource-timing/resources/blue.png": [
    "820f8cace2143bfc45c0c301e84b6c29b8630068",
    "support"
@@ -438300,7 +438477,7 @@
    "support"
   ],
   "resources/idlharness.js": [
-   "926a615e59c9cd6f602c6e700494d1600ac6d0a5",
+   "8b7a1e2d98ea3167793f6996d56211ebb2cf0f41",
    "support"
   ],
   "resources/idlharness.js.headers": [
@@ -441947,6 +442124,10 @@
    "e388e461448f64d37d747b26830f4e869a2c6528",
    "support"
   ],
+  "service-workers/service-worker/resources/xhr-iframe.html": [
+   "bfdfac69712b3e10526e1f059d2638b1fab06eeb",
+   "support"
+  ],
   "service-workers/service-worker/resources/xsl-base-url-iframe.xml": [
    "065a07acb284821dde1cbea8680781a524f21bc2",
    "support"
@@ -442167,6 +442348,14 @@
    "f9ba656b5178359f2c7b6e2419a57ff12ec79d23",
    "testharness"
   ],
+  "service-workers/service-worker/xhr-response-url.https-expected.txt": [
+   "4cd8e750f94dd07e7ecb798ad6a6d38ce5a5fa37",
+   "support"
+  ],
+  "service-workers/service-worker/xhr-response-url.https.html": [
+   "9f00cdd183052568ed7c6933f28f7859739e926c",
+   "testharness"
+  ],
   "service-workers/service-worker/xsl-base-url.https.html": [
    "1d3c36408a66a7785b884c9cdc39dcd2820f9af0",
    "testharness"
@@ -443175,6 +443364,10 @@
    "c854d5b8848638d11563a48348e7c3393ff58459",
    "testharness"
   ],
+  "storage/estimate-usage-details-service-workers.https.tentative.window.js": [
+   "cf3a2aa9430d480df4fc7ff2487e7a42d5c15699",
+   "testharness"
+  ],
   "storage/estimate-usage-details.https.tentative.any.js": [
    "2a1cea5fb8dfa78c320328e667bb7ffd940802b0",
    "testharness"
@@ -446664,7 +446857,7 @@
    "testharness"
   ],
   "user-timing/idlharness.any-expected.txt": [
-   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "0990fccc9a59a13150f62c92114c986d9fdcb7c5",
    "support"
   ],
   "user-timing/idlharness.any.js": [
@@ -446672,15 +446865,15 @@
    "testharness"
   ],
   "user-timing/idlharness.any.serviceworker-expected.txt": [
-   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "0990fccc9a59a13150f62c92114c986d9fdcb7c5",
    "support"
   ],
   "user-timing/idlharness.any.sharedworker-expected.txt": [
-   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "0990fccc9a59a13150f62c92114c986d9fdcb7c5",
    "support"
   ],
   "user-timing/idlharness.any.worker-expected.txt": [
-   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "0990fccc9a59a13150f62c92114c986d9fdcb7c5",
    "support"
   ],
   "user-timing/invoke_with_timing_attributes.html": [
@@ -448631,6 +448824,10 @@
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
+  "webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html": [
+   "b5555b0137af4c1c8f6c5578de4bc9c5eedfe405",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-connect-audioratesignal.html": [
    "517d64f3dbb69e610a5ed17475c45f530f749a42",
    "testharness"
@@ -451887,10 +452084,6 @@
    "fe7fca77a93c7adec48dc31e7926331e427b969b",
    "testharness"
   ],
-  "webstorage/event_initstorageevent-expected.txt": [
-   "0650fd5671d0792d5a501571a11b22ae87e0cff7",
-   "support"
-  ],
   "webstorage/event_initstorageevent.html": [
    "2fe893143614ca6d44becd027f6befba82e18ad1",
    "testharness"
@@ -455396,7 +455589,7 @@
    "support"
   ],
   "workers/OWNERS": [
-   "f62e415410dec282258bafcba7dcff7115547e55",
+   "51dd60fea8510b6c27acaa22bfee6924191fd3e2",
    "support"
   ],
   "workers/README.md": [
@@ -456804,7 +456997,7 @@
    "support"
   ],
   "worklets/OWNERS": [
-   "f62e415410dec282258bafcba7dcff7115547e55",
+   "51dd60fea8510b6c27acaa22bfee6924191fd3e2",
    "support"
   ],
   "worklets/README.md": [
@@ -457572,11 +457765,11 @@
    "testharness"
   ],
   "xhr/idlharness.any.sharedworker-expected.txt": [
-   "50788e8c4785fc44f06fad8e5541710990a0f94f",
+   "3405f42e3c73f48bbe52619a962cbd57bf56fa03",
    "support"
   ],
   "xhr/idlharness.any.worker-expected.txt": [
-   "50788e8c4785fc44f06fad8e5541710990a0f94f",
+   "3405f42e3c73f48bbe52619a962cbd57bf56fa03",
    "support"
   ],
   "xhr/loadstart-and-state.html": [
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/META.yml b/third_party/blink/web_tests/external/wpt/async-local-storage/META.yml
deleted file mode 100644
index 1bbe9e5ac..0000000
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/META.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-spec: https://domenic.github.io/async-local-storage/
-suggested_reviewers:
-  - domenic
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-large-with-auto.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-large-with-auto.html
new file mode 100644
index 0000000..2658e40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-image-large-with-auto.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>CSS Background Test: Large images with one auto size are correctly sized</title>
+  <link rel="author" title="schenney" href="mailto:schenney@chromium.org">
+  <link rel="help" href="http://www.w3.org/TR/css3-background">
+  <link rel="match" href="reference/background-image-large-with-auto-ref.html">
+  <style>
+    .wide-div {
+      background-image: url(support/green-1000x10.png);
+      background-repeat: no-repeat;
+      background-size: 10000px auto;
+      width: 1000px;
+      height: 100px;
+      border: 1px solid black;
+    }
+
+    .high-div {
+      background-image: url(support/green-10x1000.png);
+      background-repeat: no-repeat;
+      background-size: auto 10000px;
+      width: 100px;
+      height: 1000px;
+      border: 1px solid black;
+    }
+  </style>
+</head>
+
+<body>
+  <div class='wide-div'></div>
+  <div class='high-div'></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-large-with-auto-ref.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-large-with-auto-ref.html
new file mode 100644
index 0000000..7bad169
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/reference/background-image-large-with-auto-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>CSS Background Test Reference</title>
+  <link rel="author" title="schenney" href="mailto:schenney@chromium.org">
+  <style>
+    .wide-div {
+      background-image: url(../support/green-1000x10.png);
+      background-repeat: no-repeat;
+      background-size: 10000px 100px;
+      width: 1000px;
+      height: 100px;
+      border: 1px solid black;
+    }
+
+    .high-div {
+      background-image: url(../support/green-10x1000.png);
+      background-repeat: no-repeat;
+      background-size: 100px 10000px;
+      width: 100px;
+      height: 1000px;
+      border: 1px solid black;
+    }
+  </style>
+</head>
+
+<body>
+  <div class='wide-div'></div>
+  <div class='high-div'></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-1000x10.png b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-1000x10.png
new file mode 100644
index 0000000..8ce354c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-1000x10.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-10x1000.png b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-10x1000.png
new file mode 100644
index 0000000..c7fd6ee
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/support/green-10x1000.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001-ref.html
new file mode 100644
index 0000000..8b5f501
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<style>div::first-line { font-family:monospace; color:green; }</style>
+<p>Test passes if PASS is displayed in green below.</p>
+<div>PASS</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001.html b/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001.html
new file mode 100644
index 0000000..7df697f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/display-inline-dynamic-001.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-display/">
+<link rel="match" href="display-inline-dynamic-001-ref.html">
+<style>div::first-line { font-family:monospace; color:green; }</style>
+<p>Test passes if PASS is displayed in green below.</p>
+<div>P<span id="showme" style="display:none;">AS</span>S</div>
+<script>
+  document.body.offsetTop;
+  showme.style.display = "inline";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt
new file mode 100644
index 0000000..129e043
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS ::before in selected host is styled
+PASS ::after in selected host is styled
+FAIL ::placeholder in selected host is styled assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html
new file mode 100644
index 0000000..c11da7d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-tree-abiding.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Interaction with tree-abiding</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>
+      #c-e::part(before-p)::before { color: green; }
+      #c-e::part(after-p)::after { color: green; }
+      #c-e::part(placeholder-p)::placeholder { color: green; }
+    </style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>
+        #before-i::before { content: "this text"; color: red; }
+        #after-i::after { content: "this text"; color: red; }
+        #placeholder-i::placeholder { color: red; }
+      </style>
+      <div>
+        The following text should be green:
+        <span id="before-i" part="before-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <span id="after-i" part="after-p"></span>
+      </div>
+      <div>
+        The following text should be green:
+        <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
+      </div>
+    </template>
+    <custom-element id="c-e"></custom-element>
+    <script>
+      "use strict";
+      const colorGreen = "rgb(0, 128, 0)";
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "before-i"]);
+        assert_equals(window.getComputedStyle(el, '::before').color, colorGreen);
+      }, "::before in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "after-i"]);
+        assert_equals(window.getComputedStyle(el, '::after').color, colorGreen);
+      }, "::after in selected host is styled");
+      test(function() {
+        const el = getElementByShadowIds(document, ["c-e", "placeholder-i"]);
+        assert_equals(window.getComputedStyle(el, '::placeholder').color, colorGreen);
+      }, "::placeholder in selected host is styled");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/interfaces-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/interfaces-expected.txt
index 0211c26..caef6ac 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/interfaces-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/interfaces-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 438 tests; 405 PASS, 33 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 408 tests; 384 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Element: original interface defined
 PASS Partial interface CSSStyleRule: original interface defined
@@ -20,16 +20,11 @@
 PASS StylePropertyMapReadOnly interface: existence and properties of interface prototype object
 PASS StylePropertyMapReadOnly interface: existence and properties of interface prototype object's "constructor" property
 PASS StylePropertyMapReadOnly interface: existence and properties of interface prototype object's @@unscopables property
-PASS Testing Symbol.iterator property of iterable interface StylePropertyMapReadOnly
-PASS Testing pair iterable interface StylePropertyMapReadOnly
+PASS StylePropertyMapReadOnly interface: iterable<USVString, [object Object]>
 PASS StylePropertyMapReadOnly interface: operation get(USVString)
 PASS StylePropertyMapReadOnly interface: operation getAll(USVString)
 PASS StylePropertyMapReadOnly interface: operation has(USVString)
 PASS StylePropertyMapReadOnly interface: attribute size
-PASS StylePropertyMapReadOnly interface: operation entries()
-PASS StylePropertyMapReadOnly interface: operation keys()
-PASS StylePropertyMapReadOnly interface: operation values()
-PASS StylePropertyMapReadOnly interface: operation forEach(function, any)
 PASS StylePropertyMap interface: existence and properties of interface object
 PASS StylePropertyMap interface object length
 PASS StylePropertyMap interface object name
@@ -56,30 +51,14 @@
 PASS StylePropertyMapReadOnly interface: styleMap must inherit property "has(USVString)" with the proper type
 PASS StylePropertyMapReadOnly interface: calling has(USVString) on styleMap with too few arguments must throw TypeError
 PASS StylePropertyMapReadOnly interface: styleMap must inherit property "size" with the proper type
-PASS StylePropertyMapReadOnly interface: styleMap must inherit property "entries()" with the proper type
-PASS StylePropertyMapReadOnly interface: styleMap must inherit property "keys()" with the proper type
-PASS StylePropertyMapReadOnly interface: styleMap must inherit property "values()" with the proper type
-PASS StylePropertyMapReadOnly interface: styleMap must inherit property "forEach(function, any)" with the proper type
-PASS StylePropertyMapReadOnly interface: calling forEach(function, any) on styleMap with too few arguments must throw TypeError
 PASS CSSUnparsedValue interface: existence and properties of interface object
 PASS CSSUnparsedValue interface object length
 PASS CSSUnparsedValue interface object name
 PASS CSSUnparsedValue interface: existence and properties of interface prototype object
 PASS CSSUnparsedValue interface: existence and properties of interface prototype object's "constructor" property
 PASS CSSUnparsedValue interface: existence and properties of interface prototype object's @@unscopables property
-PASS Testing Symbol.iterator property of iterable interface CSSUnparsedValue
-PASS Testing value iterable interface CSSUnparsedValue
+PASS CSSUnparsedValue interface: iterable<CSSUnparsedSegment>
 PASS CSSUnparsedValue interface: attribute length
-FAIL CSSUnparsedValue interface: operation entries() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSUnparsedValue interface: operation keys() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSUnparsedValue interface: operation values() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-PASS CSSUnparsedValue interface: operation forEach(function, any)
 PASS CSSVariableReferenceValue interface: existence and properties of interface object
 PASS CSSVariableReferenceValue interface object length
 PASS CSSVariableReferenceValue interface object name
@@ -242,50 +221,23 @@
 PASS CSSNumericArray interface: existence and properties of interface prototype object
 PASS CSSNumericArray interface: existence and properties of interface prototype object's "constructor" property
 PASS CSSNumericArray interface: existence and properties of interface prototype object's @@unscopables property
-PASS Testing Symbol.iterator property of iterable interface CSSNumericArray
-PASS Testing value iterable interface CSSNumericArray
+PASS CSSNumericArray interface: iterable<CSSNumericValue>
 PASS CSSNumericArray interface: attribute length
-FAIL CSSNumericArray interface: operation entries() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSNumericArray interface: operation keys() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSNumericArray interface: operation values() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-PASS CSSNumericArray interface: operation forEach(function, any)
 PASS CSSTransformValue interface: existence and properties of interface object
 PASS CSSTransformValue interface object length
 PASS CSSTransformValue interface object name
 PASS CSSTransformValue interface: existence and properties of interface prototype object
 PASS CSSTransformValue interface: existence and properties of interface prototype object's "constructor" property
 PASS CSSTransformValue interface: existence and properties of interface prototype object's @@unscopables property
-PASS Testing Symbol.iterator property of iterable interface CSSTransformValue
-PASS Testing value iterable interface CSSTransformValue
+PASS CSSTransformValue interface: iterable<CSSTransformComponent>
 PASS CSSTransformValue interface: attribute length
 PASS CSSTransformValue interface: attribute is2D
 PASS CSSTransformValue interface: operation toMatrix()
-FAIL CSSTransformValue interface: operation entries() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSTransformValue interface: operation keys() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-FAIL CSSTransformValue interface: operation values() assert_throws: calling operation with this = {} didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
-PASS CSSTransformValue interface: operation forEach(function, any)
 PASS CSSTransformValue must be primary interface of transformValue
 PASS Stringification of transformValue
 PASS CSSTransformValue interface: transformValue must inherit property "length" with the proper type
 PASS CSSTransformValue interface: transformValue must inherit property "is2D" with the proper type
 PASS CSSTransformValue interface: transformValue must inherit property "toMatrix()" with the proper type
-PASS CSSTransformValue interface: transformValue must inherit property "entries()" with the proper type
-PASS CSSTransformValue interface: transformValue must inherit property "keys()" with the proper type
-PASS CSSTransformValue interface: transformValue must inherit property "values()" with the proper type
-PASS CSSTransformValue interface: transformValue must inherit property "forEach(function, any)" with the proper type
-PASS CSSTransformValue interface: calling forEach(function, any) on transformValue with too few arguments must throw TypeError
 PASS CSSStyleValue interface: transformValue must inherit property "parse(USVString, USVString)" with the proper type
 FAIL CSSStyleValue interface: calling parse(USVString, USVString) on transformValue with too few arguments must throw TypeError assert_own_property: interface object must have static operation as own property expected property "parse" missing
 PASS CSSStyleValue interface: transformValue must inherit property "parseAll(USVString, USVString)" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/dom/interfaces_exclude=Node-expected.txt b/third_party/blink/web_tests/external/wpt/dom/interfaces_exclude=Node-expected.txt
index 41fe5fad..e5d1218 100644
--- a/third_party/blink/web_tests/external/wpt/dom/interfaces_exclude=Node-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/dom/interfaces_exclude=Node-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1102 tests; 1078 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1104 tests; 1080 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Test driver
 PASS Partial interface Window: original interface defined
 PASS Event interface: existence and properties of interface object
@@ -179,6 +179,7 @@
 PASS NodeList interface: existence and properties of interface prototype object's @@unscopables property
 PASS NodeList interface: operation item(unsigned long)
 PASS NodeList interface: attribute length
+PASS NodeList interface: iterable<Node>
 PASS NodeList must be primary interface of document.querySelectorAll("script")
 PASS Stringification of document.querySelectorAll("script")
 PASS NodeList interface: document.querySelectorAll("script") must inherit property "item(unsigned long)" with the proper type
@@ -1083,6 +1084,7 @@
 PASS DOMTokenList interface: operation supports(DOMString)
 PASS DOMTokenList interface: attribute value
 PASS DOMTokenList interface: stringifier
+PASS DOMTokenList interface: iterable<DOMString>
 PASS DOMTokenList must be primary interface of document.body.classList
 PASS Stringification of document.body.classList
 PASS DOMTokenList interface: document.body.classList must inherit property "length" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any-expected.txt
index b013c5c..8bf325d7 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 139 tests; 133 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 129 tests; 123 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
 PASS Headers interface: existence and properties of interface object
@@ -13,12 +13,7 @@
 PASS Headers interface: operation get(ByteString)
 PASS Headers interface: operation has(ByteString)
 PASS Headers interface: operation set(ByteString, ByteString)
-PASS Testing Symbol.iterator property of iterable interface Headers
-PASS Testing pair iterable interface Headers
-PASS Headers interface: operation entries()
-PASS Headers interface: operation keys()
-PASS Headers interface: operation values()
-PASS Headers interface: operation forEach(function, any)
+PASS Headers interface: iterable<ByteString, ByteString>
 PASS Headers must be primary interface of new Headers()
 PASS Stringification of new Headers()
 PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
@@ -31,11 +26,6 @@
 PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
 PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
 PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "entries()" with the proper type
-PASS Headers interface: new Headers() must inherit property "keys()" with the proper type
-PASS Headers interface: new Headers() must inherit property "values()" with the proper type
-PASS Headers interface: new Headers() must inherit property "forEach(function, any)" with the proper type
-PASS Headers interface: calling forEach(function, any) on new Headers() with too few arguments must throw TypeError
 PASS Request interface: existence and properties of interface object
 PASS Request interface object length
 PASS Request interface object name
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.serviceworker-expected.txt
index 7687f61..600ca72 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.serviceworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.serviceworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 139 tests; 130 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 129 tests; 120 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
 PASS Headers interface: existence and properties of interface object
@@ -13,12 +13,7 @@
 PASS Headers interface: operation get(ByteString)
 PASS Headers interface: operation has(ByteString)
 PASS Headers interface: operation set(ByteString, ByteString)
-PASS Testing Symbol.iterator property of iterable interface Headers
-PASS Testing pair iterable interface Headers
-PASS Headers interface: operation entries()
-PASS Headers interface: operation keys()
-PASS Headers interface: operation values()
-PASS Headers interface: operation forEach(function, any)
+PASS Headers interface: iterable<ByteString, ByteString>
 PASS Headers must be primary interface of new Headers()
 PASS Stringification of new Headers()
 PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
@@ -31,11 +26,6 @@
 PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
 PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
 PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "entries()" with the proper type
-PASS Headers interface: new Headers() must inherit property "keys()" with the proper type
-PASS Headers interface: new Headers() must inherit property "values()" with the proper type
-PASS Headers interface: new Headers() must inherit property "forEach(function, any)" with the proper type
-PASS Headers interface: calling forEach(function, any) on new Headers() with too few arguments must throw TypeError
 PASS Request interface: existence and properties of interface object
 PASS Request interface object length
 PASS Request interface object name
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.sharedworker-expected.txt
index 76f58a4..7f9da6e 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.sharedworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.sharedworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 139 tests; 133 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 129 tests; 123 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
 PASS Headers interface: existence and properties of interface object
@@ -13,12 +13,7 @@
 PASS Headers interface: operation get(ByteString)
 PASS Headers interface: operation has(ByteString)
 PASS Headers interface: operation set(ByteString, ByteString)
-PASS Testing Symbol.iterator property of iterable interface Headers
-PASS Testing pair iterable interface Headers
-PASS Headers interface: operation entries()
-PASS Headers interface: operation keys()
-PASS Headers interface: operation values()
-PASS Headers interface: operation forEach(function, any)
+PASS Headers interface: iterable<ByteString, ByteString>
 PASS Headers must be primary interface of new Headers()
 PASS Stringification of new Headers()
 PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
@@ -31,11 +26,6 @@
 PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
 PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
 PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "entries()" with the proper type
-PASS Headers interface: new Headers() must inherit property "keys()" with the proper type
-PASS Headers interface: new Headers() must inherit property "values()" with the proper type
-PASS Headers interface: new Headers() must inherit property "forEach(function, any)" with the proper type
-PASS Headers interface: calling forEach(function, any) on new Headers() with too few arguments must throw TypeError
 PASS Request interface: existence and properties of interface object
 PASS Request interface object length
 PASS Request interface object name
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.worker-expected.txt
index 76f58a4..7f9da6e 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/idl.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 139 tests; 133 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 129 tests; 123 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
 PASS Headers interface: existence and properties of interface object
@@ -13,12 +13,7 @@
 PASS Headers interface: operation get(ByteString)
 PASS Headers interface: operation has(ByteString)
 PASS Headers interface: operation set(ByteString, ByteString)
-PASS Testing Symbol.iterator property of iterable interface Headers
-PASS Testing pair iterable interface Headers
-PASS Headers interface: operation entries()
-PASS Headers interface: operation keys()
-PASS Headers interface: operation values()
-PASS Headers interface: operation forEach(function, any)
+PASS Headers interface: iterable<ByteString, ByteString>
 PASS Headers must be primary interface of new Headers()
 PASS Stringification of new Headers()
 PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
@@ -31,11 +26,6 @@
 PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
 PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
 PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "entries()" with the proper type
-PASS Headers interface: new Headers() must inherit property "keys()" with the proper type
-PASS Headers interface: new Headers() must inherit property "values()" with the proper type
-PASS Headers interface: new Headers() must inherit property "forEach(function, any)" with the proper type
-PASS Headers interface: calling forEach(function, any) on new Headers() with too few arguments must throw TypeError
 PASS Request interface: existence and properties of interface object
 PASS Request interface object length
 PASS Request interface object name
diff --git "a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt" "b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
index 87ecff0..8f45c85 100644
--- "a/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
+++ "b/third_party/blink/web_tests/external/wpt/html/dom/interfaces.https_exclude=\050Document_Window_HTML._\051-expected.txt"
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1134 tests; 1115 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1130 tests; 1111 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface NavigatorID: original interface defined
@@ -1130,9 +1130,5 @@
 PASS Stringification of window.external
 PASS External interface: window.external must inherit property "AddSearchProvider()" with the proper type
 PASS External interface: window.external must inherit property "IsSearchProviderInstalled()" with the proper type
-PASS FormData interface: operation entries()
-PASS FormData interface: operation keys()
-PASS FormData interface: operation values()
-PASS FormData interface: operation forEach(function, any)
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html b/third_party/blink/web_tests/external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html
new file mode 100644
index 0000000..73a3ff66
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-event-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: tabindex - focus, click</title>
+<link rel="author" title="Intel" href="www.intel.com/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute">
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<h2>Test steps</h2>
+<p>Focus on the button below by "Tab" key, then press "Enter" key</p>
+
+<p><button type="button">Test tabIndex</button></p>
+
+<script>
+
+setup({explicit_done: true});
+setup({explicit_timeout: true});
+
+let button = document.querySelector("button");
+let focused = false;
+
+on_event(button, "focus", () => {
+  focused = !focused;
+});
+
+on_event(button, "click", () => {
+  test(() => {
+    assert_true(focused, "Focus on the button by Tab key");
+  }, "Check if click event will be fired when press the 'enter' key while the element is focused");
+  done();
+});
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/dom.idl b/third_party/blink/web_tests/external/wpt/interfaces/dom.idl
index 503c391..1788faae 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/dom.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/dom.idl
@@ -1,7 +1,7 @@
 // GENERATED CONTENT - DO NOT EDIT
-// Content of this file was automatically extracted from the
-// "DOM Standard" spec.
-// See: https://dom.spec.whatwg.org/
+// Content was automatically extracted by Reffy into reffy-reports
+// (https://github.com/tidoust/reffy-reports)
+// Source: DOM Standard (https://dom.spec.whatwg.org/)
 
 [Constructor(DOMString type, optional EventInit eventInitDict),
  Exposed=(Window,Worker,AudioWorklet)]
@@ -145,7 +145,7 @@
 interface NodeList {
   getter Node? item(unsigned long index);
   readonly attribute unsigned long length;
-//  iterable<Node>;
+  iterable<Node>;
 };
 
 [Exposed=Window, LegacyUnenumerableNamedProperties]
@@ -548,5 +548,5 @@
   [CEReactions] boolean replace(DOMString token, DOMString newToken);
   boolean supports(DOMString token);
   [CEReactions] stringifier attribute DOMString value;
-  //  iterable<DOMString>;
+  iterable<DOMString>;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
index 723b063..da8a0acd 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
@@ -22,7 +22,8 @@
     void clearMeasures(optional DOMString measureName);
 };
 
-[Exposed=(Window,Worker)]
+[Exposed=(Window,Worker),
+ Constructor(DOMString markName, optional PerformanceMarkOptions markOptions)]
 interface PerformanceMark : PerformanceEntry {
   readonly attribute any detail;
 };
diff --git a/third_party/blink/web_tests/external/wpt/kv-storage/META.yml b/third_party/blink/web_tests/external/wpt/kv-storage/META.yml
new file mode 100644
index 0000000..bf4a1e6e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/META.yml
@@ -0,0 +1,3 @@
+spec: https://wicg.github.io/kv-storage/
+suggested_reviewers:
+  - domenic
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html b/third_party/blink/web_tests/external/wpt/kv-storage/api-surface.https.html
similarity index 91%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/api-surface.https.html
index 927871a..65452f5 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/api-surface.https.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage API surface</title>
+<title>KV Storage: API surface</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <script type="module">
-import { storage, StorageArea } from "std:async-local-storage";
+import { storage, StorageArea } from "std:kv-storage";
 import * as classAssert from "./helpers/class-assert.js";
-import { testWithArea } from "./helpers/als-tests.js";
+import { testWithArea } from "./helpers/kvs-tests.js";
 
 test(() => {
   classAssert.isConstructor(StorageArea);
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/class-assert.js b/third_party/blink/web_tests/external/wpt/kv-storage/helpers/class-assert.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/helpers/class-assert.js
rename to third_party/blink/web_tests/external/wpt/kv-storage/helpers/class-assert.js
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/equality-asserters.js b/third_party/blink/web_tests/external/wpt/kv-storage/helpers/equality-asserters.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/helpers/equality-asserters.js
rename to third_party/blink/web_tests/external/wpt/kv-storage/helpers/equality-asserters.js
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js b/third_party/blink/web_tests/external/wpt/kv-storage/helpers/kvs-tests.js
similarity index 96%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js
rename to third_party/blink/web_tests/external/wpt/kv-storage/helpers/kvs-tests.js
index 28087abf..0ffe71fa 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/helpers/kvs-tests.js
@@ -1,4 +1,4 @@
-import { StorageArea, storage as defaultArea } from "std:async-local-storage";
+import { StorageArea, storage as defaultArea } from "std:kv-storage";
 import { assertArrayCustomEquals } from "./equality-asserters.js";
 
 export function testWithArea(testFn, description) {
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html b/third_party/blink/web_tests/external/wpt/kv-storage/key-types.https.html
similarity index 93%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/key-types.https.html
index c3985b7..0dc93025 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/key-types.https.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage: tests against various key types</title>
+<title>KV Storage: tests against various key types</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <script type="module">
-import { testWithArea, testVariousMethods } from "./helpers/als-tests.js";
+import { testWithArea, testVariousMethods } from "./helpers/kvs-tests.js";
 import { assertEqualDates, assertEqualArrayBuffers, assertArrayBufferEqualsABView }
   from "./helpers/equality-asserters.js";
 
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-dynamic-import.tentative.html b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-dynamic-import.html
similarity index 63%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-dynamic-import.tentative.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-dynamic-import.html
index 9270f6c8..6ccbf84 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-dynamic-import.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-dynamic-import.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage: should not work in non-secure contexts when included via import()</title>
+<title>KV Storage: should not work in non-secure contexts when included via import()</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -13,6 +13,6 @@
 }, "Prerequisite check");
 
 promise_test(t => {
-  return promise_rejects(t, "SecurityError", import("std:async-local-storage"));
+  return promise_rejects(t, "SecurityError", import("std:kv-storage"));
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-import-statement.tentative.html b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-import-statement.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-import-statement.tentative.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-import-statement.html
index 8797296..fda02aad 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-import-statement.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-import-statement.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage: should not work in non-secure contexts when included via an import statement</title>
+<title>KV Storage: should not work in non-secure contexts when included via an import statement</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -23,5 +23,5 @@
 </script>
 
 <script type="module">
-import "std:async-local-storage";
+import "std:kv-storage";
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-script-element.tentative.html b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-script-element.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-script-element.tentative.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-script-element.html
index feeddaf..66802b1 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/non-secure-context-script-element.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/non-secure-context-script-element.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage: should not work in non-secure contexts when included via a script element</title>
+<title>KV Storage: should not work in non-secure contexts when included via a script element</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -22,4 +22,4 @@
 });
 </script>
 
-<script type="module" src="std:async-local-storage"></script>
+<script type="module" src="std:kv-storage"></script>
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/storage-smoke-test.tentative.https.html b/third_party/blink/web_tests/external/wpt/kv-storage/storage-smoke-test.https.html
similarity index 76%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/storage-smoke-test.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/storage-smoke-test.https.html
index f978480..df6fd8c 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/storage-smoke-test.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/storage-smoke-test.https.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage storage export smoke test</title>
+<title>KV storage: storage export smoke test</title>
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <script type="module">
-import { testVariousMethodsWithDefaultArea } from "./helpers/als-tests.js";
-import { storage } from "std:async-local-storage";
+import { testVariousMethodsWithDefaultArea } from "./helpers/kvs-tests.js";
+import { storage } from "std:kv-storage";
 
 test(() => {
   const { backingStore } = storage;
@@ -17,7 +17,7 @@
   assert_own_property(backingStore, "version");
   assert_equals(Object.getPrototypeOf(backingStore), Object.prototype);
 
-  assert_equals(backingStore.database, "async-local-storage:default");
+  assert_equals(backingStore.database, "kv-storage:default");
   assert_equals(backingStore.store, "store");
   assert_equals(backingStore.version, 1);
 }, "backingStore returns the correct object");
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html b/third_party/blink/web_tests/external/wpt/kv-storage/undefined-value.https.html
similarity index 81%
rename from third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html
rename to third_party/blink/web_tests/external/wpt/kv-storage/undefined-value.https.html
index c76c32f..89da5d5 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html
+++ b/third_party/blink/web_tests/external/wpt/kv-storage/undefined-value.https.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>Async local storage: undefined keys</title>
-<!-- https://github.com/domenic/async-local-storage/commit/5bf31109f37d1371f619ea33d0e2391f10e8b8f5 -->
+<title>KV Storage: undefined values</title>
+<!-- https://github.com/wicg/kv-storage/commit/5bf31109f37d1371f619ea33d0e2391f10e8b8f5 -->
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
 <script type="module">
-import { StorageArea } from "std:async-local-storage";
-import { testWithArea } from "./helpers/als-tests.js";
+import { StorageArea } from "std:kv-storage";
+import { testWithArea } from "./helpers/kvs-tests.js";
 
 testWithArea(async (area) => {
   assert_equals(await area.get("key"), undefined);
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html
new file mode 100644
index 0000000..39a88836
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+<head>
+    <title>MediaRecorder Pause and Resume</title>
+    <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<canvas id="canvas" width="200" height="200">
+</canvas>
+<script>
+    function createVideoStream() {
+        let canvas = document.getElementById("canvas");
+        canvas.getContext('2d');
+        return canvas.captureStream();
+    }
+
+    function recordEvents(target, events) {
+        let arr = [];
+        for (let ev of events) {
+            target.addEventListener(ev, _ => arr.push(ev));
+        }
+        return arr;
+    }
+
+    promise_test(async () => {
+        let video = createVideoStream();
+        let recorder = new MediaRecorder(video);
+        let events = recordEvents(recorder,
+            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
+
+        recorder.start();
+        assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
+
+        recorder.pause();
+        assert_equals(recorder.state, "paused", "MediaRecorder should be paused immediately following pause()");
+
+        // A second call to pause should be idempotent
+        recorder.pause();
+        assert_equals(recorder.state, "paused", "MediaRecorder should be paused immediately following pause()");
+
+        let event = await new Promise(r => recorder.onpause = r);
+        assert_equals(event.type, "pause", "the event type should be pause");
+        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
+
+        recorder.resume();
+        assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following resume() call");
+
+        // A second call to resume should be idempotent
+        recorder.resume();
+        assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following resume() call");
+
+        event = await new Promise(r => recorder.onresume = r);
+        assert_equals(event.type, "resume", "the event type should be resume");
+        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
+
+        recorder.stop();
+        await new Promise(r => recorder.onstop = r);
+
+        assert_array_equals(events, ["start", "pause", "resume", "dataavailable", "stop"],
+            "Should have gotten expected events");
+    }, "MediaRecorder handles pause() and resume() calls appropriately in state and events");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
index 8e05fc54..5dd9aa6 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
@@ -16,35 +16,68 @@
         return canvas.captureStream();
     }
 
-    async_test(t => {
+    function recordEvents(target, events) {
+        let arr = [];
+        for (let ev of events) {
+            target.addEventListener(ev, _ => arr.push(ev));
+        }
+        return arr;
+    }
+
+    promise_test(async t => {
         let video = createVideoStream();
         let recorder = new MediaRecorder(video);
-        recorder.onstop = t.step_func(errorEvent => {
-            assert_equals(errorEvent.type, 'stop', 'the error type should be stop');
-            assert_true(errorEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
-            assert_equals(recorder.state, "inactive", "MediaRecorder has been stopped when all tracks are ended");
-            t.done();
-        });
+        let events = recordEvents(recorder,
+            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
         assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track");
         recorder.start();
         assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
+
         video.getVideoTracks()[0].stop();
+        assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following last track ending");
+        let event = await new Promise(r => recorder.onstop = r);
+
+        assert_equals(event.type, "stop", "the event type should be stop");
+        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
+        assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
+
+        assert_array_equals(events, ["start", "dataavailable", "stop"],
+            "Should have gotten expected events");
+
+        recorder.stop();
+        await Promise.race([
+            new Promise((_, reject) => recorder.onstop =
+                _ => reject(new Error("stop() is idempotent"))),
+            new Promise(r => t.step_timeout(r, 0))
+        ]);
     }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended");
 
-    async_test(t => {
+    promise_test(async t => {
         let video = createVideoStream();
         let recorder = new MediaRecorder(video);
-        recorder.onstop = t.step_func(errorEvent => {
-            assert_equals(errorEvent.type, 'stop', 'the error type should be stop');
-            assert_true(errorEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
-            assert_equals(recorder.state, "inactive", "MediaRecorder has been stopped when stop() is called");
-            t.done();
-        });
+        let events = recordEvents(recorder,
+            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
         recorder.start();
         assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
+
         recorder.stop();
-        assert_equals(recorder.state, "recording", "State should remain the same until stop event is fired");
+        assert_equals(recorder.state, "inactive", "MediaRecorder state should be inactive immediately following stop() call");
+
+        let event = await new Promise (r => recorder.onstop = r);
+        assert_equals(event.type, "stop", "the event type should be stop");
+        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
+        assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
+
+        assert_array_equals(events, ["start", "dataavailable", "stop"],
+            "Should have gotten expected events");
+
+        recorder.stop();
+        await Promise.race([
+            new Promise((_, reject) => recorder.onstop =
+                _ => reject(new Error("stop() is idempotent"))),
+            new Promise(r => t.step_timeout(r, 0))
+        ]);
     }, "MediaRecorder will stop recording and fire a stop event when stop() is called");
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
index 0c8d31ef..0b65efbd 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idlharness
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
@@ -56,7 +56,7 @@
 PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
 PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
index 0c8d31ef..0b65efbd 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idlharness
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
@@ -56,7 +56,7 @@
 PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
 PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
index 0c8d31ef..0b65efbd 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idlharness
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
@@ -56,7 +56,7 @@
 PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
 PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
index 0c8d31ef..0b65efbd 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idlharness
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
@@ -56,7 +56,7 @@
 PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
 PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/portals/portal-activate-event.html b/third_party/blink/web_tests/external/wpt/portals/portal-activate-event.html
index ed56026..ac1505d 100644
--- a/third_party/blink/web_tests/external/wpt/portals/portal-activate-event.html
+++ b/third_party/blink/web_tests/external/wpt/portals/portal-activate-event.html
@@ -4,11 +4,29 @@
 <script src="/resources/testharnessreport.js"></script>
 <script>
   async_test(function(t) {
-    var bc = new BroadcastChannel("test");
+    var bc = new BroadcastChannel("test-eventlistener");
     bc.onmessage = t.step_func_done(function(e) {
       assert_equals(e.data, "passed");
       bc.close();
     });
-    window.open("resources/portal-activate-event-window.html");
+    window.open("resources/portal-activate-event-window.html?test=eventlistener");
   }, "Tests that the PortalActivateEvent is dispatched when a portal is activated.");
+
+  async_test(function(t) {
+    var bc = new BroadcastChannel("test-eventhandler");
+    bc.onmessage = t.step_func_done(function(e) {
+      assert_equals(e.data, "passed");
+      bc.close();
+    });
+    window.open("resources/portal-activate-event-window.html?test=eventhandler");
+  }, "Tests that the portalactivate event handler is dispatched when a portal is activated.");
+
+  async_test(function(t) {
+    var bc = new BroadcastChannel("test-bodyeventhandler");
+    bc.onmessage = t.step_func_done(function(e) {
+      assert_equals(e.data, "passed");
+      bc.close();
+    });
+    window.open("resources/portal-activate-event-window.html?test=bodyeventhandler");
+  }, "Tests that the HTMLBodyElement has the portalactivate event handler.");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-portal.html b/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-portal.html
index d0eebcd1..b2759c3 100644
--- a/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-portal.html
+++ b/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-portal.html
@@ -1,13 +1,23 @@
 <!DOCTYPE html>
 <title>Tests that the PortalActivateEvent is dispatched when a portal is activated</title>
 <script>
-  window.addEventListener("portalactivate", function(e) {
-    var bc = new BroadcastChannel("test");
+  var test = (new URL(location)).searchParams.get("test");
+
+  function portalActivate(e) {
+    var bc = new BroadcastChannel("test-" + test);
     bc.postMessage("passed");
     bc.close();
-  });
+  }
 
-  var bc = new BroadcastChannel("portal");
+  if (test == "bodyeventhandler") {
+    document.write('<body onportalactivate="portalActivate()"></body>');
+  } else if (test == "eventhandler") {
+    window.onportalactivate = portalActivate;
+  } else if (test == "eventlistener") {
+    window.addEventListener("portalactivate", portalActivate);
+  }
+
+  var bc = new BroadcastChannel("portal-" + test);
   bc.postMessage("loaded");
   bc.close();
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-window.html b/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-window.html
index 1994dc3..cf09caeb 100644
--- a/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-window.html
+++ b/third_party/blink/web_tests/external/wpt/portals/resources/portal-activate-event-window.html
@@ -1,11 +1,17 @@
 <!DOCTYPE html>
 <script>
-  var bc = new BroadcastChannel("portal");
-  bc.onmessage = function(e) {
-    document.querySelector("portal").activate();
-    bc.close();
+  window.onload = function(e) {
+    var test = (new URL(location)).searchParams.get("test");
+    var portal = document.createElement("portal");
+    portal.src = "portal-activate-event-portal.html" + location.search;
+    document.body.appendChild(portal);
+
+    var bc = new BroadcastChannel("portal-" + test);
+    bc.onmessage = function(e) {
+      document.querySelector("portal").activate();
+      bc.close();
+    }
   }
 </script>
 <body>
-  <portal src="portal-activate-event-portal.html"></portal>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/OWNERS b/third_party/blink/web_tests/external/wpt/preload/OWNERS
index 46f12faf7..39fb049e 100644
--- a/third_party/blink/web_tests/external/wpt/preload/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/preload/OWNERS
@@ -1,4 +1,4 @@
 # TEAM: loading-dev@chromium.org
 # COMPONENT: Blink>Loader>Preload
 igrigorik@chromium.org
-yoav@yoav.ws
+yoavweiss@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
index 2db6eef..51b22248 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
@@ -9,8 +9,8 @@
 <script nonce="abc">
     window.addEventListener('load', t.step_func(function() {
         verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("resources/dummy.js?without-nonce", 0);
-        verifyNumberOfDownloads("resources/dummy.js?with-nonce", 1);
+        verifyNumberOfDownloads("resources/dummy.js?from-header&without-nonce", 0);
+        verifyNumberOfDownloads("resources/dummy.js?from-header&with-nonce", 1);
         t.done();
     }));
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers
index a3c41d2..a54b6937 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers
@@ -1,3 +1,3 @@
 Content-Security-Policy: script-src 'nonce-abc'
-Link: </preload/resources/dummy.js?without-nonce>;rel=preload;as=script
-Link: </preload/resources/dummy.js?with-nonce>;rel=preload;as=script;nonce=abc
+Link: </preload/resources/dummy.js?from-header&without-nonce>;rel=preload;as=script
+Link: </preload/resources/dummy.js?from-header&with-nonce>;rel=preload;as=script;nonce=abc
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
index 70d34e5..9eb8ac4e 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
@@ -11,16 +11,16 @@
 <script>
     window.addEventListener("load", t.step_func(function() {
         verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads('resources/square.png?1x', 1);
-        verifyNumberOfDownloads('resources/square.png?2x', 0);
-        verifyNumberOfDownloads('resources/square.png?3x', 0);
-        verifyNumberOfDownloads('resources/square.png?base', 0);
-        verifyNumberOfDownloads('resources/square.png?200', 0);
-        verifyNumberOfDownloads('resources/square.png?400', 1);
-        verifyNumberOfDownloads('resources/square.png?800', 0);
-        verifyNumberOfDownloads('resources/square.png?150', 0);
-        verifyNumberOfDownloads('resources/square.png?300', 1);
-        verifyNumberOfDownloads('resources/square.png?600', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&1x', 1);
+        verifyNumberOfDownloads('resources/square.png?from-header&2x', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&3x', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&base', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&200', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&400', 1);
+        verifyNumberOfDownloads('resources/square.png?from-header&800', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&150', 0);
+        verifyNumberOfDownloads('resources/square.png?from-header&300', 1);
+        verifyNumberOfDownloads('resources/square.png?from-header&600', 0);
         t.done();
     }));
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html.headers b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html.headers
index 1e4e9b8..906de0c 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html.headers
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html.headers
@@ -1,3 +1,3 @@
-Link: <resources/square.png?1x>; rel=preload; as=image; imagesrcset="resources/square.png?2x 2x, resources/square.png?3x 3x"
-Link: <resources/square.png?base>; rel=preload; as=image; imagesrcset="resources/square.png?200 200w, resources/square.png?400 400w, resources/square.png?800 800w"; imagesizes=400px
-Link: <resources/square.png?base>; rel=preload; as=image; imagesrcset="resources/square.png?150 150w, resources/square.png?300 300w, resources/square.png?600 600w"; imagesizes="(min-width: 300px) 300px, 150px"
+Link: <resources/square.png?from-header&1x>; rel=preload; as=image; imagesrcset="resources/square.png?from-header&2x 2x, resources/square.png?from-header&3x 3x"
+Link: <resources/square.png?from-header&base>; rel=preload; as=image; imagesrcset="resources/square.png?from-header&200 200w, resources/square.png?from-header&400 400w, resources/square.png?from-header&800 800w"; imagesizes=400px
+Link: <resources/square.png?from-header&base>; rel=preload; as=image; imagesrcset="resources/square.png?from-header&150 150w, resources/square.png?from-header&300 300w, resources/square.png?from-header&600 600w"; imagesizes="(min-width: 300px) 300px, 150px"
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource_redirects.html b/third_party/blink/web_tests/external/wpt/resource-timing/redirects.sub.html
similarity index 64%
rename from third_party/blink/web_tests/external/wpt/resource-timing/resource_redirects.html
rename to third_party/blink/web_tests/external/wpt/resource-timing/redirects.sub.html
index 606662af..0e3f405e 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resource_redirects.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/redirects.sub.html
@@ -11,16 +11,22 @@
 <script src="resources/webperftestharnessextension.js"></script>
 <script>
 let iframe;
+const redirect_url = 'common/redirect.py';
+const url_prefix = redirect_url + '?location=/resource-timing/resources/';
+const https_url_prefix = redirect_url + '?location=https://{{hosts[][www]}}:{{ports[https][0]}}/resource-timing/resources/';
 function setup_iframe() {
     const iframe_content =
-        '<link rel="stylesheet" href="/common/redirect.py?location=/resource-timing/resources/resource_timing_test0.css"></link>' +
-        '<img src="/common/redirect.py?location=/resource-timing/resources/blue.png"></img>' +
-        '<iframe src="/common/redirect.py?location=/resource-timing/resources/blank_page_green.htm"></iframe>' +
-        '<script src="/common/redirect.py?location=/resource-timing/resources/empty_script.js"></scr' + 'ipt>' +
+        '<link rel="stylesheet" href="/' + url_prefix + 'resource_timing_test0.css"></link>' +
+        '<img src="/' + url_prefix + 'blue.png"></img>' +
+        '<iframe src="/' + url_prefix + 'blank_page_green.htm"></iframe>' +
+        '<script src="/' + url_prefix + 'empty_script.js"></scr' + 'ipt>' +
         '<scr' + 'ipt>' +
         'const xhr = new XMLHttpRequest;' +
-        'xhr.open("GET", "/common/redirect.py?location=/resource-timing/resources/blank_page_green.htm?id=xhr", false);' +
+        'xhr.open("GET", "/' + url_prefix + 'blank_page_green.htm?id=xhr", false);' +
         'xhr.send();' +
+        'const xhr2 = new XMLHttpRequest;' +
+        'xhr2.open("GET", "/' + https_url_prefix + 'blank_page_green.htm?id=xhr", false);' +
+        'xhr2.send();' +
         '</scr' + 'ipt>';
     iframe = document.getElementById('frameContext');
     iframe.contentWindow.document.write(iframe_content);
@@ -30,14 +36,15 @@
     const entries = context.getEntriesByType('resource');
 
     const index = window.location.pathname.lastIndexOf('resource-timing');
-    const pathname = window.location.pathname.substring(0, index) +
-        'common/redirect.py?location=/resource-timing/resources/';
+    const pathname = window.location.pathname.substring(0, index) + url_prefix;
+    const https_pathname = window.location.pathname.substring(0, index) + https_url_prefix;
     let expected_entries = {};
     expected_entries[pathname + 'resource_timing_test0.css'] = 'link';
     expected_entries[pathname + 'blue.png'] = 'img';
     expected_entries[pathname + 'blank_page_green.htm'] = 'iframe';
     expected_entries[pathname + 'empty_script.js'] = 'script';
     expected_entries[pathname + 'blank_page_green.htm?id=xhr'] = 'xmlhttprequest';
+    expected_entries[https_pathname + 'blank_page_green.htm?id=xhr'] = 'xmlhttprequest';
 
     test_resource_entries(entries, expected_entries);
 }
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/blank_page_green.htm.headers b/third_party/blink/web_tests/external/wpt/resource-timing/resources/blank_page_green.htm.headers
new file mode 100644
index 0000000..cb762eff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/blank_page_green.htm.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/third_party/blink/web_tests/external/wpt/resources/idlharness.js b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
index 926a615..8b7a1e2 100644
--- a/third_party/blink/web_tests/external/wpt/resources/idlharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
@@ -2309,21 +2309,6 @@
     }
 }
 
-IdlInterface.prototype.add_iterable_members = function(member)
-{
-    this.members.push(new IdlInterfaceMember(
-        { type: "operation", name: "entries", idlType: "iterator", arguments: []}));
-    this.members.push(new IdlInterfaceMember(
-        { type: "operation", name: "keys", idlType: "iterator", arguments: []}));
-    this.members.push(new IdlInterfaceMember(
-        { type: "operation", name: "values", idlType: "iterator", arguments: []}));
-    this.members.push(new IdlInterfaceMember(
-        { type: "operation", name: "forEach", idlType: "void",
-          arguments:
-          [{ name: "callback", idlType: {idlType: "function"}},
-           { name: "thisValue", idlType: {idlType: "any"}, optional: true}]}));
-};
-
 IdlInterface.prototype.test_to_json_operation = function(memberHolderObject, member) {
     var instanceName = memberHolderObject && memberHolderObject.constructor.name
         || member.name + " object";
@@ -2351,27 +2336,29 @@
 
 IdlInterface.prototype.test_member_iterable = function(member)
 {
-    var isPairIterator = member.idlType.length === 2;
     subsetTestByKey(this.name, test, function()
     {
-        var descriptor = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.iterator);
-        assert_true(descriptor.writable, "property should be writable");
-        assert_true(descriptor.configurable, "property should be configurable");
-        assert_false(descriptor.enumerable, "property should not be enumerable");
-        assert_equals(this.get_interface_object().prototype[Symbol.iterator].name, isPairIterator ? "entries" : "values", "@@iterator function does not have the right name");
-    }.bind(this), "Testing Symbol.iterator property of iterable interface " + this.name);
+        var isPairIterator = member.idlType.length === 2;
+        var proto = this.get_interface_object().prototype;
+        var descriptor = Object.getOwnPropertyDescriptor(proto, Symbol.iterator);
 
-    if (isPairIterator) {
-        subsetTestByKey(this.name, test, function() {
-            assert_equals(this.get_interface_object().prototype[Symbol.iterator], this.get_interface_object().prototype["entries"], "entries method is not the same as @@iterator");
-        }.bind(this), "Testing pair iterable interface " + this.name);
-    } else {
-        subsetTestByKey(this.name, test, function() {
-            ["entries", "keys", "values", "forEach", Symbol.Iterator].forEach(function(property) {
-                assert_equals(this.get_interface_object().prototype[property], Array.prototype[property], property + " function is not the same as Array one");
+        assert_true(descriptor.writable, "@@iterator property should be writable");
+        assert_true(descriptor.configurable, "@@iterator property should be configurable");
+        assert_false(descriptor.enumerable, "@@iterator property should not be enumerable");
+        assert_equals(typeof descriptor.value, "function", "@@iterator property should be a function");
+        assert_equals(descriptor.value.length, 0, "@@iterator function object length should be 0");
+        assert_equals(descriptor.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name");
+
+        if (isPairIterator) {
+            assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method");
+        } else {
+            assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's");
+            ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(function(property) {
+                var propertyName = property === Symbol.iterator ? "@@iterator" : property;
+                assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's");
             }.bind(this));
-        }.bind(this), "Testing value iterable interface " + this.name);
-    }
+        }
+    }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">");
 };
 
 IdlInterface.prototype.test_member_stringifier = function(member)
@@ -2440,19 +2427,6 @@
     for (var i = 0; i < this.members.length; i++)
     {
         var member = this.members[i];
-        switch (member.type) {
-        case "iterable":
-            this.add_iterable_members(member);
-            break;
-        // TODO: add setlike and maplike handling.
-        default:
-            break;
-        }
-    }
-
-    for (var i = 0; i < this.members.length; i++)
-    {
-        var member = this.members[i];
         if (member.untested) {
             continue;
         }
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-iframe.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-iframe.html
new file mode 100644
index 0000000..4c57bbb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-iframe.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>iframe for xhr tests</title>
+<script>
+async function xhr(url, options) {
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest();
+    const opts = options ? options : {};
+    xhr.onload = () => {
+      resolve(xhr);
+    };
+    xhr.onerror = () => {
+      reject('xhr failed');
+    };
+
+    xhr.open('GET', url);
+    if (opts.responseType) {
+      xhr.responseType = opts.responseType;
+    }
+    xhr.send();
+  });
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-response-url-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-response-url-worker.js
new file mode 100644
index 0000000..906ad50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/xhr-response-url-worker.js
@@ -0,0 +1,32 @@
+// Service worker for the xhr-response-url test.
+
+self.addEventListener('fetch', event => {
+  const url = new URL(event.request.url);
+  const respondWith = url.searchParams.get('respondWith');
+  if (!respondWith)
+    return;
+
+  if (respondWith == 'fetch') {
+    const target = url.searchParams.get('url');
+    event.respondWith(fetch(target));
+    return;
+  }
+
+  if (respondWith == 'string') {
+    const headers = {'content-type': 'text/plain'};
+    event.respondWith(new Response('hello', {headers}));
+    return;
+  }
+
+  if (respondWith == 'document') {
+    const doc = `
+        <!DOCTYPE html>
+        <html>
+        <title>hi</title>
+        <body>hello</body>
+        </html>`;
+    const headers = {'content-type': 'text/html'};
+    event.respondWith(new Response(doc, {headers}));
+    return;
+  }
+});
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/xhr-response-url.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/xhr-response-url.https.html
new file mode 100644
index 0000000..1ad2ce38
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/xhr-response-url.https.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Service Worker: XHR responseURL uses the response url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+const scope = 'resources/xhr-iframe.html';
+const script = 'resources/xhr-response-url-worker.js';
+let iframe;
+
+function build_url(options) {
+  const url = new URL('test', window.location);
+  const opts = options ? options : {};
+  if (opts.respondWith)
+    url.searchParams.set('respondWith', opts.respondWith);
+  if (opts.url)
+    url.searchParams.set('url', opts.url.href);
+  return url.href;
+}
+
+promise_test(async (t) => {
+  const registration =
+      await service_worker_unregister_and_register(t, script, scope);
+  await wait_for_state(t, registration.installing, 'activated');
+  iframe = await with_iframe(scope);
+}, 'global setup');
+
+// Test that XMLHttpRequest.responseURL uses the response URL from the service
+// worker.
+promise_test(async (t) => {
+  // Build a URL that tells the service worker to respondWith(fetch(|target|)).
+  const target = new URL('resources/dummy.txt', window.location);
+  const url = build_url({
+    respondWith: 'fetch',
+    url: target
+  });
+
+  // Perform the XHR.
+  const xhr = await iframe.contentWindow.xhr(url);
+  assert_equals(xhr.responseURL, target.href, 'responseURL');
+}, 'XHR responseURL should be the response URL');
+
+// Same as above with a generated response.
+promise_test(async (t) => {
+  // Build a URL that tells the service worker to respondWith(new Response()).
+  const url = build_url({respondWith: 'string'});
+
+  // Perform the XHR.
+  const xhr = await iframe.contentWindow.xhr(url);
+  assert_equals(xhr.responseURL, url, 'responseURL');
+}, 'XHR responseURL should be the response URL (generated response)');
+
+// Test that XMLHttpRequest.responseXML is a Document whose URL is the
+// response URL from the service worker.
+promise_test(async (t) => {
+  // Build a URL that tells the service worker to respondWith(fetch(|target|)).
+  const target = new URL('resources/blank.html', window.location);
+  const url = build_url({
+    respondWith: 'fetch',
+    url: target
+  });
+
+  // Perform the XHR.
+  const xhr = await iframe.contentWindow.xhr(url, {responseType: 'document'});
+  assert_equals(xhr.responseURL, target.href, 'responseURL');
+
+  // The document's URL uses the response URL:
+  // "Set |document|’s URL to |response|’s url."
+  // https://xhr.spec.whatwg.org/#document-response
+  assert_equals(xhr.responseXML.URL, target.href, 'responseXML.URL');
+
+  // The document's base URL falls back to the document URL:
+  // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#document-base-url
+  assert_equals(xhr.responseXML.baseURI, target.href, 'responseXML.baseURI');
+}, 'XHR Document should use the response URL');
+
+// Same as above with a generated response from the service worker.
+promise_test(async (t) => {
+  // Build a URL that tells the service worker to
+  // respondWith(new Response()) with a document response.
+  const url = build_url({respondWith: 'document'});
+
+  // Perform the XHR.
+  const xhr = await iframe.contentWindow.xhr(url, {responseType: 'document'});
+  assert_equals(xhr.responseURL, url, 'responseURL');
+
+  // The document's URL uses the response URL, which is the request URL:
+  // "Set |document|’s URL to |response|’s url."
+  // https://xhr.spec.whatwg.org/#document-response
+  assert_equals(xhr.responseXML.URL, url, 'responseXML.URL');
+
+  // The document's base URL falls back to the document URL:
+  // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#document-base-url
+  assert_equals(xhr.responseXML.baseURI, url, 'responseXML.baseURI');
+}, 'XHR Document should use the response URL (generated response)');
+
+promise_test(async (t) => {
+  if (iframe)
+    iframe.remove();
+  await service_worker_unregister(t, scope);
+}, 'global cleanup');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
index c208e73a..f264c054 100755
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
@@ -1,5 +1,6 @@
 #!/bin/sh
 
+sxg_version=1b3
 certfile=127.0.0.1.sxg.pem
 keyfile=127.0.0.1.sxg.key
 inner_url_origin=https://127.0.0.1:8444
@@ -25,7 +26,7 @@
 
 # A valid Signed Exchange.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
   -status 200 \
   -content sxg-location.html \
@@ -40,7 +41,7 @@
 
 # For check-cert-request.tentative.html
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
   -status 200 \
   -content sxg-location.html \
@@ -53,25 +54,9 @@
   -o sxg/check-cert-request.sxg \
   -miRecordSize 100
 
-# Request method is HEAD.
-gen-signedexchange \
-  -version 1b2 \
-  -method HEAD \
-  -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
-  -status 200 \
-  -content sxg-location.html \
-  -certificate $certfile \
-  -certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
-  -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
-  -privateKey $keyfile \
-  -date 2018-04-01T00:00:00Z \
-  -expire 168h \
-  -o sxg/sxg-head-request.sxg \
-  -miRecordSize 100
-
 # validityUrl is different origin from request URL.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
   -status 200 \
   -content failure.html \
@@ -86,7 +71,7 @@
 
 # certUrl is 404 and fallback URL is another signed exchange.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri $inner_url_origin/signed-exchange/resources/sxg/sxg-location.sxg \
   -status 200 \
   -content failure.html \
@@ -101,7 +86,7 @@
 
 # Nested signed exchange.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri "$inner_url_origin/signed-exchange/resources/inner-url.html?fallback-from-nested-sxg" \
   -status 200 \
   -content sxg/sxg-location.sxg \
@@ -117,7 +102,7 @@
 
 # Fallback URL has non-ASCII UTF-8 characters.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -ignoreErrors \
   -uri "$inner_url_origin/signed-exchange/resources/🌐📦.html" \
   -status 200 \
@@ -133,7 +118,7 @@
 
 # Fallback URL has invalid UTF-8 sequence.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -ignoreErrors \
   -uri "$inner_url_origin/signed-exchange/resources/$(echo -e '\xce\xce\xa9').html" \
   -status 200 \
@@ -149,7 +134,7 @@
 
 # Fallback URL has UTF-8 BOM.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -ignoreErrors \
   -uri "$(echo -e '\xef\xbb\xbf')$inner_url_origin/signed-exchange/resources/inner-url.html" \
   -status 200 \
@@ -165,7 +150,7 @@
 
 # Response has Cache-Control: no-store header.
 gen-signedexchange \
-  -version 1b2 \
+  -version $sxg_version \
   -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
   -status 200 \
   -responseHeader "Cache-Control: no-store" \
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/__dir__.headers b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/__dir__.headers
index ca411784..83a3c12 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/__dir__.headers
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/__dir__.headers
@@ -1,2 +1,2 @@
-Content-Type: application/signed-exchange;v=b2
+Content-Type: application/signed-exchange;v=b3
 X-Content-Type-Options: nosniff
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/check-cert-request.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/check-cert-request.sxg
index 05f1646..d412f463 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/check-cert-request.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/check-cert-request.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/fallback-to-another-sxg.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/fallback-to-another-sxg.sxg
index 1af86c04..784b44a 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/fallback-to-another-sxg.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/fallback-to-another-sxg.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/nested-sxg.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/nested-sxg.sxg
index 2970e2df..c4eaf77 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/nested-sxg.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/nested-sxg.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-head-request.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-head-request.sxg
deleted file mode 100644
index 9d096474..0000000
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-head-request.sxg
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-inner-url-bom.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-inner-url-bom.sxg
index ace5abd..88e617d5 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-inner-url-bom.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-inner-url-bom.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-utf8-inner-url.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-utf8-inner-url.sxg
index 1f697b74..d045d5b 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-utf8-inner-url.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-utf8-inner-url.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-validity-url.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-validity-url.sxg
index 3266e7e..628f6cc 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-validity-url.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-invalid-validity-url.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-location.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-location.sxg
index a818541..3540bfe 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-location.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-location.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-noncacheable.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-noncacheable.sxg
index 38be1ee..38aa4598 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-noncacheable.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-noncacheable.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-utf8-inner-url.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-utf8-inner-url.sxg
index c8f8a94e..914a8458 100644
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-utf8-inner-url.sxg
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-utf8-inner-url.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-head-request.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-head-request.tentative.html
deleted file mode 100644
index ad65a589..0000000
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-head-request.tentative.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<title>Loading SignedHTTPExchange with HEAD request method must fail</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-<script src="./resources/sxg-util.js"></script>
-<body>
-<script>
-promise_test(async (t) => {
-  const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-head-request.sxg';
-  const message = await openSXGInIframeAndWaitForMessage(t, sxgUrl);
-  assert_equals(message.location, innerURLOrigin() + '/signed-exchange/resources/inner-url.html');
-  assert_true(message.is_fallback);
-}, 'Loading SignedHTTPExchange with HEAD request method must fail');
-
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt
index 4fc30f1..0990fcc 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt
@@ -2,7 +2,7 @@
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
index 4fc30f1..0990fcc 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
@@ -2,7 +2,7 @@
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
index 4fc30f1..0990fcc 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
@@ -2,7 +2,7 @@
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
index 4fc30f1..0990fcc 100644
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
@@ -2,7 +2,7 @@
 PASS idl_test setup
 PASS Partial interface Performance: original interface defined
 PASS PerformanceMark interface: existence and properties of interface object
-PASS PerformanceMark interface object length
+FAIL PerformanceMark interface object length assert_equals: wrong value for PerformanceMark.length expected 1 but got 0
 PASS PerformanceMark interface object name
 PASS PerformanceMark interface: existence and properties of interface prototype object
 PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html
new file mode 100644
index 0000000..b5555b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html
@@ -0,0 +1,161 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test AudioParam events very close in time</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+
+  <body>
+    <script>
+      const audit = Audit.createTaskRunner();
+
+      // Largest sample rate that is required to be supported and is a power of
+      // two, to eliminate round-off as much as possible.
+      const sampleRate = 65536;
+
+      // Only need one render quantum for testing.
+      const testFrames = 128;
+
+      // Largest representable single-float number
+      const floatMax = Math.fround(3.4028234663852886e38);
+
+      // epspos is the smallest x such that 1 + x != 1
+      const epspos = 1.1102230246251568e-16;
+      // epsneg is the smallest x such that 1 - x != 1
+      const epsneg = 5.551115123125784e-17;
+
+      audit.define(
+          {label: 'no-nan', description: 'NaN does not occur'},
+          (task, should) => {
+            const context = new OfflineAudioContext({
+              numberOfChannels: 1,
+              sampleRate: sampleRate,
+              length: testFrames
+            });
+
+            const src0 = new ConstantSourceNode(context, {offset: 0});
+
+            // This should always succeed.  We just want to print out a message
+            // that |src0| is a constant source node for the following
+            // processing.
+            should(src0, 'src0 = new ConstantSourceNode(context, {offset: 0})')
+                .beEqualTo(src0);
+
+            src0.connect(context.destination);
+
+            // Values for the first event (setValue).  |time1| MUST be 0.
+            const time1 = 0;
+            const value1 = 10;
+
+            // Values for the second event (linearRamp).  |value2| must be huge,
+            // and |time2| must be small enough that 1/|time2| overflows a
+            // single float. This value is the least positive single float.
+            const value2 = floatMax;
+            const time2 = 1.401298464324817e-45;
+
+            // These should always succeed; the messages are just informational
+            // to show the events that we scheduled.
+            should(
+                src0.offset.setValueAtTime(value1, time1),
+                `src0.offset.setValueAtTime(${value1}, ${time1})`)
+                .beEqualTo(src0.offset);
+            should(
+                src0.offset.linearRampToValueAtTime(value2, time2),
+                `src0.offset.linearRampToValueAtTime(${value2}, ${time2})`)
+                .beEqualTo(src0.offset);
+
+            src0.start();
+
+            context.startRendering()
+                .then(buffer => {
+                  const output = buffer.getChannelData(0);
+
+                  // Since time1 = 0, the output at frame 0 MUST be value1.
+                  should(output[0], 'output[0]').beEqualTo(value1);
+
+                  // Since time2 < 1, output from frame 1 and later must be a
+                  // constant.
+                  should(output.slice(1), 'output[1]')
+                      .beConstantValueOf(value2);
+                })
+                .then(() => task.done());
+          });
+
+      audit.define(
+          {label: 'interpolation', description: 'Interpolation of linear ramp'},
+          (task, should) => {
+            const context = new OfflineAudioContext({
+              numberOfChannels: 1,
+              sampleRate: sampleRate,
+              length: testFrames
+            });
+
+            const src1 = new ConstantSourceNode(context, {offset: 0});
+
+            // This should always succeed.  We just want to print out a message
+            // that |src1| is a constant source node for the following
+            // processing.
+            should(src1, 'src1 = new ConstantSourceNode(context, {offset: 0})')
+                .beEqualTo(src1);
+
+            src1.connect(context.destination);
+
+            const frame = 1;
+
+            // These time values are arranged so that time1 < frame/sampleRate <
+            // time2.  This means we need to interpolate to get a value at given
+            // frame.
+            //
+            // The values are not so important, but |value2| should be huge.
+            const time1 = frame * (1 - epsneg) / context.sampleRate;
+            const value1 = 1e15;
+
+            const time2 = frame * (1 + epspos) / context.sampleRate;
+            const value2 = floatMax;
+
+            should(
+                src1.offset.setValueAtTime(value1, time1),
+                `src1.offset.setValueAtTime(${value1}, ${time1})`)
+                .beEqualTo(src1.offset);
+            should(
+                src1.offset.linearRampToValueAtTime(value2, time2),
+                `src1.offset.linearRampToValueAtTime(${value2}, ${time2})`)
+                .beEqualTo(src1.offset);
+
+            src1.start();
+
+            context.startRendering()
+                .then(buffer => {
+                  const output = buffer.getChannelData(0);
+
+                  // Sanity check
+                  should(time2 - time1, 'Event time difference')
+                      .notBeEqualTo(0);
+
+                  // Because 0 < time1 < 1, output must be 0 at time 0.
+                  should(output[0], 'output[0]').beEqualTo(0);
+
+                  // Because time1 < 1/sampleRate < time2, we need to
+                  // interpolate the value between these times to determine the
+                  // output at frame 1.
+                  const t = frame / context.sampleRate;
+                  const v = value1 +
+                      (value2 - value1) * (t - time1) / (time2 - time1);
+
+                  should(output[1], 'output[1]').beCloseTo(v, {threshold: 0});
+
+                  // Because 1 < time2 < 2, the output at frame 2 and higher is
+                  // constant.
+                  should(output.slice(2), 'output[2:]')
+                      .beConstantValueOf(value2);
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/workers/OWNERS b/third_party/blink/web_tests/external/wpt/workers/OWNERS
index f62e415..51dd60fe 100644
--- a/third_party/blink/web_tests/external/wpt/workers/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/workers/OWNERS
@@ -1,3 +1,2 @@
 # TEAM: worker-dev@chromium.org
 # COMPONENT: Blink>Workers
-worker-dev@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/worklets/OWNERS b/third_party/blink/web_tests/external/wpt/worklets/OWNERS
index f62e415..51dd60fe 100644
--- a/third_party/blink/web_tests/external/wpt/worklets/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/worklets/OWNERS
@@ -1,3 +1,2 @@
 # TEAM: worker-dev@chromium.org
 # COMPONENT: Blink>Workers
-worker-dev@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.sharedworker-expected.txt
index 50788e8..3405f42 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.sharedworker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.sharedworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 164 tests; 162 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 154 tests; 152 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS XMLHttpRequestEventTarget interface: existence and properties of interface object
 PASS XMLHttpRequestEventTarget interface object length
@@ -119,12 +119,7 @@
 PASS FormData interface: operation has(USVString)
 PASS FormData interface: operation set(USVString, USVString)
 PASS FormData interface: operation set(USVString, Blob, USVString)
-PASS Testing Symbol.iterator property of iterable interface FormData
-PASS Testing pair iterable interface FormData
-PASS FormData interface: operation entries()
-PASS FormData interface: operation keys()
-PASS FormData interface: operation values()
-PASS FormData interface: operation forEach(function, any)
+PASS FormData interface: iterable<USVString, FormDataEntryValue>
 PASS FormData must be primary interface of new FormData()
 PASS Stringification of new FormData()
 PASS FormData interface: new FormData() must inherit property "append(USVString, USVString)" with the proper type
@@ -143,11 +138,6 @@
 PASS FormData interface: calling set(USVString, USVString) on new FormData() with too few arguments must throw TypeError
 PASS FormData interface: new FormData() must inherit property "set(USVString, Blob, USVString)" with the proper type
 PASS FormData interface: calling set(USVString, Blob, USVString) on new FormData() with too few arguments must throw TypeError
-PASS FormData interface: new FormData() must inherit property "entries()" with the proper type
-PASS FormData interface: new FormData() must inherit property "keys()" with the proper type
-PASS FormData interface: new FormData() must inherit property "values()" with the proper type
-PASS FormData interface: new FormData() must inherit property "forEach(function, any)" with the proper type
-PASS FormData interface: calling forEach(function, any) on new FormData() with too few arguments must throw TypeError
 PASS ProgressEvent interface: existence and properties of interface object
 PASS ProgressEvent interface object length
 PASS ProgressEvent interface object name
diff --git a/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.worker-expected.txt
index 50788e8..3405f42 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/xhr/idlharness.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 164 tests; 162 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 154 tests; 152 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS XMLHttpRequestEventTarget interface: existence and properties of interface object
 PASS XMLHttpRequestEventTarget interface object length
@@ -119,12 +119,7 @@
 PASS FormData interface: operation has(USVString)
 PASS FormData interface: operation set(USVString, USVString)
 PASS FormData interface: operation set(USVString, Blob, USVString)
-PASS Testing Symbol.iterator property of iterable interface FormData
-PASS Testing pair iterable interface FormData
-PASS FormData interface: operation entries()
-PASS FormData interface: operation keys()
-PASS FormData interface: operation values()
-PASS FormData interface: operation forEach(function, any)
+PASS FormData interface: iterable<USVString, FormDataEntryValue>
 PASS FormData must be primary interface of new FormData()
 PASS Stringification of new FormData()
 PASS FormData interface: new FormData() must inherit property "append(USVString, USVString)" with the proper type
@@ -143,11 +138,6 @@
 PASS FormData interface: calling set(USVString, USVString) on new FormData() with too few arguments must throw TypeError
 PASS FormData interface: new FormData() must inherit property "set(USVString, Blob, USVString)" with the proper type
 PASS FormData interface: calling set(USVString, Blob, USVString) on new FormData() with too few arguments must throw TypeError
-PASS FormData interface: new FormData() must inherit property "entries()" with the proper type
-PASS FormData interface: new FormData() must inherit property "keys()" with the proper type
-PASS FormData interface: new FormData() must inherit property "values()" with the proper type
-PASS FormData interface: new FormData() must inherit property "forEach(function, any)" with the proper type
-PASS FormData interface: calling forEach(function, any) on new FormData() with too few arguments must throw TypeError
 PASS ProgressEvent interface: existence and properties of interface object
 PASS ProgressEvent interface object length
 PASS ProgressEvent interface object name
diff --git a/third_party/blink/web_tests/fast/css/font-face-character-fallback.html b/third_party/blink/web_tests/fast/css/font-face-character-fallback.html
index 35103066..018cd78 100644
--- a/third_party/blink/web_tests/fast/css/font-face-character-fallback.html
+++ b/third_party/blink/web_tests/fast/css/font-face-character-fallback.html
@@ -5,13 +5,13 @@
   font-family: 'test';
   font-weight: 400;
   unicode-range: U+0020, U+0041-005A;  /* space, A-Z */
-  src: local('Helvetica'), local('Arial');
+  src: local('Helvetica'), local('Arial'), local('Arimo-Regular');
 }
 @font-face {
   font-family: 'test';
   font-weight: 700;
   unicode-range: U+0061-007A;  /* a-z */
-  src: local('Times'), local('Times New Roman');
+  src: local('Times Roman'), local('Times New Roman'), local('Tinos-Regular');
 }
 span {
   font-family: 'test', 'Courier', 'Courier New';
diff --git a/third_party/blink/web_tests/fast/css/font-face-descending-unicode-range.html b/third_party/blink/web_tests/fast/css/font-face-descending-unicode-range.html
index b63f8dc..60b9cbd 100644
--- a/third_party/blink/web_tests/fast/css/font-face-descending-unicode-range.html
+++ b/third_party/blink/web_tests/fast/css/font-face-descending-unicode-range.html
@@ -5,7 +5,7 @@
 <style>
 @font-face {
     font-family: 'myfont';
-    src: local(Courier), local('Courier New'); /* Use monospace font */
+    src: local(Courier), local('Courier New'), local('Cousine-Regular'); /* Use monospace font */
     unicode-range: U+062-60;
 }
 </style>
diff --git a/third_party/blink/web_tests/fast/css/font-face-download-error.html b/third_party/blink/web_tests/fast/css/font-face-download-error.html
index a0e5fac..a66ce54b1 100644
--- a/third_party/blink/web_tests/fast/css/font-face-download-error.html
+++ b/third_party/blink/web_tests/fast/css/font-face-download-error.html
@@ -11,7 +11,7 @@
 /* Test 0: Download success */
 @font-face {
     font-family:myfont_0;
-    src: local('Courier'), local('Courier New');
+    src: local('Courier'), local('Courier New'), local('Cousine-Regular');
 }
 @font-face {
     font-family:myfont_0;
@@ -22,7 +22,7 @@
 /* Test 1: Download error */
 @font-face {
     font-family:myfont_1;
-    src: local('Courier'), local('Courier New');
+    src: local('Courier'), local('Courier New'), local('Cousine-Regular');
 }
 @font-face {
     font-family:myfont_1;
@@ -33,7 +33,7 @@
 /* Test 2: Download error followed by success */
 @font-face {
     font-family:myfont_2;
-    src: local('Courier'), local('Courier New');
+    src: local('Courier'), local('Courier New'), local('Cousine-Regular');
 }
 @font-face {
     font-family:myfont_2;
@@ -44,11 +44,11 @@
 /* Test 3: Download error followed by existing local font */
 @font-face {
     font-family:myfont_3;
-    src: local('Courier'), local('Courier New');
+    src: local('Courier'), local('Courier New'), local('Cousine-Regular');
 }
 @font-face {
     font-family:myfont_3;
-    src: url('resources/DownLoadErrorAhem.otf'), local(Arial);
+    src: url('resources/DownLoadErrorAhem.otf'), local(Arial), local('Arimo-Regular');
     unicode-range: u+69; /* 'i' */
 }
 
@@ -127,7 +127,7 @@
 // Using a timer is not ideal, but there seems to be no better options.
 function runTest()
 {
-    window.setTimeout(test, 200);
+    document.fonts.ready.then(test);
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/css/font-face-in-media-rule.html b/third_party/blink/web_tests/fast/css/font-face-in-media-rule.html
index 3b17c49..7c8d86ee 100644
--- a/third_party/blink/web_tests/fast/css/font-face-in-media-rule.html
+++ b/third_party/blink/web_tests/fast/css/font-face-in-media-rule.html
@@ -5,7 +5,7 @@
     @media screen {
       @font-face {
           font-family: Times;
-          src: local("Arial");
+          src: local("Arial"), local('Arimo-Regular');
       }
     }
   
diff --git a/third_party/blink/web_tests/fast/css/font-face-multiple-ranges-for-unicode-range.html b/third_party/blink/web_tests/fast/css/font-face-multiple-ranges-for-unicode-range.html
index 428f5db..8527fc8 100644
--- a/third_party/blink/web_tests/fast/css/font-face-multiple-ranges-for-unicode-range.html
+++ b/third_party/blink/web_tests/fast/css/font-face-multiple-ranges-for-unicode-range.html
@@ -10,77 +10,77 @@
 /* Test 0: Comma-separated list, which is valid. */
 @font-face {
     font-family:myfont_0;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_0;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69, u+6a; /* 'i' and 'j' */
 }
 
 /* Test 1: Comma-separated list with three elements, which is valid. */
 @font-face {
     font-family:myfont_1;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_1;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69 , u+6a ,u+6c; /* 'i', 'j', and 'l' */
 }
 
 /* Test 2: Comma-separated list with two consecutive commas, which is invalid. */
 @font-face {
     font-family:myfont_2;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_2;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69, , u+6a; /* 'i' and 'j' */
 }
 
 /* Test 3: Comma-separated list with a trailing comma, which is invalid. */
 @font-face {
     font-family:myfont_3;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_3;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69, u+6a,; /* 'i' and 'j' */
 }
 
 /* Test 4: Comma-separated list with a leading comma, which is invalid. */
 @font-face {
     font-family:myfont_4;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_4;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: , u+69, u+6a; /* 'i' and 'j' */
 }
 
 /* Test 5: Space-separated list, which is invalid. */
 @font-face {
     font-family:myfont_5;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_5;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69 u+6a ; /* 'i' and 'j' */
 }
 
 /* Test 6: Slash-separated list, which is invalid. */
 @font-face {
     font-family:myfont_6;
-    src: local('Times New Roman'), local('Times');
+    src: local('Times New Roman'), local('Times'), local('Tinos-Regular');
 }
 @font-face {
     font-family:myfont_6;
-    src: local('Courier New'), local('Courier');
+    src: local('Courier New'), local('Courier'), local('Cousine-Regular');
     unicode-range: u+69/u+6a; /* 'i' and 'j' */
 }
 
diff --git a/third_party/blink/web_tests/fast/css/font-face-unicode-range-monospace.html b/third_party/blink/web_tests/fast/css/font-face-unicode-range-monospace.html
index 933eb30..3904338 100644
--- a/third_party/blink/web_tests/fast/css/font-face-unicode-range-monospace.html
+++ b/third_party/blink/web_tests/fast/css/font-face-unicode-range-monospace.html
@@ -3,13 +3,13 @@
 <style>
 @font-face {
   font-family: font1;
-  src: local(Courier), local('Courier New'); /* Use monospace font */
+  src: local(Courier), local('Courier New'), local('Cousine-Regular'); /* Use monospace font */
   unicode-range: U+0041; /* 'A' */
 }
 
 @font-face {
   font-family: font2;
-  src: local(Arial);
+  src: local(Arial), local('Arimo-Regular');
   unicode-range: U+006d; /* 'm' */
 }
 
diff --git a/third_party/blink/web_tests/fast/css/fontfaceset-add-remove.html b/third_party/blink/web_tests/fast/css/fontfaceset-add-remove.html
index 63d74f3..feb6bc9 100644
--- a/third_party/blink/web_tests/fast/css/fontfaceset-add-remove.html
+++ b/third_party/blink/web_tests/fast/css/fontfaceset-add-remove.html
@@ -4,7 +4,7 @@
 <style>
 @font-face {
     font-family: TestFont;
-    src: local(Arial);
+    src: local(Arial), local(Arimo-Regular);
 }
 @font-face {
     font-family: Ahem;
diff --git a/third_party/blink/web_tests/fast/css/fontfaceset-download-error.html b/third_party/blink/web_tests/fast/css/fontfaceset-download-error.html
index d27d0f4..f31bda0 100644
--- a/third_party/blink/web_tests/fast/css/fontfaceset-download-error.html
+++ b/third_party/blink/web_tests/fast/css/fontfaceset-download-error.html
@@ -23,7 +23,7 @@
 /* Test 4: Download error followed by existing local font */
 @font-face {
     font-family: myfont4;
-    src: url('resources/DownLoadErrorAhem.otf'), local('Courier New');
+    src: url('resources/DownLoadErrorAhem.otf'), local('Courier New'), local('Cousine-Regular');
 }
 
 /* Test 5: Multiple errors */
diff --git a/third_party/blink/web_tests/fast/css/fontfaceset-multiple-faces.html b/third_party/blink/web_tests/fast/css/fontfaceset-multiple-faces.html
index 387b531..f4e8bf8 100644
--- a/third_party/blink/web_tests/fast/css/fontfaceset-multiple-faces.html
+++ b/third_party/blink/web_tests/fast/css/fontfaceset-multiple-faces.html
@@ -4,7 +4,7 @@
 <style>
 @font-face {
     font-family: TestFont;
-    src: local('Courier New');
+    src: local('Courier New'), local('Cousine-Regular');
 }
 
 @font-face {
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index d855c314..88c7be3 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -146,6 +146,7 @@
 PASS oldChildWindow.onpointerrawmove is newChildWindow.onpointerrawmove
 PASS oldChildWindow.onpointerup is newChildWindow.onpointerup
 PASS oldChildWindow.onpopstate is newChildWindow.onpopstate
+PASS oldChildWindow.onportalactivate is newChildWindow.onportalactivate
 PASS oldChildWindow.onprogress is newChildWindow.onprogress
 PASS oldChildWindow.onratechange is newChildWindow.onratechange
 PASS oldChildWindow.onrejectionhandled is newChildWindow.onrejectionhandled
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 4ac84a4..4accc946 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -111,6 +111,7 @@
 PASS childWindow.onpointerrawmove is null
 PASS childWindow.onpointerup is null
 PASS childWindow.onpopstate is null
+PASS childWindow.onportalactivate is null
 PASS childWindow.onprogress is null
 PASS childWindow.onratechange is null
 PASS childWindow.onrejectionhandled is null
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index 6d55bba..929d985 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -111,6 +111,7 @@
 PASS childWindow.onpointerrawmove is null
 PASS childWindow.onpointerup is null
 PASS childWindow.onpopstate is null
+PASS childWindow.onportalactivate is null
 PASS childWindow.onprogress is null
 PASS childWindow.onratechange is null
 PASS childWindow.onrejectionhandled is null
diff --git a/third_party/blink/web_tests/fast/forms/select-popup/popup-menu-font-face.html b/third_party/blink/web_tests/fast/forms/select-popup/popup-menu-font-face.html
index fbeee1c5..cecdf18 100644
--- a/third_party/blink/web_tests/fast/forms/select-popup/popup-menu-font-face.html
+++ b/third_party/blink/web_tests/fast/forms/select-popup/popup-menu-font-face.html
@@ -13,8 +13,8 @@
   <option class="font2">font 2</option>
 </select>
 <script>
-var face1 = new FontFace("TestFont", "local(Arial)");
-var face2 = new FontFace("TestFont 2", "local(Arial)");
+var face1 = new FontFace("TestFont", "local(Arial), local(Arimo-Regular)");
+var face2 = new FontFace("TestFont 2", "local(Arial), local(Arimo-Regular)");
 document.fonts.add(face1);
 document.fonts.add(face2);
 
diff --git a/third_party/blink/web_tests/fast/inline/vertical-align-with-fallback-fonts.html b/third_party/blink/web_tests/fast/inline/vertical-align-with-fallback-fonts.html
index 47fb973..c13bd64 100644
--- a/third_party/blink/web_tests/fast/inline/vertical-align-with-fallback-fonts.html
+++ b/third_party/blink/web_tests/fast/inline/vertical-align-with-fallback-fonts.html
@@ -2,7 +2,7 @@
 <style>
 @font-face {
     font-family:T;
-    src:local('Times New Roman');
+    src:local('Times New Roman'), local('Tinos-Regular');
     unicode-range:U+41;
 }
 div {
diff --git a/third_party/blink/web_tests/fast/text/capitalize-boundaries.html b/third_party/blink/web_tests/fast/text/capitalize-boundaries.html
index bf4a55d7f..a9d5105 100644
--- a/third_party/blink/web_tests/fast/text/capitalize-boundaries.html
+++ b/third_party/blink/web_tests/fast/text/capitalize-boundaries.html
Binary files differ
diff --git a/third_party/blink/web_tests/fast/text/unique-vs-family-match.html b/third_party/blink/web_tests/fast/text/unique-vs-family-match.html
new file mode 100644
index 0000000..ea0266e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/text/unique-vs-family-match.html
@@ -0,0 +1,13 @@
+<style type="text/css">
+    @font-face {
+        font-family: test;
+        src: local('Times');
+    }
+    * {
+    font-family: Times;
+    }
+</style>
+<!-- Test for https://bugs.chromium.org/p/chromium/issues/detail?id=921029
+     checks that local unique matches must not mask family matches in font cache. -->
+<div style="font-family: test;">Prime the cache.</div>
+Test passes if this appears in Times.
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
index c12bb1e1..1f6a315 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
@@ -4,6 +4,23 @@
 {
     0 : {
         commandIndex : 0
+        method : "drawPaint"
+        params : {
+            paint : {
+                blendMode : "Src"
+                color : "#0"
+                filterLevel : "None"
+                flags : "none"
+                strokeCap : "Butt"
+                strokeJoin : "Miter"
+                strokeMiter : 4
+                strokeWidth : 0
+                styleName : "Fill"
+            }
+        }
+    }
+    1 : {
+        commandIndex : 1
         method : "drawRect"
         params : {
             paint : {
@@ -24,8 +41,8 @@
             }
         }
     }
-    1 : {
-        commandIndex : 1
+    2 : {
+        commandIndex : 2
         method : "drawRect"
         params : {
             paint : {
@@ -46,8 +63,8 @@
             }
         }
     }
-    2 : {
-        commandIndex : 2
+    3 : {
+        commandIndex : 3
         method : "drawImageRect"
         params : {
             dst : {
@@ -79,21 +96,21 @@
             }
         }
     }
-    3 : {
-        commandIndex : 3
+    4 : {
+        commandIndex : 4
         method : "save"
         params : undefined
     }
-    4 : {
-        commandIndex : 4
+    5 : {
+        commandIndex : 5
         method : "translate"
         params : {
             dx : 0
             dy : 71
         }
     }
-    5 : {
-        commandIndex : 5
+    6 : {
+        commandIndex : 6
         method : "clipRect"
         params : {
             SkRegion::Op : "kIntersect_Op"
@@ -106,8 +123,8 @@
             softClipEdgeStyle : true
         }
     }
-    6 : {
-        commandIndex : 6
+    7 : {
+        commandIndex : 7
         method : "drawRect"
         params : {
             paint : {
@@ -128,8 +145,8 @@
             }
         }
     }
-    7 : {
-        commandIndex : 7
+    8 : {
+        commandIndex : 8
         method : "restore"
         params : undefined
     }
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layer-tree-model-expected.txt b/third_party/blink/web_tests/http/tests/devtools/layers/layer-tree-model-expected.txt
index 530c1610..defb215 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layer-tree-model-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layer-tree-model-expected.txt
@@ -1,32 +1,47 @@
 Tests general layer tree model functionality
 Initial layer tree
-#document (6)
-    iframe#frame (7)
-        #document 200x200 (8)
-            div#subframe1 80x80 (9)
-    div#a 200x200 (7)
-        div#b1 100x150 (8)
-        div#b2 110x140 (8)
-            div#c 90x100 (9)
-        div#b3 110x140 (8)
+<invalid node id> 0x0 (0)
+    <invalid node id> (1)
+    <invalid node id> 0x0 (1)
+    <invalid node id> (1)
+    #document (1)
+    #document (1)
+    iframe#frame (1)
+    #document 200x200 (1)
+    div#subframe1 80x80 (1)
+    div#a 200x200 (1)
+    div#b1 100x150 (1)
+    div#b2 110x140 (1)
+    div#c 90x100 (1)
+    div#b3 110x140 (1)
 Updated layer tree
-#document (6)
-    iframe#frame (7)
-        #document 200x200 (8)
-            div#subframe1 80x80 (9)
-    div#a 200x200 (7)
-        div#b2 110x140 (8)
-            div#c 90x100 (9)
-                div#b1 100x150
-        div#b4 0x0
+<invalid node id> 0x0 (0)
+    <invalid node id> (1)
+    <invalid node id> 0x0 (1)
+    <invalid node id> (1)
+    #document (1)
+    #document (1)
+    iframe#frame (1)
+    #document 200x200 (1)
+    div#subframe1 80x80 (1)
+    div#a 200x200 (1)
+    div#b1 100x150 (1)
+    div#b2 110x140 (1)
+    div#c 90x100 (1)
+    div#b3 110x140 (1)
 Updated layer geometry
-#document (6)
-    iframe#frame (7)
-        #document 200x200 (8)
-            div#subframe1 80x80 (9)
-    div#a 200x200 (7)
-        div#b2 110x140 (8)
-            div#c 90x80 (9)
-                div#b1 100x150
-        div#b4 0x0
+<invalid node id> 0x0 (0)
+    <invalid node id> (1)
+    <invalid node id> 0x0 (1)
+    <invalid node id> (1)
+    #document (1)
+    #document (1)
+    iframe#frame (1)
+    #document 200x200 (1)
+    div#subframe1 80x80 (1)
+    div#a 200x200 (1)
+    div#b2 110x140 (1)
+    div#c 90x80 (1)
+    div#b1 100x150
+    div#b4 0x0
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing-expected.txt b/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing-expected.txt
index 2839b22..25978cd8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing-expected.txt
@@ -7,7 +7,7 @@
 
 Hovering content root
 State of layers:
-hovered layer: content root
+hovered layer: none
 selected layer: content root
 
 Selecting layer b
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing.js b/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing.js
index 3432e75..b59bcc4 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing.js
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layers-3d-view-hit-testing.js
@@ -59,8 +59,8 @@
     canvas = UI.panels.layers._layers3DView._canvasElement;
     var canvasWidth = canvas.offsetWidth;
     var canvasHeight = canvas.offsetHeight;
-    var rootWidth = contentRoot.width();
-    var rootHeight = contentRoot.height();
+    var rootWidth = 800;
+    var rootHeight = 600;
     var paddingX = canvasWidth * 0.1;
     var paddingY = canvasHeight * 0.1;
     var scaleX = rootWidth / (canvasWidth - paddingX);
diff --git a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js
index f804ff2..efbe73c9 100644
--- a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js
+++ b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js
@@ -31,7 +31,9 @@
   else
     return testRunner.fail('cannot find leaking node');
 
-  var retainers = helper.firstRetainingPath(node).map(node => node.name());
+  var retainers = helper.firstRetainingPath(node).map(
+      node => (node.name().includes("::"))
+          ? "Detached InternalNode" : node.name());
   var actual = retainers.join(', ');
   testRunner.log(`SUCCESS: retaining path = [${actual}]`);
   testRunner.completeTest();
diff --git a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js
index 1fa20236b..fe52bd97 100644
--- a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js
+++ b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js
@@ -28,7 +28,8 @@
   else
     return testRunner.fail('cannot find myEventListener node');
 
-  var retainers = helper.firstRetainingPath(node).map(node => node.name());
+  var retainers = helper.firstRetainingPath(node).map(
+      node => (node.name().includes("::")) ? "InternalNode" : node.name());
   var actual = retainers.join(', ');
   testRunner.log(`SUCCESS: retaining path = [${actual}]`);
   testRunner.completeTest();
diff --git a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js
index 94f3d82..0803db9 100644
--- a/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js
+++ b/third_party/blink/web_tests/inspector-protocol/heap-profiler/heap-snapshot-with-multiple-retainers.js
@@ -55,7 +55,8 @@
   var retainingPaths = [];
   for (var iter = eventListener.retainers(); iter.hasNext(); iter.next()) {
     var path = helper.firstRetainingPath(iter.retainer.node());
-    path = path.map(node => node.name());
+    path = path.map(
+        node => (node.name().includes("::")) ? "InternalNode" : node.name());
     retainingPaths.push(path.join(', '));
   }
 
diff --git a/third_party/blink/web_tests/inspector-protocol/layers/paint-profiler-expected.txt b/third_party/blink/web_tests/inspector-protocol/layers/paint-profiler-expected.txt
index c6836fe4..97cfc3f 100644
--- a/third_party/blink/web_tests/inspector-protocol/layers/paint-profiler-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/layers/paint-profiler-expected.txt
@@ -1,34 +1,38 @@
 Sanity test for DevTools Paint Profiler.
 matchingLayers.length: 1
 Profile array length: 4
-Profile subarray 0 length: 6
+Profile subarray 0 length: 7
 Profile timing [0][0] is a number: true
 Profile timing [0][1] is a number: true
 Profile timing [0][2] is a number: true
 Profile timing [0][3] is a number: true
 Profile timing [0][4] is a number: true
 Profile timing [0][5] is a number: true
-Profile subarray 1 length: 6
+Profile timing [0][6] is a number: true
+Profile subarray 1 length: 7
 Profile timing [1][0] is a number: true
 Profile timing [1][1] is a number: true
 Profile timing [1][2] is a number: true
 Profile timing [1][3] is a number: true
 Profile timing [1][4] is a number: true
 Profile timing [1][5] is a number: true
-Profile subarray 2 length: 6
+Profile timing [1][6] is a number: true
+Profile subarray 2 length: 7
 Profile timing [2][0] is a number: true
 Profile timing [2][1] is a number: true
 Profile timing [2][2] is a number: true
 Profile timing [2][3] is a number: true
 Profile timing [2][4] is a number: true
 Profile timing [2][5] is a number: true
-Profile subarray 3 length: 6
+Profile timing [2][6] is a number: true
+Profile subarray 3 length: 7
 Profile timing [3][0] is a number: true
 Profile timing [3][1] is a number: true
 Profile timing [3][2] is a number: true
 Profile timing [3][3] is a number: true
 Profile timing [3][4] is a number: true
 Profile timing [3][5] is a number: true
+Profile timing [3][6] is a number: true
 LayerTree.replaySnapshot returned valid image: true
 DONE!
 
diff --git a/third_party/blink/web_tests/inspector-protocol/layout-fonts/unicode-range-order.js b/third_party/blink/web_tests/inspector-protocol/layout-fonts/unicode-range-order.js
index ba47bf68..27c89ec 100644
--- a/third_party/blink/web_tests/inspector-protocol/layout-fonts/unicode-range-order.js
+++ b/third_party/blink/web_tests/inspector-protocol/layout-fonts/unicode-range-order.js
@@ -8,7 +8,7 @@
 
         @font-face {
             font-family: 'test1';
-            src: local('Times'), local('Tinos-Regular');
+            src: local('Times Roman'), local('Tinos-Regular'), local('Times New Roman');
         }
         @font-face {
             font-family: 'test1';
@@ -18,7 +18,7 @@
 
         @font-face {
             font-family: 'test2';
-            src: local('Times'), local('Tinos-Regular');
+            src: local('Times Roman'), local('Tinos-Regular'), local('Times New Roman');
         }
         @font-face {
             font-family: 'test2';
@@ -28,7 +28,7 @@
 
         @font-face {
             font-family: 'test3';
-            src: local('Times'), local('Tinos-Regular');
+            src: local('Times Roman'), local('Tinos-Regular'), local('Times New Roman');
         }
         @font-face {
             font-family: 'test3';
@@ -38,7 +38,7 @@
 
         @font-face {
             font-family: 'test4';
-            src: local('Times'), local('Tinos-Regular');
+            src: local('Times Roman'), local('Tinos-Regular'), local('Times New Roman');
         }
         @font-face {
             font-family: 'test4';
@@ -53,7 +53,7 @@
 
         @font-face {
             font-family: 'test5';
-            src: local('Times'), local('Tinos-Regular'), local('Times New Roman');
+            src: local('Times Roman'), local('Tinos-Regular'), local('Times New Roman');
         }
         @font-face {
             font-family: 'test5';
diff --git a/third_party/blink/web_tests/inspector-protocol/page/get-layout-metrics-expected.txt b/third_party/blink/web_tests/inspector-protocol/page/get-layout-metrics-expected.txt
index 65a1240..90a9721 100644
--- a/third_party/blink/web_tests/inspector-protocol/page/get-layout-metrics-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/page/get-layout-metrics-expected.txt
@@ -13,5 +13,6 @@
     pageX : 0
     pageY : 200
     scale : 1
+    zoom : 1
 }
 
diff --git a/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html b/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html
new file mode 100644
index 0000000..1a135b88
--- /dev/null
+++ b/third_party/blink/web_tests/media/controls/progress-bar-repaint-on-size-change.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<title>Progress bar repaint on resize</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script src="../media-controls.js"></script>
+<video controls></video>
+<script>
+async_test(t => {
+
+  const video = document.querySelector('video');
+  video.src = '../content/test.ogv'
+
+  const timeline = timelineElement(video);
+  const thumb = timelineThumb(video);
+  const segmentBefore = mediaControlsElement(internals.shadowRoot(timeline), '-internal-track-segment-highlight-before');
+  if (!segmentBefore)
+    throw 'Could not find segment before element';
+
+  const testCases = [300, 800, 200];
+
+  function runTestCase(index) {
+    let test = testCases[index];
+    video.width = test;
+    runAfterLayoutAndPaint(t.step_func(() => {
+      progressBarShouldMeetThumb();
+
+      let nextIndex = index + 1;
+      if (nextIndex == testCases.length) {
+        t.done();
+        return;
+      }
+      runTestCase(nextIndex);
+    }));
+  }
+
+  function progressBarShouldMeetThumb() {
+    const delta = 1.0;
+    let progress = segmentBefore.getBoundingClientRect().right;
+    let thumbPosition = elementCoordinates(thumb)[0];
+    assert_less_than(progress, thumbPosition + delta, 'progress bar should meet thumb');
+    assert_greater_than(progress, thumbPosition - delta, 'progress bar should meet thumb');
+  }
+
+  video.onloadedmetadata = t.step_func(() => {
+    const timelineBoundingRect = timeline.getBoundingClientRect();
+    let x = timelineBoundingRect.right - 1;
+    let y = timelineBoundingRect.top + 1;
+    // Click at top right corner of timeline element,
+    // thumb should moved to the end of timeline.
+    singleTapAtCoordinates(x, y, t.step_func(() => {
+      assert_equals(video.currentTime, video.duration, 'video should reach the end');
+      runTestCase(0);
+    }));
+  });
+});
+
+</script>
+</html>
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/unique-vs-family-match-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/unique-vs-family-match-expected.png
new file mode 100644
index 0000000..2575189
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/unique-vs-family-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/unique-vs-family-match-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/unique-vs-family-match-expected.png
new file mode 100644
index 0000000..6843064
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/unique-vs-family-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/unique-vs-family-match-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/unique-vs-family-match-expected.png
new file mode 100644
index 0000000..61d7356f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/fast/text/unique-vs-family-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/threaded/http/tests/devtools/tracing/timeline-paint/layer-tree-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/threaded/http/tests/devtools/tracing/timeline-paint/layer-tree-expected.txt
deleted file mode 100644
index 12014a9..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/threaded/http/tests/devtools/tracing/timeline-paint/layer-tree-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Tests that LayerTreeModel successfully imports layers from a trace.
-Exception while running: function layerTreeResolved(layerTreeModel)
-    {
-        InspectorTest.dumpLayerTree(undefined, layerTreeModel.contentRoot());
-        InspectorTest.completeTest();
-    }
-TypeError: Cannot read property 'contentRoot' of null
-    at layerTreeResolved (evaluateInWebInspector1.js:73:62)
-    at result (evaluateInWebInspector0.js:630:25)
-    at <anonymous>
-
diff --git a/third_party/blink/web_tests/storage/indexeddb/resources/transaction-starvation.js b/third_party/blink/web_tests/storage/indexeddb/resources/transaction-starvation.js
deleted file mode 100644
index 82d4074..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/resources/transaction-starvation.js
+++ /dev/null
@@ -1,63 +0,0 @@
-if (this.importScripts) {
-    importScripts('../../../resources/js-test.js');
-    importScripts('shared.js');
-}
-
-description("Check that read-only transactions don't starve read-write transactions.");
-
-indexedDBTest(prepareDatabase, runTransactions);
-
-function prepareDatabase(evt)
-{
-    preamble(evt);
-    evalAndLog("db = event.target.result");
-    evalAndLog("db.createObjectStore('store')");
-}
-
-function runTransactions(evt)
-{
-    preamble(evt);
-    evalAndLog("db = event.target.result");
-    debug("");
-
-    evalAndLog("readWriteTransactionStarted = false");
-    evalAndLog("readWriteTransactionComplete = false");
-
-    startReadOnlyTransaction();
-}
-
-
-function startReadOnlyTransaction()
-{
-    preamble();
-    evalAndLog("transaction = db.transaction('store', 'readonly')");
-    transaction.onabort = unexpectedAbortCallback;
-    evalAndLog("store = transaction.objectStore('store')");
-    debug("Keep the transaction alive with an endless series of gets");
-
-    function doGet() {
-        request = store.get(0);
-        request.onsuccess = function() {
-            if (!readWriteTransactionStarted)
-                startReadWriteTransaction();
-
-            if (!readWriteTransactionComplete)
-                doGet();
-        };
-    }
-    doGet();
-}
-
-function startReadWriteTransaction()
-{
-    preamble();
-    evalAndLog("transaction = db.transaction('store', 'readwrite')");
-    transaction.onabort = unexpectedAbortCallback;
-    evalAndLog("readWriteTransactionStarted = true");
-    transaction.oncomplete = function readWriteTransactionComplete() {
-        preamble();
-        testPassed("Transaction wasn't starved");
-        evalAndLog("readWriteTransactionComplete = true");
-        finishJSTest();
-    };
-}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/storage/indexeddb/transaction-starvation-expected.txt b/third_party/blink/web_tests/storage/indexeddb/transaction-starvation-expected.txt
deleted file mode 100644
index 0202ef0..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/transaction-starvation-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-Check that read-only transactions don't starve read-write transactions.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-dbname = "transaction-starvation.html"
-indexedDB.deleteDatabase(dbname)
-indexedDB.open(dbname)
-
-
-prepareDatabase():
-db = event.target.result
-db.createObjectStore('store')
-
-
-runTransactions():
-db = event.target.result
-
-
-readWriteTransactionStarted = false
-readWriteTransactionComplete = false
-
-
-startReadOnlyTransaction():
-transaction = db.transaction('store', 'readonly')
-store = transaction.objectStore('store')
-Keep the transaction alive with an endless series of gets
-
-
-startReadWriteTransaction():
-transaction = db.transaction('store', 'readwrite')
-readWriteTransactionStarted = true
-
-
-readWriteTransactionComplete():
-PASS Transaction wasn't starved
-readWriteTransactionComplete = true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/storage/indexeddb/transaction-starvation.html b/third_party/blink/web_tests/storage/indexeddb/transaction-starvation.html
deleted file mode 100644
index f3d7967..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/transaction-starvation.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/shared.js"></script>
-</head>
-<body>
-<script src="resources/transaction-starvation.js"></script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/virtual/outofblink-cors-ns/external/wpt/fetch/api/idl.any-expected.txt b/third_party/blink/web_tests/virtual/outofblink-cors-ns/external/wpt/fetch/api/idl.any-expected.txt
new file mode 100644
index 0000000..8bf325d7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/outofblink-cors-ns/external/wpt/fetch/api/idl.any-expected.txt
@@ -0,0 +1,133 @@
+This is a testharness.js-based test.
+Found 129 tests; 123 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
+PASS Headers interface: existence and properties of interface object
+PASS Headers interface object length
+PASS Headers interface object name
+PASS Headers interface: existence and properties of interface prototype object
+PASS Headers interface: existence and properties of interface prototype object's "constructor" property
+PASS Headers interface: existence and properties of interface prototype object's @@unscopables property
+PASS Headers interface: operation append(ByteString, ByteString)
+PASS Headers interface: operation delete(ByteString)
+PASS Headers interface: operation get(ByteString)
+PASS Headers interface: operation has(ByteString)
+PASS Headers interface: operation set(ByteString, ByteString)
+PASS Headers interface: iterable<ByteString, ByteString>
+PASS Headers must be primary interface of new Headers()
+PASS Stringification of new Headers()
+PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
+PASS Headers interface: calling append(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
+PASS Headers interface: new Headers() must inherit property "delete(ByteString)" with the proper type
+PASS Headers interface: calling delete(ByteString) on new Headers() with too few arguments must throw TypeError
+PASS Headers interface: new Headers() must inherit property "get(ByteString)" with the proper type
+PASS Headers interface: calling get(ByteString) on new Headers() with too few arguments must throw TypeError
+PASS Headers interface: new Headers() must inherit property "has(ByteString)" with the proper type
+PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
+PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
+PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
+PASS Request interface: existence and properties of interface object
+PASS Request interface object length
+PASS Request interface object name
+PASS Request interface: existence and properties of interface prototype object
+PASS Request interface: existence and properties of interface prototype object's "constructor" property
+PASS Request interface: existence and properties of interface prototype object's @@unscopables property
+PASS Request interface: attribute method
+PASS Request interface: attribute url
+PASS Request interface: attribute headers
+PASS Request interface: attribute destination
+PASS Request interface: attribute referrer
+PASS Request interface: attribute referrerPolicy
+PASS Request interface: attribute mode
+PASS Request interface: attribute credentials
+PASS Request interface: attribute cache
+PASS Request interface: attribute redirect
+PASS Request interface: attribute integrity
+PASS Request interface: attribute keepalive
+FAIL Request interface: attribute isReloadNavigation assert_true: The prototype object must have a property "isReloadNavigation" expected true got false
+PASS Request interface: attribute isHistoryNavigation
+PASS Request interface: attribute signal
+PASS Request interface: operation clone()
+FAIL Request interface: attribute body assert_true: The prototype object must have a property "body" expected true got false
+PASS Request interface: attribute bodyUsed
+PASS Request interface: operation arrayBuffer()
+PASS Request interface: operation blob()
+PASS Request interface: operation formData()
+PASS Request interface: operation json()
+PASS Request interface: operation text()
+PASS Request must be primary interface of new Request('about:blank')
+PASS Stringification of new Request('about:blank')
+PASS Request interface: new Request('about:blank') must inherit property "method" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "url" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "headers" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "destination" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "referrer" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "referrerPolicy" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "mode" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "credentials" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "cache" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "redirect" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "integrity" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type
+FAIL Request interface: new Request('about:blank') must inherit property "isReloadNavigation" with the proper type assert_inherits: property "isReloadNavigation" not found in prototype chain
+PASS Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "signal" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "clone()" with the proper type
+FAIL Request interface: new Request('about:blank') must inherit property "body" with the proper type assert_inherits: property "body" not found in prototype chain
+PASS Request interface: new Request('about:blank') must inherit property "bodyUsed" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "arrayBuffer()" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "blob()" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "formData()" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "json()" with the proper type
+PASS Request interface: new Request('about:blank') must inherit property "text()" with the proper type
+PASS Response interface: existence and properties of interface object
+PASS Response interface object length
+PASS Response interface object name
+PASS Response interface: existence and properties of interface prototype object
+PASS Response interface: existence and properties of interface prototype object's "constructor" property
+PASS Response interface: existence and properties of interface prototype object's @@unscopables property
+PASS Response interface: operation error()
+PASS Response interface: operation redirect(USVString, unsigned short)
+PASS Response interface: attribute type
+PASS Response interface: attribute url
+PASS Response interface: attribute redirected
+PASS Response interface: attribute status
+PASS Response interface: attribute ok
+PASS Response interface: attribute statusText
+PASS Response interface: attribute headers
+FAIL Response interface: attribute trailer assert_true: The prototype object must have a property "trailer" expected true got false
+PASS Response interface: operation clone()
+PASS Response interface: attribute body
+PASS Response interface: attribute bodyUsed
+PASS Response interface: operation arrayBuffer()
+PASS Response interface: operation blob()
+PASS Response interface: operation formData()
+PASS Response interface: operation json()
+PASS Response interface: operation text()
+PASS Response must be primary interface of new Response()
+PASS Stringification of new Response()
+PASS Response interface: new Response() must inherit property "error()" with the proper type
+PASS Response interface: new Response() must inherit property "redirect(USVString, unsigned short)" with the proper type
+PASS Response interface: calling redirect(USVString, unsigned short) on new Response() with too few arguments must throw TypeError
+PASS Response interface: new Response() must inherit property "type" with the proper type
+PASS Response interface: new Response() must inherit property "url" with the proper type
+PASS Response interface: new Response() must inherit property "redirected" with the proper type
+PASS Response interface: new Response() must inherit property "status" with the proper type
+PASS Response interface: new Response() must inherit property "ok" with the proper type
+PASS Response interface: new Response() must inherit property "statusText" with the proper type
+PASS Response interface: new Response() must inherit property "headers" with the proper type
+FAIL Response interface: new Response() must inherit property "trailer" with the proper type assert_inherits: property "trailer" not found in prototype chain
+PASS Response interface: new Response() must inherit property "clone()" with the proper type
+PASS Response interface: new Response() must inherit property "body" with the proper type
+PASS Response interface: new Response() must inherit property "bodyUsed" with the proper type
+PASS Response interface: new Response() must inherit property "arrayBuffer()" with the proper type
+PASS Response interface: new Response() must inherit property "blob()" with the proper type
+PASS Response interface: new Response() must inherit property "formData()" with the proper type
+PASS Response interface: new Response() must inherit property "json()" with the proper type
+PASS Response interface: new Response() must inherit property "text()" with the proper type
+PASS Window interface: operation fetch(RequestInfo, RequestInit)
+PASS Window interface: window must inherit property "fetch(RequestInfo, RequestInit)" with the proper type
+PASS Window interface: calling fetch(RequestInfo, RequestInit) on window with too few arguments must throw TypeError
+PASS WorkerGlobalScope interface: existence and properties of interface object
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index 76c19c2..2dc7b5e 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -687,6 +687,8 @@
     property disabled
     property href
     property hreflang
+    property imageSizes
+    property imageSrcset
     property import
     property integrity
     property media
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 2a1cbcb..b37f741 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -1673,6 +1673,11 @@
     method constructor
     method dispatchEvent
     method removeEventListener
+interface External
+    attribute @@toStringTag
+    method AddSearchProvider
+    method IsSearchProviderInstalled
+    method constructor
 interface FederatedCredential : Credential
     attribute @@toStringTag
     getter iconURL
@@ -2667,6 +2672,8 @@
     getter disabled
     getter href
     getter hreflang
+    getter imageSizes
+    getter imageSrcset
     getter import
     getter integrity
     getter media
@@ -2685,6 +2692,8 @@
     setter disabled
     setter href
     setter hreflang
+    setter imageSizes
+    setter imageSrcset
     setter integrity
     setter media
     setter referrerPolicy
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index e445dc8..4d3d4c266 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -440,6 +440,7 @@
     property onpagehide
     property onpageshow
     property onpopstate
+    property onportalactivate
     property onrejectionhandled
     property onstorage
     property onunhandledrejection
@@ -591,6 +592,7 @@
     property onpagehide
     property onpageshow
     property onpopstate
+    property onportalactivate
     property onrejectionhandled
     property onstorage
     property onunhandledrejection
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 6d6420f..9bfeea6 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
@@ -2175,6 +2175,11 @@
     method constructor
     method dispatchEvent
     method removeEventListener
+interface External
+    attribute @@toStringTag
+    method AddSearchProvider
+    method IsSearchProviderInstalled
+    method constructor
 interface FaceDetector
     attribute @@toStringTag
     method constructor
@@ -2491,6 +2496,7 @@
     getter onpagehide
     getter onpageshow
     getter onpopstate
+    getter onportalactivate
     getter onrejectionhandled
     getter onresize
     getter onscroll
@@ -2520,6 +2526,7 @@
     setter onpagehide
     setter onpageshow
     setter onpopstate
+    setter onportalactivate
     setter onrejectionhandled
     setter onresize
     setter onscroll
@@ -2952,6 +2959,7 @@
     getter onpagehide
     getter onpageshow
     getter onpopstate
+    getter onportalactivate
     getter onrejectionhandled
     getter onresize
     getter onscroll
@@ -2977,6 +2985,7 @@
     setter onpagehide
     setter onpageshow
     setter onpopstate
+    setter onportalactivate
     setter onrejectionhandled
     setter onresize
     setter onscroll
@@ -10772,6 +10781,7 @@
     getter onpointerrawmove
     getter onpointerup
     getter onpopstate
+    getter onportalactivate
     getter onprogress
     getter onratechange
     getter onrejectionhandled
@@ -10966,6 +10976,7 @@
     setter onpointerrawmove
     setter onpointerup
     setter onpopstate
+    setter onportalactivate
     setter onprogress
     setter onratechange
     setter onrejectionhandled
diff --git a/third_party/closure_compiler/externs/mediasession.js b/third_party/closure_compiler/externs/mediasession.js
new file mode 100644
index 0000000..eb234e1
--- /dev/null
+++ b/third_party/closure_compiler/externs/mediasession.js
@@ -0,0 +1,88 @@
+// Copyright 2018 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 The current spec of the Media Session API.
+ * @see https://wicg.github.io/mediasession/
+ * @externs
+ */
+
+/**
+ * @see https://wicg.github.io/mediasession/#the-mediaimage-dictionary
+ * @record
+ * @struct
+ */
+function MediaImage() {}
+
+/** @type {string} */
+MediaImage.prototype.src;
+
+/** @type {(string|undefined)} */
+MediaImage.prototype.sizes;
+
+/** @type {(string|undefined)} */
+MediaImage.prototype.type;
+
+/**
+ * A MediaMetadata object is a representation of the metadata associated with a
+ * MediaSession that can be used by user agents to provide customized user
+ * interface.
+ * @see https://wicg.github.io/mediasession/#the-mediametadata-interface
+ * @constructor
+ * @param {?MediaMetadataInit} init
+ */
+function MediaMetadata(init) {}
+
+/** @type {string} */
+MediaMetadata.prototype.album;
+
+/** @type {string} */
+MediaMetadata.prototype.artist;
+
+/** @type {!Array<!MediaImage>} */
+MediaMetadata.prototype.artwork;
+
+/** @type {string} */
+MediaMetadata.prototype.title;
+
+/**
+ * @see https://wicg.github.io/mediasession/#the-mediametadata-interface
+ * @record
+ * @struct
+ */
+function MediaMetadataInit() {}
+
+/** @type {(string|undefined)} */
+MediaMetadataInit.prototype.album;
+
+/** @type {(string|undefined)} */
+MediaMetadataInit.prototype.artist;
+
+/** @type {(!Array<!MediaImage>|undefined)} */
+MediaMetadataInit.prototype.artwork;
+
+/** @type {(string|undefined)} */
+MediaMetadataInit.prototype.title;
+
+/**
+ * A MediaSession objects represents a media session for a given document and
+ * allows a document to communicate to the user agent some information about the
+ * playback and how to handle it.
+ * @see https://wicg.github.io/mediasession/#the-mediasession-interface
+ * @interface
+ * @struct
+ */
+function MediaSession() {}
+
+/** @type {?MediaMetadata} */
+MediaSession.prototype.metadata;
+
+/** @type {string} */
+MediaSession.prototype.playbackState;
+
+/** @type {function(string, ?function())} */
+MediaSession.prototype.setActionHandler;
+
+/** @type {?MediaSession} */
+Navigator.prototype.mediaSession;
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn
index 064078a..f9618f3 100644
--- a/third_party/fuchsia-sdk/BUILD.gn
+++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -11,10 +11,37 @@
   lib_dirs = [ "sdk/arch/${target_cpu}/lib" ]
 }
 
-copy("vulkan_layers") {
+copy("vulkan_base_configs") {
+  sources = [
+    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json",
+  ]
+
+  outputs = [
+    "${root_gen_dir}/data/vulkan/explicit_layer.d/{{source_file_part}}",
+  ]
+}
+
+copy("vulkan_base_libs") {
+  sources = [
+    "sdk/arch/${target_cpu}/dist/libVkLayer_image_pipe_swapchain.so",
+    "sdk/arch/${target_cpu}/dist/libvulkan.so",
+  ]
+
+  outputs = [
+    "${root_out_dir}/{{source_file_part}}",
+  ]
+}
+
+group("vulkan_base") {
+  data_deps = [
+    ":vulkan_base_configs",
+    ":vulkan_base_libs",
+  ]
+}
+
+copy("vulkan_validation_configs") {
   sources = [
     "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json",
     "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_tracker.json",
     "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_parameter_validation.json",
     "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_standard_validation.json",
@@ -26,3 +53,24 @@
     "${root_gen_dir}/data/vulkan/explicit_layer.d/{{source_file_part}}",
   ]
 }
+
+copy("vulkan_validation_libs") {
+  sources = [
+    "sdk/arch/${target_cpu}/dist/libVkLayer_core_validation.so",
+    "sdk/arch/${target_cpu}/dist/libVkLayer_object_tracker.so",
+    "sdk/arch/${target_cpu}/dist/libVkLayer_parameter_validation.so",
+    "sdk/arch/${target_cpu}/dist/libVkLayer_threading.so",
+    "sdk/arch/${target_cpu}/dist/libVkLayer_unique_objects.so",
+  ]
+
+  outputs = [
+    "${root_out_dir}/{{source_file_part}}",
+  ]
+}
+
+group("vulkan_validation") {
+  data_deps = [
+    ":vulkan_validation_configs",
+    ":vulkan_validation_libs",
+  ]
+}
diff --git a/third_party/inspector_protocol/ConvertProtocolToJSON.py b/third_party/inspector_protocol/ConvertProtocolToJSON.py
deleted file mode 100755
index 6cfdc7f..0000000
--- a/third_party/inspector_protocol/ConvertProtocolToJSON.py
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018 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 convert_protocol_to_json
-
-
-def main():
-  convert_protocol_to_json.main()
diff --git a/third_party/inspector_protocol/OWNERS b/third_party/inspector_protocol/OWNERS
index 115505f6..40cea0c 100644
--- a/third_party/inspector_protocol/OWNERS
+++ b/third_party/inspector_protocol/OWNERS
@@ -4,3 +4,4 @@
 dgozman@chromium.org
 kozyatinskiy@chromium.org
 pfeldman@chromium.org
+johannes@chromium.org
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index c13caa5..2a6a994 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: 460186cff1f0eead0d418626e7e75f52105182b2
+Revision: fdbdb154336fc1f15a0a6775349dd90243b8d3fc
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/README.md b/third_party/inspector_protocol/README.md
new file mode 100644
index 0000000..4eff433
--- /dev/null
+++ b/third_party/inspector_protocol/README.md
@@ -0,0 +1,28 @@
+# Chromium inspector (devtools) protocol
+
+This package contains code generators and templates for the Chromium
+inspector protocol.
+
+The canonical location of this package is at
+https://chromium.googlesource.com/deps/inspector_protocol/
+
+In the Chromium tree, it's rolled into
+https://cs.chromium.org/chromium/src/third_party/inspector_protocol/
+
+In the V8 tree, it's rolled into
+https://cs.chromium.org/chromium/src/v8/third_party/inspector_protocol/
+
+See also [Contributing to Chrome Devtools Protocol](https://docs.google.com/document/d/1c-COD2kaK__5iMM5SEx-PzNA7HFmgttcYfOHHX0HaOM/edit).
+
+We're working on enabling standalone builds for parts of this package for
+testing and development, please feel free to ignore this for now.
+But, if you're familiar with
+[Chromium's development process](https://www.chromium.org/developers/contributing-code)
+and have the depot_tools installed, you may use these commands
+to fetch the package (and dependencies) and build and run the tests:
+
+    fetch inspector_protocol
+    cd src
+    gn gen out/Release
+    ninja -C out/Release json_parser_test
+    out/Release/json_parser_test
diff --git a/third_party/inspector_protocol/lib/Allocator_h.template b/third_party/inspector_protocol/lib/Allocator_h.template
index 8f8109d..d05ddae 100644
--- a/third_party/inspector_protocol/lib/Allocator_h.template
+++ b/third_party/inspector_protocol/lib/Allocator_h.template
@@ -11,13 +11,6 @@
 
 enum NotNullTagEnum { NotNullLiteral };
 
-#define PROTOCOL_DISALLOW_NEW()                                 \
-    private:                                                    \
-        void* operator new(size_t) = delete;                    \
-        void* operator new(size_t, NotNullTagEnum, void*) = delete; \
-        void* operator new(size_t, void*) = delete;             \
-    public:
-
 #define PROTOCOL_DISALLOW_COPY(ClassName) \
     private: \
         ClassName(const ClassName&) = delete; \
diff --git a/third_party/inspector_protocol/lib/Collections_h.template b/third_party/inspector_protocol/lib/Collections_h.template
deleted file mode 100644
index e69de29..0000000
--- a/third_party/inspector_protocol/lib/Collections_h.template
+++ /dev/null
diff --git a/third_party/inspector_protocol/lib/Forward_h.template b/third_party/inspector_protocol/lib/Forward_h.template
index a3f7c2f0..ac792e0 100644
--- a/third_party/inspector_protocol/lib/Forward_h.template
+++ b/third_party/inspector_protocol/lib/Forward_h.template
@@ -11,6 +11,7 @@
 #include {{format_include(config.lib.string_header)}}
 
 #include <cstddef>
+#include <memory>
 #include <vector>
 #include <unordered_map>
 #include <unordered_set>
diff --git a/third_party/libjingle_xmpp/BUILD.gn b/third_party/libjingle_xmpp/BUILD.gn
index 95d87e5..7d7c8ad 100644
--- a/third_party/libjingle_xmpp/BUILD.gn
+++ b/third_party/libjingle_xmpp/BUILD.gn
@@ -97,6 +97,7 @@
   deps = [
     ":rtc_xmllite",
     "//base",
+    "//net",
     "//third_party/webrtc/rtc_base:rtc_base_approved",
     "//third_party/webrtc_overrides:task_queue_impl",
   ]
diff --git a/third_party/libjingle_xmpp/DEPS b/third_party/libjingle_xmpp/DEPS
index c845c2d4..3590e17 100644
--- a/third_party/libjingle_xmpp/DEPS
+++ b/third_party/libjingle_xmpp/DEPS
@@ -2,6 +2,7 @@
   "+base/logging.h",
   "+base/macros.h",
   "+base/stl_util.h",
+  "+net/base/host_port_pair.h",
 ]
 
 specific_include_rules = {
diff --git a/third_party/libjingle_xmpp/task_runner/task.cc b/third_party/libjingle_xmpp/task_runner/task.cc
index 62396c55..11a8f6a 100644
--- a/third_party/libjingle_xmpp/task_runner/task.cc
+++ b/third_party/libjingle_xmpp/task_runner/task.cc
@@ -51,21 +51,9 @@
   }
 }
 
-int64_t Task::CurrentTime() {
-  return GetRunner()->CurrentTime();
-}
-
-int64_t Task::ElapsedTime() {
-  return CurrentTime() - start_time_;
-}
-
 void Task::Start() {
   if (state_ != STATE_INIT)
     return;
-  // Set the start time before starting the task.  Otherwise if the task
-  // finishes quickly and deletes the Task object, setting start_time_
-  // will crash.
-  start_time_ = CurrentTime();
   GetRunner()->StartTask(this);
 }
 
@@ -113,7 +101,6 @@
   } else {
     state_ = new_state;
     blocked_ = false;
-    ResetTimeout();
   }
 
   if (new_state == STATE_DONE) {
@@ -197,26 +184,20 @@
 int Task::Process(int state) {
   int newstate = STATE_ERROR;
 
-  if (TimedOut()) {
-    ClearTimeout();
-    newstate = OnTimeout();
-    SignalTimeout();
-  } else {
-    switch (state) {
-      case STATE_INIT:
-        newstate = STATE_START;
-        break;
-      case STATE_START:
-        newstate = ProcessStart();
-        break;
-      case STATE_RESPONSE:
-        newstate = ProcessResponse();
-        break;
-      case STATE_DONE:
-      case STATE_ERROR:
-        newstate = STATE_BLOCKED;
-        break;
-    }
+  switch (state) {
+    case STATE_INIT:
+      newstate = STATE_START;
+      break;
+    case STATE_START:
+      newstate = ProcessStart();
+      break;
+    case STATE_RESPONSE:
+      newstate = ProcessResponse();
+      break;
+    case STATE_DONE:
+    case STATE_ERROR:
+      newstate = STATE_BLOCKED;
+      break;
   }
 
   return newstate;
@@ -231,54 +212,4 @@
   return STATE_DONE;
 }
 
-void Task::set_timeout_seconds(const int timeout_seconds) {
-  timeout_seconds_ = timeout_seconds;
-  ResetTimeout();
-}
-
-bool Task::TimedOut() {
-  return timeout_seconds_ &&
-    timeout_time_ &&
-    CurrentTime() >= timeout_time_;
-}
-
-void Task::ResetTimeout() {
-  int64_t previous_timeout_time = timeout_time_;
-  bool timeout_allowed = (state_ != STATE_INIT)
-                      && (state_ != STATE_DONE)
-                      && (state_ != STATE_ERROR);
-  if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
-    timeout_time_ = CurrentTime() +
-                    (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
-  else
-    timeout_time_ = 0;
-
-  GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
-}
-
-void Task::ClearTimeout() {
-  int64_t previous_timeout_time = timeout_time_;
-  timeout_time_ = 0;
-  GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
-}
-
-void Task::SuspendTimeout() {
-  if (!timeout_suspended_) {
-    timeout_suspended_ = true;
-    ResetTimeout();
-  }
-}
-
-void Task::ResumeTimeout() {
-  if (timeout_suspended_) {
-    timeout_suspended_ = false;
-    ResetTimeout();
-  }
-}
-
-int Task::OnTimeout() {
-  // by default, we are finished after timing out
-  return STATE_DONE;
-}
-
 } // namespace rtc
diff --git a/third_party/libjingle_xmpp/task_runner/task.h b/third_party/libjingle_xmpp/task_runner/task.h
index 90c91609..40bbc48 100644
--- a/third_party/libjingle_xmpp/task_runner/task.h
+++ b/third_party/libjingle_xmpp/task_runner/task.h
@@ -16,7 +16,6 @@
 #include <string>
 
 #include "third_party/libjingle_xmpp/task_runner/taskparent.h"
-#include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
 
 /////////////////////////////////////////////////////////////////////
 //
@@ -109,14 +108,6 @@
   // Called from outside to stop task without any more callbacks
   void Abort(bool nowake = false);
 
-  bool TimedOut();
-
-  int64_t timeout_time() const { return timeout_time_; }
-  int timeout_seconds() const { return timeout_seconds_; }
-  void set_timeout_seconds(int timeout_seconds);
-
-  sigslot::signal0<> SignalTimeout;
-
   // Called inside the task to signal that the task may be unblocked
   void Wake();
 
@@ -135,22 +126,13 @@
   // Called inside to advise that the task should wake and signal an error
   void Error();
 
-  int64_t CurrentTime();
-
   virtual std::string GetStateName(int state) const;
   virtual int Process(int state);
   virtual void Stop();
   virtual int ProcessStart() = 0;
   virtual int ProcessResponse();
 
-  void ResetTimeout();
-  void ClearTimeout();
-
-  void SuspendTimeout();
-  void ResumeTimeout();
-
  protected:
-  virtual int OnTimeout();
 
  private:
   void Done();
diff --git a/third_party/libjingle_xmpp/task_runner/task_unittest.cc b/third_party/libjingle_xmpp/task_runner/task_unittest.cc
index 04765ef..b8d8933 100644
--- a/third_party/libjingle_xmpp/task_runner/task_unittest.cc
+++ b/third_party/libjingle_xmpp/task_runner/task_unittest.cc
@@ -16,81 +16,25 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/task_runner/taskrunner.h"
 #include "third_party/webrtc/rtc_base/thread.h"
-#include "third_party/webrtc/rtc_base/time_utils.h"
 
 namespace rtc {
 
-static int64_t GetCurrentTime() {
-  return TimeMillis() * 10000;
-}
-
-// feel free to change these numbers.  Note that '0' won't work, though
-#define STUCK_TASK_COUNT 5
-#define HAPPY_TASK_COUNT 20
-
-// this is a generic timeout task which, when it signals timeout, will
-// include the unique ID of the task in the signal (we don't use this
-// in production code because we haven't yet had occasion to generate
-// an array of the same types of task)
-
-class IdTimeoutTask : public Task, public sigslot::has_slots<> {
+class FakeTask : public Task {
  public:
-  explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
-    SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
-  }
-
-  sigslot::signal1<const int> SignalTimeoutId;
-  sigslot::signal1<const int> SignalDoneId;
-
-  virtual int ProcessStart() {
+  explicit FakeTask(TaskParent *parent) : Task(parent) {}
+  int ProcessStart() override {
     return STATE_RESPONSE;
   }
-
-  void OnLocalTimeout() {
-    SignalTimeoutId(unique_id());
-  }
-
- protected:
-  virtual void Stop() {
-    SignalDoneId(unique_id());
-    Task::Stop();
-  }
-};
-
-class StuckTask : public IdTimeoutTask {
- public:
-  explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
-  virtual int ProcessStart() {
-    return STATE_BLOCKED;
-  }
-};
-
-class HappyTask : public IdTimeoutTask {
- public:
-  explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
-    time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
-  }
-  virtual int ProcessStart() {
-    if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
-      return STATE_RESPONSE;
-    else
-      return STATE_BLOCKED;
-  }
-
- private:
-  int time_to_perform_;
 };
 
 // simple implementation of a task runner which uses Windows'
 // GetSystemTimeAsFileTime() to get the current clock ticks
-
 class MyTaskRunner : public TaskRunner {
  public:
   MyTaskRunner() { ThreadManager::Instance()->WrapCurrentThread(); }
   ~MyTaskRunner() { ThreadManager::Instance()->UnwrapCurrentThread(); }
 
   virtual void WakeTasks() { RunTasks(); }
-  virtual int64_t CurrentTime() { return GetCurrentTime(); }
 
   bool timeout_change() const {
     return timeout_change_;
@@ -106,191 +50,11 @@
   bool timeout_change_;
 };
 
-//
-// this unit test is primarily concerned (for now) with the timeout
-// functionality in tasks.  It works as follows:
-//
-//   * Create a bunch of tasks, some "stuck" (ie., guaranteed to timeout)
-//     and some "happy" (will immediately finish).
-//   * Set the timeout on the "stuck" tasks to some number of seconds between
-//     1 and the number of stuck tasks
-//   * Start all the stuck & happy tasks in random order
-//   * Wait "number of stuck tasks" seconds and make sure everything timed out
-
-class TaskTest : public sigslot::has_slots<> {
- public:
-  TaskTest() {}
-
-  // no need to delete any tasks; the task runner owns them
-  ~TaskTest() {}
-
-  void Start() {
-    // create and configure tasks
-    for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
-      stuck_[i].task_ = new StuckTask(&task_runner_);
-      stuck_[i].task_->SignalTimeoutId.connect(this,
-                                               &TaskTest::OnTimeoutStuck);
-      stuck_[i].timed_out_ = false;
-      stuck_[i].xlat_ = stuck_[i].task_->unique_id();
-      stuck_[i].task_->set_timeout_seconds(i + 1);
-      DVLOG(1) << "Task " << stuck_[i].xlat_ << " created with timeout "
-               << stuck_[i].task_->timeout_seconds();
-    }
-
-    for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
-      happy_[i].task_ = new HappyTask(&task_runner_);
-      happy_[i].task_->SignalTimeoutId.connect(this,
-                                               &TaskTest::OnTimeoutHappy);
-      happy_[i].task_->SignalDoneId.connect(this,
-                                            &TaskTest::OnDoneHappy);
-      happy_[i].timed_out_ = false;
-      happy_[i].xlat_ = happy_[i].task_->unique_id();
-    }
-
-    // start all the tasks in random order
-    int stuck_index = 0;
-    int happy_index = 0;
-    for (int i = 0; i < STUCK_TASK_COUNT + HAPPY_TASK_COUNT; ++i) {
-      if ((stuck_index < STUCK_TASK_COUNT) &&
-          (happy_index < HAPPY_TASK_COUNT)) {
-        if (rand() % 2 == 1) {
-          stuck_[stuck_index++].task_->Start();
-        } else {
-          happy_[happy_index++].task_->Start();
-        }
-      } else if (stuck_index < STUCK_TASK_COUNT) {
-        stuck_[stuck_index++].task_->Start();
-      } else {
-        happy_[happy_index++].task_->Start();
-      }
-    }
-
-    for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
-      std::cout << "Stuck task #" << i << " timeout is " <<
-          stuck_[i].task_->timeout_seconds() << " at " <<
-          stuck_[i].task_->timeout_time() << std::endl;
-    }
-
-    // just a little self-check to make sure we started all the tasks
-    ASSERT_EQ(STUCK_TASK_COUNT, stuck_index);
-    ASSERT_EQ(HAPPY_TASK_COUNT, happy_index);
-
-    // run the unblocked tasks
-    DVLOG(1) << "Running tasks";
-    task_runner_.RunTasks();
-
-    std::cout << "Start time is " << GetCurrentTime() << std::endl;
-
-    // give all the stuck tasks time to timeout
-    for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
-         ++i) {
-      Thread::Current()->ProcessMessages(1000);
-      for (int j = 0; j < HAPPY_TASK_COUNT; ++j) {
-        if (happy_[j].task_) {
-          happy_[j].task_->Wake();
-        }
-      }
-      DVLOG(1) << "Polling tasks";
-      task_runner_.PollTasks();
-    }
-
-    // We see occasional test failures here due to the stuck tasks not having
-    // timed-out yet, which seems like it should be impossible. To help track
-    // this down we have added logging of the timing information, which we send
-    // directly to stdout so that we get it in opt builds too.
-    std::cout << "End time is " << GetCurrentTime() << std::endl;
-  }
-
-  void OnTimeoutStuck(const int id) {
-    DVLOG(1) << "Timed out task " << id;
-
-    int i;
-    for (i = 0; i < STUCK_TASK_COUNT; ++i) {
-      if (stuck_[i].xlat_ == id) {
-        stuck_[i].timed_out_ = true;
-        stuck_[i].task_ = NULL;
-        break;
-      }
-    }
-
-    // getting a bad ID here is a failure, but let's continue
-    // running to see what else might go wrong
-    EXPECT_LT(i, STUCK_TASK_COUNT);
-  }
-
-  void OnTimeoutHappy(const int id) {
-    int i;
-    for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
-      if (happy_[i].xlat_ == id) {
-        happy_[i].timed_out_ = true;
-        happy_[i].task_ = NULL;
-        break;
-      }
-    }
-
-    // getting a bad ID here is a failure, but let's continue
-    // running to see what else might go wrong
-    EXPECT_LT(i, HAPPY_TASK_COUNT);
-  }
-
-  void OnDoneHappy(const int id) {
-    int i;
-    for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
-      if (happy_[i].xlat_ == id) {
-        happy_[i].task_ = NULL;
-        break;
-      }
-    }
-
-    // getting a bad ID here is a failure, but let's continue
-    // running to see what else might go wrong
-    EXPECT_LT(i, HAPPY_TASK_COUNT);
-  }
-
-  void check_passed() {
-    EXPECT_TRUE(task_runner_.AllChildrenDone());
-
-    // make sure none of our happy tasks timed out
-    for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
-      EXPECT_FALSE(happy_[i].timed_out_);
-    }
-
-    // make sure all of our stuck tasks timed out
-    for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
-      EXPECT_TRUE(stuck_[i].timed_out_);
-      if (!stuck_[i].timed_out_) {
-        std::cout << "Stuck task #" << i << " timeout is at "
-                  << stuck_[i].task_->timeout_time() << std::endl;
-      }
-    }
-
-    std::cout.flush();
-  }
-
- private:
-  struct TaskInfo {
-    IdTimeoutTask *task_;
-    bool timed_out_;
-    int xlat_;
-  };
-
-  MyTaskRunner task_runner_;
-  TaskInfo stuck_[STUCK_TASK_COUNT];
-  TaskInfo happy_[HAPPY_TASK_COUNT];
-};
-
-TEST(start_task_test, Timeout) {
-  TaskTest task_test;
-  task_test.Start();
-  task_test.check_passed();
-}
-
 // Test for aborting the task while it is running
 
 class AbortTask : public Task {
  public:
   explicit AbortTask(TaskParent *parent) : Task(parent) {
-    set_timeout_seconds(1);
   }
 
   virtual int ProcessStart() {
@@ -310,7 +74,6 @@
 
   void Start() {
     Task *abort_task = new AbortTask(&task_runner_);
-    abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
     abort_task->Start();
 
     // run the task
@@ -394,92 +157,11 @@
   abort_should_wake_test.Start();
 }
 
-// Validate that TaskRunner's OnTimeoutChange gets called appropriately
-//  * When a task calls UpdateTaskTimeout
-//  * When the next timeout task time, times out
-class TimeoutChangeTest : public sigslot::has_slots<> {
- public:
-  TimeoutChangeTest() : task_count_(base::size(stuck_tasks_)) {}
-
-  // no need to delete any tasks; the task runner owns them
-  ~TimeoutChangeTest() {}
-
-  void Start() {
-    for (int i = 0; i < task_count_; ++i) {
-      stuck_tasks_[i] = new StuckTask(&task_runner_);
-      stuck_tasks_[i]->set_timeout_seconds(i + 2);
-      stuck_tasks_[i]->SignalTimeoutId.connect(this,
-                                               &TimeoutChangeTest::OnTimeoutId);
-    }
-
-    for (int i = task_count_ - 1; i >= 0; --i) {
-      stuck_tasks_[i]->Start();
-    }
-    task_runner_.clear_timeout_change();
-
-    // At this point, our timeouts are set as follows
-    // task[0] is 2 seconds, task[1] at 3 seconds, etc.
-
-    stuck_tasks_[0]->set_timeout_seconds(2);
-    // Now, task[0] is 2 seconds, task[1] at 3 seconds...
-    // so timeout change shouldn't be called.
-    EXPECT_FALSE(task_runner_.timeout_change());
-    task_runner_.clear_timeout_change();
-
-    stuck_tasks_[0]->set_timeout_seconds(1);
-    // task[0] is 1 seconds, task[1] at 3 seconds...
-    // The smallest timeout got smaller so timeout change be called.
-    EXPECT_TRUE(task_runner_.timeout_change());
-    task_runner_.clear_timeout_change();
-
-    stuck_tasks_[1]->set_timeout_seconds(2);
-    // task[0] is 1 seconds, task[1] at 2 seconds...
-    // The smallest timeout is still 1 second so no timeout change.
-    EXPECT_FALSE(task_runner_.timeout_change());
-    task_runner_.clear_timeout_change();
-
-    while (task_count_ > 0) {
-      int previous_count = task_count_;
-      task_runner_.PollTasks();
-      if (previous_count != task_count_) {
-        // We only get here when a task times out.  When that
-        // happens, the timeout change should get called because
-        // the smallest timeout is now in the past.
-        EXPECT_TRUE(task_runner_.timeout_change());
-        task_runner_.clear_timeout_change();
-      }
-      Thread::Current()->socketserver()->Wait(500, false);
-    }
-  }
-
- private:
-  void OnTimeoutId(const int id) {
-    for (size_t i = 0; i < base::size(stuck_tasks_); ++i) {
-      if (stuck_tasks_[i] && stuck_tasks_[i]->unique_id() == id) {
-        task_count_--;
-        stuck_tasks_[i] = NULL;
-        break;
-      }
-    }
-  }
-
-  MyTaskRunner task_runner_;
-  StuckTask* (stuck_tasks_[3]);
-  int task_count_;
-  DISALLOW_COPY_AND_ASSIGN(TimeoutChangeTest);
-};
-
-TEST(start_task_test, TimeoutChange) {
-  TimeoutChangeTest timeout_change_test;
-  timeout_change_test.Start();
-}
-
 class DeleteTestTaskRunner : public TaskRunner {
  public:
   DeleteTestTaskRunner() {
   }
   virtual void WakeTasks() { }
-  virtual int64_t CurrentTime() { return GetCurrentTime(); }
  private:
   DISALLOW_COPY_AND_ASSIGN(DeleteTestTaskRunner);
 };
@@ -488,12 +170,12 @@
   // This test ensures that we don't
   // crash if a task is deleted without running it.
   DeleteTestTaskRunner task_runner;
-  HappyTask* happy_task = new HappyTask(&task_runner);
-  happy_task->Start();
+  FakeTask* task = new FakeTask(&task_runner);
+  task->Start();
 
   // try deleting the task directly
-  HappyTask* child_happy_task = new HappyTask(happy_task);
-  delete child_happy_task;
+  FakeTask* child_task = new FakeTask(task);
+  delete child_task;
 
   // run the unblocked tasks
   task_runner.RunTasks();
@@ -504,11 +186,11 @@
   // crash if a task runner is deleted without
   // running a certain task.
   DeleteTestTaskRunner task_runner;
-  HappyTask* happy_task = new HappyTask(&task_runner);
-  happy_task->Start();
+  FakeTask* task = new FakeTask(&task_runner);
+  task->Start();
 
-  HappyTask* child_happy_task = new HappyTask(happy_task);
-  child_happy_task->Start();
+  FakeTask* child_task = new FakeTask(task);
+  child_task->Start();
 
   // Never run the tasks
 }
diff --git a/third_party/libjingle_xmpp/task_runner/taskrunner.cc b/third_party/libjingle_xmpp/task_runner/taskrunner.cc
index 83d5be6..8711b6c 100644
--- a/third_party/libjingle_xmpp/task_runner/taskrunner.cc
+++ b/third_party/libjingle_xmpp/task_runner/taskrunner.cc
@@ -28,11 +28,6 @@
 
 void TaskRunner::StartTask(Task * task) {
   tasks_.push_back(task);
-
-  // the task we just started could be about to timeout --
-  // make sure our "next timeout task" is correct
-  UpdateTaskTimeout(task, 0);
-
   WakeTasks();
 }
 
@@ -56,8 +51,6 @@
 
   tasks_running_ = true;
 
-  int64_t previous_timeout_time = next_task_timeout();
-
   int did_run = true;
   while (did_run) {
     did_run = false;
@@ -70,16 +63,9 @@
     }
   }
   // Tasks are deleted when running has paused
-  bool need_timeout_recalc = false;
   for (size_t i = 0; i < tasks_.size(); ++i) {
     if (tasks_[i]->IsDone()) {
       Task* task = tasks_[i];
-      if (next_timeout_task_ &&
-          task->unique_id() == next_timeout_task_->unique_id()) {
-        next_timeout_task_ = NULL;
-        need_timeout_recalc = true;
-      }
-
 #if DCHECK_IS_ON
       deleting_task_ = task;
 #endif
@@ -97,120 +83,7 @@
                    reinterpret_cast<Task *>(NULL));
 
   tasks_.erase(it, tasks_.end());
-
-  if (need_timeout_recalc)
-    RecalcNextTimeout(NULL);
-
-  // Make sure that adjustments are done to account
-  // for any timeout changes (but don't call this
-  // while being destroyed since it calls a pure virtual function).
-  if (!in_destructor)
-    CheckForTimeoutChange(previous_timeout_time);
-
   tasks_running_ = false;
 }
 
-void TaskRunner::PollTasks() {
-  // see if our "next potentially timed-out task" has indeed timed out.
-  // If it has, wake it up, then queue up the next task in line
-  // Repeat while we have new timed-out tasks.
-  // TODO: We need to guard against WakeTasks not updating
-  // next_timeout_task_. Maybe also add documentation in the header file once
-  // we understand this code better.
-  Task* old_timeout_task = NULL;
-  while (next_timeout_task_ &&
-      old_timeout_task != next_timeout_task_ &&
-      next_timeout_task_->TimedOut()) {
-    old_timeout_task = next_timeout_task_;
-    next_timeout_task_->Wake();
-    WakeTasks();
-  }
-}
-
-int64_t TaskRunner::next_task_timeout() const {
-  if (next_timeout_task_) {
-    return next_timeout_task_->timeout_time();
-  }
-  return 0;
-}
-
-// this function gets called frequently -- when each task changes
-// state to something other than DONE, ERROR or BLOCKED, it calls
-// ResetTimeout(), which will call this function to make sure that
-// the next timeout-able task hasn't changed.  The logic in this function
-// prevents RecalcNextTimeout() from getting called in most cases,
-// effectively making the task scheduler O-1 instead of O-N
-
-void TaskRunner::UpdateTaskTimeout(Task* task,
-                                   int64_t previous_task_timeout_time) {
-  DCHECK(task != NULL);
-  int64_t previous_timeout_time = next_task_timeout();
-  bool task_is_timeout_task = next_timeout_task_ != NULL &&
-      task->unique_id() == next_timeout_task_->unique_id();
-  if (task_is_timeout_task) {
-    previous_timeout_time = previous_task_timeout_time;
-  }
-
-  // if the relevant task has a timeout, then
-  // check to see if it's closer than the current
-  // "about to timeout" task
-  if (task->timeout_time()) {
-    if (next_timeout_task_ == NULL ||
-        (task->timeout_time() <= next_timeout_task_->timeout_time())) {
-      next_timeout_task_ = task;
-    }
-  } else if (task_is_timeout_task) {
-    // otherwise, if the task doesn't have a timeout,
-    // and it used to be our "about to timeout" task,
-    // walk through all the tasks looking for the real
-    // "about to timeout" task
-    RecalcNextTimeout(task);
-  }
-
-  // Note when task_running_, then the running routine
-  // (TaskRunner::InternalRunTasks) is responsible for calling
-  // CheckForTimeoutChange.
-  if (!tasks_running_) {
-    CheckForTimeoutChange(previous_timeout_time);
-  }
-}
-
-void TaskRunner::RecalcNextTimeout(Task *exclude_task) {
-  // walk through all the tasks looking for the one
-  // which satisfies the following:
-  //   it's not finished already
-  //   we're not excluding it
-  //   it has the closest timeout time
-
-  int64_t next_timeout_time = 0;
-  next_timeout_task_ = NULL;
-
-  for (size_t i = 0; i < tasks_.size(); ++i) {
-    Task *task = tasks_[i];
-    // if the task isn't complete, and it actually has a timeout time
-    if (!task->IsDone() && (task->timeout_time() > 0))
-      // if it doesn't match our "exclude" task
-      if (exclude_task == NULL ||
-          exclude_task->unique_id() != task->unique_id())
-        // if its timeout time is sooner than our current timeout time
-        if (next_timeout_time == 0 ||
-            task->timeout_time() <= next_timeout_time) {
-          // set this task as our next-to-timeout
-          next_timeout_time = task->timeout_time();
-          next_timeout_task_ = task;
-        }
-  }
-}
-
-void TaskRunner::CheckForTimeoutChange(int64_t previous_timeout_time) {
-  int64_t next_timeout = next_task_timeout();
-  bool timeout_change = (previous_timeout_time == 0 && next_timeout != 0) ||
-      next_timeout < previous_timeout_time ||
-      (previous_timeout_time <= CurrentTime() &&
-       previous_timeout_time != next_timeout);
-  if (timeout_change) {
-    OnTimeoutChange();
-  }
-}
-
 } // namespace rtc
diff --git a/third_party/libjingle_xmpp/task_runner/taskrunner.h b/third_party/libjingle_xmpp/task_runner/taskrunner.h
index 7a0d09b5..825ec1c7 100644
--- a/third_party/libjingle_xmpp/task_runner/taskrunner.h
+++ b/third_party/libjingle_xmpp/task_runner/taskrunner.h
@@ -33,18 +33,8 @@
 
   virtual void WakeTasks() = 0;
 
-  // Returns the current time in 100ns units.  It is used for
-  // determining timeouts.  The origin is not important, only
-  // the units and that rollover while the computer is running.
-  //
-  // On Windows, GetSystemTimeAsFileTime is the typical implementation.
-  virtual int64_t CurrentTime() = 0;
-
   void StartTask(Task *task);
   void RunTasks();
-  void PollTasks();
-
-  void UpdateTaskTimeout(Task* task, int64_t previous_task_timeout_time);
 
 #if DCHECK_IS_ON
   bool is_ok_to_delete(Task* task) {
@@ -84,17 +74,13 @@
 
  private:
   void InternalRunTasks(bool in_destructor);
-  void CheckForTimeoutChange(int64_t previous_timeout_time);
 
   std::vector<Task *> tasks_;
-  Task *next_timeout_task_ = nullptr;
   bool tasks_running_ = false;
 #if DCHECK_IS_ON
   int abort_count_ = 0;
   Task* deleting_task_ = nullptr;
 #endif
-
-  void RecalcNextTimeout(Task *exclude_task);
 };
 
 } // namespace rtc
diff --git a/third_party/libjingle_xmpp/xmpp/asyncsocket.h b/third_party/libjingle_xmpp/xmpp/asyncsocket.h
index e56a8d4..ccfbe8f 100644
--- a/third_party/libjingle_xmpp/xmpp/asyncsocket.h
+++ b/third_party/libjingle_xmpp/xmpp/asyncsocket.h
@@ -13,12 +13,9 @@
 
 #include <string>
 
+#include "net/base/host_port_pair.h"
 #include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
 
-namespace rtc {
-  class SocketAddress;
-}
-
 namespace buzz {
 
 class AsyncSocket {
@@ -45,7 +42,7 @@
   virtual Error error() = 0;
   virtual int GetError() = 0;    // winsock error code
 
-  virtual bool Connect(const rtc::SocketAddress& addr) = 0;
+  virtual bool Connect(const net::HostPortPair& addr) = 0;
   virtual bool Read(char * data, size_t len, size_t* len_read) = 0;
   virtual bool Write(const char * data, size_t len) = 0;
   virtual bool Close() = 0;
diff --git a/third_party/libjingle_xmpp/xmpp/prexmppauth.h b/third_party/libjingle_xmpp/xmpp/prexmppauth.h
index 7543b20..8088cd1 100644
--- a/third_party/libjingle_xmpp/xmpp/prexmppauth.h
+++ b/third_party/libjingle_xmpp/xmpp/prexmppauth.h
@@ -14,10 +14,6 @@
 #include "third_party/libjingle_xmpp/xmpp/saslhandler.h"
 #include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
 
-namespace rtc {
-  class SocketAddress;
-}
-
 namespace buzz {
 
 class Jid;
diff --git a/third_party/libjingle_xmpp/xmpp/xmppclient.cc b/third_party/libjingle_xmpp/xmpp/xmppclient.cc
index 40e7b3f..fd70754b 100644
--- a/third_party/libjingle_xmpp/xmpp/xmppclient.cc
+++ b/third_party/libjingle_xmpp/xmpp/xmppclient.cc
@@ -11,6 +11,7 @@
 #include "third_party/libjingle_xmpp/xmpp/xmppclient.h"
 
 #include "base/logging.h"
+#include "net/base/host_port_pair.h"
 #include "third_party/libjingle_xmpp/xmpp/constants.h"
 #include "third_party/libjingle_xmpp/xmpp/plainsaslhandler.h"
 #include "third_party/libjingle_xmpp/xmpp/prexmppauth.h"
@@ -51,7 +52,7 @@
   std::string pass_;
   std::string auth_mechanism_;
   std::string auth_token_;
-  rtc::SocketAddress server_;
+  net::HostPortPair server_;
   XmppEngine::Error pre_engine_error_;
   int pre_engine_subcode_;
   CaptchaChallenge captcha_challenge_;
@@ -110,7 +111,7 @@
   // For other servers, we leave the strings empty, which causes the jid's
   // domain to be used.  We do the same for gmail.com and googlemail.com as the
   // returned CN matches the account domain in those cases.
-  std::string server_name = settings.server().HostAsURIString();
+  std::string server_name = settings.server().ToString();
   if (server_name == buzz::STR_TALK_GOOGLE_COM ||
       server_name == buzz::STR_TALKX_L_GOOGLE_COM ||
       server_name == buzz::STR_XMPP_GOOGLE_COM ||
diff --git a/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h b/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
index d96317e4..b4cc8fe 100644
--- a/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
+++ b/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
@@ -11,7 +11,7 @@
 #ifndef THIRD_PARTY_LIBJINGLE_XMPP_XMPP_XMPPCLIENTSETTINGS_H_
 #define THIRD_PARTY_LIBJINGLE_XMPP_XMPP_XMPPCLIENTSETTINGS_H_
 
-#include "third_party/webrtc/p2p/base/port.h"
+#include "net/base/host_port_pair.h"
 #include "third_party/libjingle_xmpp/xmpp/xmppengine.h"
 
 namespace buzz {
@@ -69,16 +69,14 @@
  public:
   XmppClientSettings() : protocol_(PROTO_TCP) {}
 
-  void set_server(const rtc::SocketAddress& server) {
-      server_ = server;
-  }
+  void set_server(const net::HostPortPair& server) { server_ = server; }
   void set_protocol(ProtocolType protocol) { protocol_ = protocol; }
 
-  const rtc::SocketAddress& server() const { return server_; }
+  const net::HostPortPair& server() const { return server_; }
   ProtocolType protocol() const { return protocol_; }
 
  private:
-  rtc::SocketAddress server_;
+  net::HostPortPair server_;
   ProtocolType protocol_;
 };
 
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 2828fbc..e0bdea3 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Thursday January 10 2019
+Date: Wednesday January 16 2019
 Branch: northernshoveler
-Commit: 759d1de9d0d22bab78bc4946998af79ff8b2044d
+Commit: 858fe955ae5a42a0006c974b0837df18b246986f
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index a193f45..0e00c3b 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  7
 #define VERSION_PATCH  0
-#define VERSION_EXTRA "1636-g759d1de9d"
+#define VERSION_EXTRA "1638-g858fe955a"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.7.0-1636-g759d1de9d"
-#define VERSION_STRING " v1.7.0-1636-g759d1de9d"
+#define VERSION_STRING_NOSP "v1.7.0-1638-g858fe955a"
+#define VERSION_STRING " v1.7.0-1638-g858fe955a"
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c
index 9b04fb1..1e8ce1b 100644
--- a/third_party/sqlite/amalgamation/sqlite3.c
+++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -71659,6 +71659,7 @@
   if( bPreserve ){
     if( !pPage->leaf
      || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
+     || pPage->nCell==1  /* See dbfuzz001.test for a test case */
     ){
       /* A b-tree rebalance will be required after deleting this entry.
       ** Save the cursor key.  */
@@ -122930,15 +122931,11 @@
     */
     Index *pIndex;
     pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
-    if( pIndex==0 ){
-      /* This can occur if there exists an index on a TEMP table which
-      ** has the same name as another index on a permanent index.  Since
-      ** the permanent table is hidden by the TEMP table, we can also
-      ** safely ignore the index on the permanent table.
-      */
-      /* Do Nothing */;
-    }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){
-      corruptSchema(pData, argv[0], "invalid rootpage");
+    if( pIndex==0
+     || sqlite3GetInt32(argv[1],&pIndex->tnum)==0
+     || pIndex->tnum<2
+    ){
+      corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
     }
   }
   return 0;
@@ -125475,7 +125472,12 @@
     regRow = pDest->iSdst;
   }else{
     regRowid = sqlite3GetTempReg(pParse);
-    regRow = sqlite3GetTempRange(pParse, nColumn);
+    if( eDest==SRT_EphemTab || eDest==SRT_Table ){
+      regRow = sqlite3GetTempReg(pParse);
+      nColumn = 0;
+    }else{
+      regRow = sqlite3GetTempRange(pParse, nColumn);
+    }
   }
   nKey = pOrderBy->nExpr - pSort->nOBSat;
   if( pSort->sortFlags & SORTFLAG_UseSorter ){
@@ -125555,6 +125557,7 @@
   switch( eDest ){
     case SRT_Table:
     case SRT_EphemTab: {
+      sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow);
       sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
       sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
@@ -220280,7 +220283,7 @@
 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
 
 /************** End of stmt.c ************************************************/
-#if __LINE__!=220283
+#if __LINE__!=220286
 #undef SQLITE_SOURCE_ID
 #define SQLITE_SOURCE_ID      "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2"
 #endif
diff --git a/third_party/sqlite/patches/0001-Modify-default-VFS-to-support-WebDatabase.patch b/third_party/sqlite/patches/0001-Modify-default-VFS-to-support-WebDatabase.patch
index da9c56b9..68bf0e6 100644
--- a/third_party/sqlite/patches/0001-Modify-default-VFS-to-support-WebDatabase.patch
+++ b/third_party/sqlite/patches/0001-Modify-default-VFS-to-support-WebDatabase.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: dumi <dumi@chromium.org>
 Date: Mon, 20 Jul 2009 23:40:51 +0000
-Subject: [PATCH 01/27] Modify default VFS to support WebDatabase.
+Subject: [PATCH 01/30] Modify default VFS to support WebDatabase.
 
 The renderer WebDatabase implementation needs to broker certain requests
 to the browser.  This modifies SQLite to allow monkey-patching the VFS
diff --git a/third_party/sqlite/patches/0002-Virtual-table-supporting-recovery-of-corrupted-datab.patch b/third_party/sqlite/patches/0002-Virtual-table-supporting-recovery-of-corrupted-datab.patch
index 6b35945..180bbb8 100644
--- a/third_party/sqlite/patches/0002-Virtual-table-supporting-recovery-of-corrupted-datab.patch
+++ b/third_party/sqlite/patches/0002-Virtual-table-supporting-recovery-of-corrupted-datab.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Scott Hess <shess@chromium.org>
 Date: Sat, 20 Jul 2013 11:42:21 -0700
-Subject: [PATCH 02/27] Virtual table supporting recovery of corrupted
+Subject: [PATCH 02/30] Virtual table supporting recovery of corrupted
  databases.
 
 "recover" implements a virtual table which uses the SQLite pager layer
diff --git a/third_party/sqlite/patches/0003-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch b/third_party/sqlite/patches/0003-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
index 4035639..e9765d5 100644
--- a/third_party/sqlite/patches/0003-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
+++ b/third_party/sqlite/patches/0003-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: "tc@google.com" <tc@google.com>
 Date: Tue, 6 Jan 2009 22:39:41 +0000
-Subject: [PATCH 03/27] Custom shell.c helpers to load Chromium's ICU data.
+Subject: [PATCH 03/30] Custom shell.c helpers to load Chromium's ICU data.
 
 History uses fts3 with an icu-based segmenter.  These changes allow building a
 sqlite3 binary for Linux or Windows which can read those files.
diff --git a/third_party/sqlite/patches/0004-fts3-Disable-fts3_tokenizer-and-fts4.patch b/third_party/sqlite/patches/0004-fts3-Disable-fts3_tokenizer-and-fts4.patch
index 5306a34..610d83a5 100644
--- a/third_party/sqlite/patches/0004-fts3-Disable-fts3_tokenizer-and-fts4.patch
+++ b/third_party/sqlite/patches/0004-fts3-Disable-fts3_tokenizer-and-fts4.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Scott Hess <shess@chromium.org>
 Date: Tue, 16 Dec 2014 13:02:27 -0800
-Subject: [PATCH 04/27] [fts3] Disable fts3_tokenizer and fts4.
+Subject: [PATCH 04/30] [fts3] Disable fts3_tokenizer and fts4.
 
 fts3_tokenizer allows a SQLite user to specify a pointer to call as a
 function, which has obvious sercurity implications.  Disable fts4 until
diff --git a/third_party/sqlite/patches/0005-fuchsia-Use-dot-file-locking-for-sqlite.patch b/third_party/sqlite/patches/0005-fuchsia-Use-dot-file-locking-for-sqlite.patch
index 652e7d4..f394040 100644
--- a/third_party/sqlite/patches/0005-fuchsia-Use-dot-file-locking-for-sqlite.patch
+++ b/third_party/sqlite/patches/0005-fuchsia-Use-dot-file-locking-for-sqlite.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Scott Graham <scottmg@chromium.org>
 Date: Mon, 11 Sep 2017 13:37:46 -0700
-Subject: [PATCH 05/27] fuchsia: Use dot-file locking for sqlite
+Subject: [PATCH 05/30] fuchsia: Use dot-file locking for sqlite
 
 ---
  third_party/sqlite/src/src/os_unix.c | 4 ++++
diff --git a/third_party/sqlite/patches/0006-Fix-dbfuzz2-for-Clusterfuzz.patch b/third_party/sqlite/patches/0006-Fix-dbfuzz2-for-Clusterfuzz.patch
index baa7e39..4d0f441 100644
--- a/third_party/sqlite/patches/0006-Fix-dbfuzz2-for-Clusterfuzz.patch
+++ b/third_party/sqlite/patches/0006-Fix-dbfuzz2-for-Clusterfuzz.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Matthew Denton <mpdenton@chromium.org>
 Date: Fri, 7 Dec 2018 14:49:36 -0700
-Subject: [PATCH 06/27] Fix dbfuzz2 for Clusterfuzz.
+Subject: [PATCH 06/30] Fix dbfuzz2 for Clusterfuzz.
 
 This backports https://www.sqlite.org/src/info/9ad796a8822f1b7e
 ---
diff --git a/third_party/sqlite/patches/0007-Fix-the-Makefile-so-that-it-honors-CFLAGS-when-build.patch b/third_party/sqlite/patches/0007-Fix-the-Makefile-so-that-it-honors-CFLAGS-when-build.patch
index 5b5d3cd5..b077f3e 100644
--- a/third_party/sqlite/patches/0007-Fix-the-Makefile-so-that-it-honors-CFLAGS-when-build.patch
+++ b/third_party/sqlite/patches/0007-Fix-the-Makefile-so-that-it-honors-CFLAGS-when-build.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Wed, 19 Dec 2018 18:22:15 -0800
-Subject: [PATCH 07/27] Fix the Makefile so that it honors CFLAGS when building
+Subject: [PATCH 07/30] Fix the Makefile so that it honors CFLAGS when building
  sessionfuzz.
 
 This backports https://www.sqlite.org/src/info/54231ac4ca506e6c
diff --git a/third_party/sqlite/patches/0008-Adjustments-to-the-page-cache-to-try-to-avoid-harmle.patch b/third_party/sqlite/patches/0008-Adjustments-to-the-page-cache-to-try-to-avoid-harmle.patch
index 7438a21..1f07c199 100644
--- a/third_party/sqlite/patches/0008-Adjustments-to-the-page-cache-to-try-to-avoid-harmle.patch
+++ b/third_party/sqlite/patches/0008-Adjustments-to-the-page-cache-to-try-to-avoid-harmle.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 02:01:53 -0800
-Subject: [PATCH 08/27] Adjustments to the page cache to try to avoid harmless
+Subject: [PATCH 08/30] Adjustments to the page cache to try to avoid harmless
  TSAN warnings
 
 This backports https://www.sqlite.org/src/info/383437be276719ac
diff --git a/third_party/sqlite/patches/0009-Remove-an-ALWAYS-from-a-branch-that-is-not-always-ta.patch b/third_party/sqlite/patches/0009-Remove-an-ALWAYS-from-a-branch-that-is-not-always-ta.patch
index d2278de..ee39f33d 100644
--- a/third_party/sqlite/patches/0009-Remove-an-ALWAYS-from-a-branch-that-is-not-always-ta.patch
+++ b/third_party/sqlite/patches/0009-Remove-an-ALWAYS-from-a-branch-that-is-not-always-ta.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 02:26:10 -0800
-Subject: [PATCH 09/27] Remove an ALWAYS() from a branch that is not always
+Subject: [PATCH 09/30] Remove an ALWAYS() from a branch that is not always
  taken.
 
 This backports https://www.sqlite.org/src/info/5c7dab
diff --git a/third_party/sqlite/patches/0010-Fix-a-problem-with-nested-CTEs-with-the-same-table.patch b/third_party/sqlite/patches/0010-Fix-a-problem-with-nested-CTEs-with-the-same-table.patch
index 95904fcf..744392f7 100644
--- a/third_party/sqlite/patches/0010-Fix-a-problem-with-nested-CTEs-with-the-same-table.patch
+++ b/third_party/sqlite/patches/0010-Fix-a-problem-with-nested-CTEs-with-the-same-table.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 02:30:28 -0800
-Subject: [PATCH 10/27] Fix a problem with nested CTEs with the same table.
+Subject: [PATCH 10/30] Fix a problem with nested CTEs with the same table.
 
 This backports https://www.sqlite.org/src/info/202dd033019dd274
 
diff --git a/third_party/sqlite/patches/0011-Fix-detection-of-self-referencing-rows-in-foreign-ke.patch b/third_party/sqlite/patches/0011-Fix-detection-of-self-referencing-rows-in-foreign-ke.patch
index 18b23616..23c44f0 100644
--- a/third_party/sqlite/patches/0011-Fix-detection-of-self-referencing-rows-in-foreign-ke.patch
+++ b/third_party/sqlite/patches/0011-Fix-detection-of-self-referencing-rows-in-foreign-ke.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 03:45:18 -0800
-Subject: [PATCH 11/27] Fix detection of self-referencing rows in foreign key
+Subject: [PATCH 11/30] Fix detection of self-referencing rows in foreign key
  processing.
 
 This backports https://www.sqlite.org/src/info/16fff05347f42fe9
diff --git a/third_party/sqlite/patches/0012-Fix-a-segfault-caused-by-using-the-RAISE-function-in.patch b/third_party/sqlite/patches/0012-Fix-a-segfault-caused-by-using-the-RAISE-function-in.patch
index a4f3982..66adfebb 100644
--- a/third_party/sqlite/patches/0012-Fix-a-segfault-caused-by-using-the-RAISE-function-in.patch
+++ b/third_party/sqlite/patches/0012-Fix-a-segfault-caused-by-using-the-RAISE-function-in.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 03:52:20 -0800
-Subject: [PATCH 12/27] Fix a segfault caused by using the RAISE function
+Subject: [PATCH 12/30] Fix a segfault caused by using the RAISE function
  incorrectly.
 
 This backports https://sqlite.org/src/info/ddf06db702761d66
diff --git a/third_party/sqlite/patches/0013-Fix-for-an-assert-that-could-be-false.patch b/third_party/sqlite/patches/0013-Fix-for-an-assert-that-could-be-false.patch
index 3de5408..7d817adb 100644
--- a/third_party/sqlite/patches/0013-Fix-for-an-assert-that-could-be-false.patch
+++ b/third_party/sqlite/patches/0013-Fix-for-an-assert-that-could-be-false.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 04:18:45 -0800
-Subject: [PATCH 13/27] Fix for an assert() that could be false.
+Subject: [PATCH 13/30] Fix for an assert() that could be false.
 
 This backports https://www.sqlite.org/src/info/23b62fb160d86dc9 /
 https://www.sqlite.org/src/info/bc891ac6b62fe7d9
diff --git a/third_party/sqlite/patches/0014-Fix-another-problem-found-by-Matthew-Denton-s-new-fu.patch b/third_party/sqlite/patches/0014-Fix-another-problem-found-by-Matthew-Denton-s-new-fu.patch
index 08c1522..922cb3a 100644
--- a/third_party/sqlite/patches/0014-Fix-another-problem-found-by-Matthew-Denton-s-new-fu.patch
+++ b/third_party/sqlite/patches/0014-Fix-another-problem-found-by-Matthew-Denton-s-new-fu.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 04:21:21 -0800
-Subject: [PATCH 14/27] Fix another problem found by Matthew Denton's new
+Subject: [PATCH 14/30] Fix another problem found by Matthew Denton's new
  fuzzer.
 
 This backports https://sqlite.org/src/info/2b690dbdffe144bd
diff --git a/third_party/sqlite/patches/0015-Report-a-new-corruption-case.patch b/third_party/sqlite/patches/0015-Report-a-new-corruption-case.patch
index b85a1cd..d3404f0c 100644
--- a/third_party/sqlite/patches/0015-Report-a-new-corruption-case.patch
+++ b/third_party/sqlite/patches/0015-Report-a-new-corruption-case.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 12:15:27 -0800
-Subject: [PATCH 15/27] Report a new corruption case.
+Subject: [PATCH 15/30] Report a new corruption case.
 
 This backports https://sqlite.org/src/info/cc42dd15100db28a
 
diff --git a/third_party/sqlite/patches/0016-Avoid-a-buffer-overread-in-ptrmapPutOvflPtr.patch b/third_party/sqlite/patches/0016-Avoid-a-buffer-overread-in-ptrmapPutOvflPtr.patch
index bedd24b..d104c33 100644
--- a/third_party/sqlite/patches/0016-Avoid-a-buffer-overread-in-ptrmapPutOvflPtr.patch
+++ b/third_party/sqlite/patches/0016-Avoid-a-buffer-overread-in-ptrmapPutOvflPtr.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 12:19:01 -0800
-Subject: [PATCH 16/27] Avoid a buffer overread in ptrmapPutOvflPtr().
+Subject: [PATCH 16/30] Avoid a buffer overread in ptrmapPutOvflPtr().
 
 This backports https://sqlite.org/src/info/f8b781cf41800e9f
 
diff --git a/third_party/sqlite/patches/0017-Improved-detection-of-cell-corruption-in-sqlite3Vdbe.patch b/third_party/sqlite/patches/0017-Improved-detection-of-cell-corruption-in-sqlite3Vdbe.patch
index fe65fcb4..176bd72 100644
--- a/third_party/sqlite/patches/0017-Improved-detection-of-cell-corruption-in-sqlite3Vdbe.patch
+++ b/third_party/sqlite/patches/0017-Improved-detection-of-cell-corruption-in-sqlite3Vdbe.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Fri, 11 Jan 2019 13:22:44 -0800
-Subject: [PATCH 17/27] Improved detection of cell corruption in
+Subject: [PATCH 17/30] Improved detection of cell corruption in
  sqlite3VdbeRecordCompareWithSkip().
 
 This backports https://www.sqlite.org/src/info/fa47f4c6589c431c
diff --git a/third_party/sqlite/patches/0018-Fix-a-segfault-in-fts3-prompted-by-a-corrupted-datab.patch b/third_party/sqlite/patches/0018-Fix-a-segfault-in-fts3-prompted-by-a-corrupted-datab.patch
index 54cd456..eae9807 100644
--- a/third_party/sqlite/patches/0018-Fix-a-segfault-in-fts3-prompted-by-a-corrupted-datab.patch
+++ b/third_party/sqlite/patches/0018-Fix-a-segfault-in-fts3-prompted-by-a-corrupted-datab.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sat, 12 Jan 2019 12:36:35 -0800
-Subject: [PATCH 18/27] Fix a segfault in fts3 prompted by a corrupted
+Subject: [PATCH 18/30] Fix a segfault in fts3 prompted by a corrupted
  database.
 
 This backports https://www.sqlite.org/src/info/2d7b1d1d41ff69d5
diff --git a/third_party/sqlite/patches/0019-Prevent-integer-overflow-from-leading-to-buffer-over.patch b/third_party/sqlite/patches/0019-Prevent-integer-overflow-from-leading-to-buffer-over.patch
index 028833ea..e0fbc7fe 100644
--- a/third_party/sqlite/patches/0019-Prevent-integer-overflow-from-leading-to-buffer-over.patch
+++ b/third_party/sqlite/patches/0019-Prevent-integer-overflow-from-leading-to-buffer-over.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sat, 12 Jan 2019 14:30:08 -0800
-Subject: [PATCH 19/27] Prevent integer overflow from leading to buffer
+Subject: [PATCH 19/30] Prevent integer overflow from leading to buffer
  overread inside of an assert().
 
 This backports https://www.sqlite.org/src/info/0f850a25d67a752f
diff --git a/third_party/sqlite/patches/0020-Add-extra-tests-for-database-corruption-inside-defra.patch b/third_party/sqlite/patches/0020-Add-extra-tests-for-database-corruption-inside-defra.patch
index 5788aba..194b2aa 100644
--- a/third_party/sqlite/patches/0020-Add-extra-tests-for-database-corruption-inside-defra.patch
+++ b/third_party/sqlite/patches/0020-Add-extra-tests-for-database-corruption-inside-defra.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sat, 12 Jan 2019 17:56:03 -0800
-Subject: [PATCH 20/27] Add extra tests for database corruption inside
+Subject: [PATCH 20/30] Add extra tests for database corruption inside
  defragmentPage().
 
 This backports https://sqlite.org/src/info/997b65117f8c12db
diff --git a/third_party/sqlite/patches/0021-Fix-an-off-by-one-error-on-a-Goto-in-the-code-genera.patch b/third_party/sqlite/patches/0021-Fix-an-off-by-one-error-on-a-Goto-in-the-code-genera.patch
index 9e33bad..a4463c2 100644
--- a/third_party/sqlite/patches/0021-Fix-an-off-by-one-error-on-a-Goto-in-the-code-genera.patch
+++ b/third_party/sqlite/patches/0021-Fix-an-off-by-one-error-on-a-Goto-in-the-code-genera.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sat, 12 Jan 2019 18:04:58 -0800
-Subject: [PATCH 21/27] Fix an off-by-one error on a Goto in the code
+Subject: [PATCH 21/30] Fix an off-by-one error on a Goto in the code
  generator.
 
 This backports https://www.sqlite.org/src/info/e35eb8776ed539af
diff --git a/third_party/sqlite/patches/0022-Fix-overread-on-corrupted-btree-key.patch b/third_party/sqlite/patches/0022-Fix-overread-on-corrupted-btree-key.patch
index 5bb4f44..4fab10a 100644
--- a/third_party/sqlite/patches/0022-Fix-overread-on-corrupted-btree-key.patch
+++ b/third_party/sqlite/patches/0022-Fix-overread-on-corrupted-btree-key.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 13 Jan 2019 13:36:26 -0800
-Subject: [PATCH 22/27] Fix overread on corrupted btree key.
+Subject: [PATCH 22/30] Fix overread on corrupted btree key.
 
 This backports https://sqlite.org/src/info/160b1e31c0f27257
 
diff --git a/third_party/sqlite/patches/0023-Avoid-buffer-overreads-on-corrupted-database-files.patch b/third_party/sqlite/patches/0023-Avoid-buffer-overreads-on-corrupted-database-files.patch
index 086fd61..f27fc16 100644
--- a/third_party/sqlite/patches/0023-Avoid-buffer-overreads-on-corrupted-database-files.patch
+++ b/third_party/sqlite/patches/0023-Avoid-buffer-overreads-on-corrupted-database-files.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 13 Jan 2019 15:17:27 -0800
-Subject: [PATCH 23/27] Avoid buffer overreads on corrupted database files.
+Subject: [PATCH 23/30] Avoid buffer overreads on corrupted database files.
 
 This backports https://sqlite.org/src/info/32754ca6f86da816
 
diff --git a/third_party/sqlite/patches/0024-Fix-integer-overflow-while-running-PRAGMA-integrity_.patch b/third_party/sqlite/patches/0024-Fix-integer-overflow-while-running-PRAGMA-integrity_.patch
index 88f13839..b7b23d3 100644
--- a/third_party/sqlite/patches/0024-Fix-integer-overflow-while-running-PRAGMA-integrity_.patch
+++ b/third_party/sqlite/patches/0024-Fix-integer-overflow-while-running-PRAGMA-integrity_.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 13 Jan 2019 16:12:08 -0800
-Subject: [PATCH 24/27] Fix integer overflow while running PRAGMA
+Subject: [PATCH 24/30] Fix integer overflow while running PRAGMA
  integrity_check.
 
 This backports https://sqlite.org/src/info/395599116d801324
diff --git a/third_party/sqlite/patches/0025-Improved-corruption-handling-while-balancing-pages.patch b/third_party/sqlite/patches/0025-Improved-corruption-handling-while-balancing-pages.patch
index 7a43e2e..15335fe 100644
--- a/third_party/sqlite/patches/0025-Improved-corruption-handling-while-balancing-pages.patch
+++ b/third_party/sqlite/patches/0025-Improved-corruption-handling-while-balancing-pages.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 13 Jan 2019 18:06:01 -0800
-Subject: [PATCH 25/27] Improved corruption handling while balancing pages.
+Subject: [PATCH 25/30] Improved corruption handling while balancing pages.
 
 This backports https://www.sqlite.org/src/info/35f04235c4775013
 
diff --git a/third_party/sqlite/patches/0026-Avoid-reading-off-the-front-of-a-page-buffer-when-ba.patch b/third_party/sqlite/patches/0026-Avoid-reading-off-the-front-of-a-page-buffer-when-ba.patch
index 94fd3845..71c2aa8 100644
--- a/third_party/sqlite/patches/0026-Avoid-reading-off-the-front-of-a-page-buffer-when-ba.patch
+++ b/third_party/sqlite/patches/0026-Avoid-reading-off-the-front-of-a-page-buffer-when-ba.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Sun, 13 Jan 2019 23:10:51 -0800
-Subject: [PATCH 26/27] Avoid reading off the front of a page buffer when
+Subject: [PATCH 26/30] Avoid reading off the front of a page buffer when
  balancing a corrupt btree.
 
 This backports https://www.sqlite.org/src/info/cb50509020d952fa
diff --git a/third_party/sqlite/patches/0027-Fix-MSAN-error-in-sqlite3VdbeRecordUnpack-on-a-corru.patch b/third_party/sqlite/patches/0027-Fix-MSAN-error-in-sqlite3VdbeRecordUnpack-on-a-corru.patch
index 571c4739..5ffbe4e 100644
--- a/third_party/sqlite/patches/0027-Fix-MSAN-error-in-sqlite3VdbeRecordUnpack-on-a-corru.patch
+++ b/third_party/sqlite/patches/0027-Fix-MSAN-error-in-sqlite3VdbeRecordUnpack-on-a-corru.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: Victor Costan <pwnall@chromium.org>
 Date: Mon, 14 Jan 2019 12:32:04 -0800
-Subject: [PATCH 27/27] Fix MSAN error in sqlite3VdbeRecordUnpack() on a
+Subject: [PATCH 27/30] Fix MSAN error in sqlite3VdbeRecordUnpack() on a
  corrupt record.
 
 This backports https://www.sqlite.org/src/info/ddc3697efd61830f
diff --git a/third_party/sqlite/patches/0028-Fix-deleting-a-B-tree-entry-in-a-corrupt-database.patch b/third_party/sqlite/patches/0028-Fix-deleting-a-B-tree-entry-in-a-corrupt-database.patch
new file mode 100644
index 0000000..46560cf1
--- /dev/null
+++ b/third_party/sqlite/patches/0028-Fix-deleting-a-B-tree-entry-in-a-corrupt-database.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Victor Costan <pwnall@chromium.org>
+Date: Tue, 15 Jan 2019 20:35:58 -0800
+Subject: [PATCH 28/30] Fix deleting a B-tree entry in a corrupt database.
+
+This backports https://sqlite.org/src/info/682053d1e603c21b
+
+Bug: 921894
+---
+ third_party/sqlite/src/src/btree.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/third_party/sqlite/src/src/btree.c b/third_party/sqlite/src/src/btree.c
+index 13342288934d..9b5745558514 100644
+--- a/third_party/sqlite/src/src/btree.c
++++ b/third_party/sqlite/src/src/btree.c
+@@ -8642,6 +8642,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
+   if( bPreserve ){
+     if( !pPage->leaf
+      || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
++     || pPage->nCell==1  /* See dbfuzz001.test for a test case */
+     ){
+       /* A b-tree rebalance will be required after deleting this entry.
+       ** Save the cursor key.  */
+--
+2.18.0
+
diff --git a/third_party/sqlite/patches/0029-Fix-sorting-results-with-SRT_EphemTab-and-a-LIMIT-cl.patch b/third_party/sqlite/patches/0029-Fix-sorting-results-with-SRT_EphemTab-and-a-LIMIT-cl.patch
new file mode 100644
index 0000000..bc33c1e
--- /dev/null
+++ b/third_party/sqlite/patches/0029-Fix-sorting-results-with-SRT_EphemTab-and-a-LIMIT-cl.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Victor Costan <pwnall@chromium.org>
+Date: Wed, 16 Jan 2019 12:46:56 -0800
+Subject: [PATCH 29/30] Fix sorting results with SRT_EphemTab and a LIMIT
+ clause.
+
+This backports https://www.sqlite.org/src/info/49fcde2f1f981ac0
+
+Bug: 922312
+---
+ third_party/sqlite/src/src/select.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/third_party/sqlite/src/src/select.c b/third_party/sqlite/src/src/select.c
+index fab4df68fa17..c68c1ddc643d 100644
+--- a/third_party/sqlite/src/src/select.c
++++ b/third_party/sqlite/src/src/select.c
+@@ -1457,8 +1457,13 @@ static void generateSortTail(
+     regRow = pDest->iSdst;
+   }else{
+     regRowid = sqlite3GetTempReg(pParse);
++    if( eDest==SRT_EphemTab || eDest==SRT_Table ){
++      regRow = sqlite3GetTempReg(pParse);
++      nColumn = 0;
++    }else{
+       regRow = sqlite3GetTempRange(pParse, nColumn);
+     }
++  }
+   nKey = pOrderBy->nExpr - pSort->nOBSat;
+   if( pSort->sortFlags & SORTFLAG_UseSorter ){
+     int regSortOut = ++pParse->nMem;
+@@ -1537,6 +1542,7 @@ static void generateSortTail(
+   switch( eDest ){
+     case SRT_Table:
+     case SRT_EphemTab: {
++      sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow);
+       sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
+       sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
+       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+--
+2.18.0
+
diff --git a/third_party/sqlite/patches/0030-Fix-detection-of-orphaned-and-malformed-autoindexes.patch b/third_party/sqlite/patches/0030-Fix-detection-of-orphaned-and-malformed-autoindexes.patch
new file mode 100644
index 0000000..5b107540
--- /dev/null
+++ b/third_party/sqlite/patches/0030-Fix-detection-of-orphaned-and-malformed-autoindexes.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Victor Costan <pwnall@chromium.org>
+Date: Wed, 16 Jan 2019 13:07:12 -0800
+Subject: [PATCH 30/30] Fix detection of orphaned and malformed autoindexes.
+
+This backports https://sqlite.org/src/info/10f9e39d6ed2413f
+
+Bug: 922213
+---
+ third_party/sqlite/src/src/prepare.c | 14 +++++---------
+ 1 file changed, 5 insertions(+), 9 deletions(-)
+
+diff --git a/third_party/sqlite/src/src/prepare.c b/third_party/sqlite/src/src/prepare.c
+index fe098cfa092e..cdf6f65bb115 100644
+--- a/third_party/sqlite/src/src/prepare.c
++++ b/third_party/sqlite/src/src/prepare.c
+@@ -118,15 +118,11 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
+     */
+     Index *pIndex;
+     pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
+-    if( pIndex==0 ){
+-      /* This can occur if there exists an index on a TEMP table which
+-      ** has the same name as another index on a permanent index.  Since
+-      ** the permanent table is hidden by the TEMP table, we can also
+-      ** safely ignore the index on the permanent table.
+-      */
+-      /* Do Nothing */;
+-    }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){
+-      corruptSchema(pData, argv[0], "invalid rootpage");
++    if( pIndex==0
++     || sqlite3GetInt32(argv[1],&pIndex->tnum)==0
++     || pIndex->tnum<2
++    ){
++      corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
+     }
+   }
+   return 0;
+--
+2.18.0
+
diff --git a/third_party/sqlite/src/src/btree.c b/third_party/sqlite/src/src/btree.c
index 13342288..9b574555 100644
--- a/third_party/sqlite/src/src/btree.c
+++ b/third_party/sqlite/src/src/btree.c
@@ -8642,6 +8642,7 @@
   if( bPreserve ){
     if( !pPage->leaf
      || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
+     || pPage->nCell==1  /* See dbfuzz001.test for a test case */
     ){
       /* A b-tree rebalance will be required after deleting this entry.
       ** Save the cursor key.  */
diff --git a/third_party/sqlite/src/src/prepare.c b/third_party/sqlite/src/src/prepare.c
index fe098cfa..cdf6f65 100644
--- a/third_party/sqlite/src/src/prepare.c
+++ b/third_party/sqlite/src/src/prepare.c
@@ -118,15 +118,11 @@
     */
     Index *pIndex;
     pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
-    if( pIndex==0 ){
-      /* This can occur if there exists an index on a TEMP table which
-      ** has the same name as another index on a permanent index.  Since
-      ** the permanent table is hidden by the TEMP table, we can also
-      ** safely ignore the index on the permanent table.
-      */
-      /* Do Nothing */;
-    }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){
-      corruptSchema(pData, argv[0], "invalid rootpage");
+    if( pIndex==0
+     || sqlite3GetInt32(argv[1],&pIndex->tnum)==0
+     || pIndex->tnum<2
+    ){
+      corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
     }
   }
   return 0;
diff --git a/third_party/sqlite/src/src/select.c b/third_party/sqlite/src/src/select.c
index fab4df68..c68c1dd 100644
--- a/third_party/sqlite/src/src/select.c
+++ b/third_party/sqlite/src/src/select.c
@@ -1457,7 +1457,12 @@
     regRow = pDest->iSdst;
   }else{
     regRowid = sqlite3GetTempReg(pParse);
-    regRow = sqlite3GetTempRange(pParse, nColumn);
+    if( eDest==SRT_EphemTab || eDest==SRT_Table ){
+      regRow = sqlite3GetTempReg(pParse);
+      nColumn = 0;
+    }else{
+      regRow = sqlite3GetTempRange(pParse, nColumn);
+    }
   }
   nKey = pOrderBy->nExpr - pSort->nOBSat;
   if( pSort->sortFlags & SORTFLAG_UseSorter ){
@@ -1537,6 +1542,7 @@
   switch( eDest ){
     case SRT_Table:
     case SRT_EphemTab: {
+      sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow);
       sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
       sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn
index 463e0b1..b72da6c 100644
--- a/third_party/wayland-protocols/BUILD.gn
+++ b/third_party/wayland-protocols/BUILD.gn
@@ -72,6 +72,12 @@
   ]
 }
 
+wayland_protocol("relative_pointer_protocol") {
+  sources = [
+    "src/unstable/relative-pointer/relative-pointer-unstable-v1.xml",
+  ]
+}
+
 wayland_protocol("keyboard_configuration_protocol") {
   sources = [
     "unstable/keyboard/keyboard-configuration-unstable-v1.xml",
diff --git a/third_party/webrtc_overrides/rtc_base/logging.cc b/third_party/webrtc_overrides/rtc_base/logging.cc
index fac4004..a4699aa11 100644
--- a/third_party/webrtc_overrides/rtc_base/logging.cc
+++ b/third_party/webrtc_overrides/rtc_base/logging.cc
@@ -18,7 +18,7 @@
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/threading/platform_thread.h"
-#include "third_party/webrtc/rtc_base/stringutils.h"
+#include "third_party/webrtc/rtc_base/string_utils.h"
 
 // This needs to be included after base/logging.h.
 #include "third_party/webrtc_overrides/rtc_base/diagnostic_logging.h"
diff --git a/third_party/webrtc_overrides/rtc_base/task_queue.cc b/third_party/webrtc_overrides/rtc_base/task_queue.cc
index c569be8..6b054902 100644
--- a/third_party/webrtc_overrides/rtc_base/task_queue.cc
+++ b/third_party/webrtc_overrides/rtc_base/task_queue.cc
@@ -17,8 +17,8 @@
 #include "base/task/post_task.h"
 #include "base/threading/thread_local.h"
 #include "build/build_config.h"
-#include "third_party/webrtc/rtc_base/refcount.h"
-#include "third_party/webrtc/rtc_base/refcountedobject.h"
+#include "third_party/webrtc/rtc_base/ref_count.h"
+#include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
 using base::WaitableEvent;
 
diff --git a/tools/android/roll/android_deps/build.gradle b/tools/android/roll/android_deps/build.gradle
index 89b2daab..e1913126 100644
--- a/tools/android/roll/android_deps/build.gradle
+++ b/tools/android/roll/android_deps/build.gradle
@@ -77,7 +77,7 @@
     compile "com.google.protobuf:protobuf-lite:3.0.1"
 
     // ARCore - needed for WebXR implementation on Android
-    compile "com.google.ar:core:1.5.0"
+    compile "com.google.ar:core:1.6.0"
 
 }
 
diff --git a/tools/android/roll/update_support_library.py b/tools/android/roll/update_support_library.py
deleted file mode 100755
index 07c42f2..0000000
--- a/tools/android/roll/update_support_library.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-'''
-Updates the Android support repository (m2repository).
-'''
-
-import argparse
-import os
-import logging
-import sys
-
-_SRC_DIR = os.path.abspath(os.path.join(
-    os.path.dirname(__file__), '..', '..', '..'))
-sys.path.append(os.path.join(_SRC_DIR, 'build', 'android'))
-
-from pylib.constants import host_paths
-from pylib.utils import logging_utils
-from pylib.utils import maven_downloader
-
-SUPPORT_LIB_REPO = os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party',
-                                'android_tools', 'sdk', 'extras', 'android',
-                                'm2repository')
-
-
-def main():
-  parser = argparse.ArgumentParser(description='Updates the Android support '
-                                   'repository in third_party/android_tools')
-  parser.add_argument('--target-repo',
-                      help='Maven repo where the library will be installed.',
-                      default=SUPPORT_LIB_REPO)
-  parser.add_argument('--debug',
-                      help='Debug mode, synchronous and with extra logging.',
-                      action='store_true')
-  args = parser.parse_args()
-
-  if (args.debug):
-    logging.basicConfig(level=logging.DEBUG)
-  else:
-    logging.basicConfig(level=logging.INFO)
-  logging_utils.ColorStreamHandler.MakeDefault()
-
-  maven_downloader.MavenDownloader(args.debug).Install(args.target_repo, [
-      'android.arch.core:common:1.0.0:jar',
-      'android.arch.lifecycle:common:1.0.0:jar',
-      'android.arch.lifecycle:runtime:1.0.0:aar',
-      'com.android.support:animated-vector-drawable:27.0.0:aar',
-      'com.android.support:appcompat-v7:27.0.0:aar',
-      'com.android.support:cardview-v7:27.0.0:aar',
-      'com.android.support:design:27.0.0:aar',
-      'com.android.support:gridlayout-v7:27.0.0:aar',
-      'com.android.support:leanback-v17:27.0.0:aar',
-      'com.android.support:mediarouter-v7:27.0.0:aar',
-      'com.android.support:palette-v7:27.0.0:aar',
-      'com.android.support:preference-leanback-v17:27.0.0:aar',
-      'com.android.support:preference-v14:27.0.0:aar',
-      'com.android.support:preference-v7:27.0.0:aar',
-      'com.android.support:recyclerview-v7:27.0.0:aar',
-      'com.android.support:support-annotations:27.0.0:jar',
-      'com.android.support:support-compat:27.0.0:aar',
-      'com.android.support:support-core-ui:27.0.0:aar',
-      'com.android.support:support-core-utils:27.0.0:aar',
-      'com.android.support:support-fragment:27.0.0:aar',
-      'com.android.support:support-media-compat:27.0.0:aar',
-      'com.android.support:support-v13:27.0.0:aar',
-      'com.android.support:support-vector-drawable:27.0.0:aar',
-      'com.android.support:transition:27.0.0:aar',
-  ], include_poms=True)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/tools/android/tracing/profile-chrome-startup.sh b/tools/android/tracing/profile-chrome-startup.sh
index 30a619e..a30eaa5 100755
--- a/tools/android/tracing/profile-chrome-startup.sh
+++ b/tools/android/tracing/profile-chrome-startup.sh
@@ -88,6 +88,7 @@
 trace_time=10
 cold=false
 url=
+atrace_buffer_size=
 atrace_categories=
 killg=false
 repeat=
@@ -119,6 +120,9 @@
         --atrace=*)
         atrace_categories="${i#*=}"
         ;;
+        --atrace-buffer-size*)
+        atrace_buffer_size="${i#*=}"
+        ;;
         --killg)
         killg=true
         ;;
@@ -230,6 +234,11 @@
     output_tag="$output_tag-${atrace_categories//,/_}"
 fi
 
+if [ ! -z "$atrace_buffer_size" ]; then
+    profile_options="$profile_options --atrace-buffer-size=$atrace_buffer_size"
+    output_tag="$output_tag-${atrace_buffer_size}"
+fi
+
 if [ $cold = true ]; then
     profile_options="$profile_options --cold"
     output_tag="$output_tag-cold"
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py
index cf9011d..e2a559e5 100755
--- a/tools/clang/scripts/package.py
+++ b/tools/clang/scripts/package.py
@@ -284,6 +284,9 @@
         # Fuzzing instrumentation (-fsanitize=fuzzer-no-link).
         'lib/clang/*/lib/linux/libclang_rt.fuzzer_no_main-x86_64.a',
 
+        # HWASAN Android runtime.
+        'lib/clang/*/lib/linux/libclang_rt.hwasan-aarch64-android.so',
+
         # MemorySanitizer C runtime (pure C won't link with *_cxx).
         'lib/clang/*/lib/linux/libclang_rt.msan-x86_64.a',
         'lib/clang/*/lib/linux/libclang_rt.msan-x86_64.a.syms',
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 7cc44f14..f59c55a 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=2
+CLANG_SUB_REVISION=3
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
@@ -509,6 +509,14 @@
 
   print 'Locally building Clang %s...' % PACKAGE_VERSION
 
+  if use_head_revision:
+    # TODO(hans): Trunk version was updated; remove after the next roll.
+    # Remove the old lib dir.
+    old_lib_dir = os.path.join(LLVM_BUILD_DIR, 'lib', 'clang', '8.0.0')
+    if (os.path.isdir(old_lib_dir)):
+      print 'Removing old lib dir: %s' % old_lib_dir
+      RmTree(old_lib_dir)
+
   AddCMakeToPath(args)
   AddGnuWinToPath()
 
@@ -840,6 +848,8 @@
             'lib/linux/libclang_rt.ubsan_standalone-{0}-android.so',
             'lib/linux/libclang_rt.profile-{0}-android.a',
         ]
+      if target_arch == 'aarch64':
+        libs_want += ['lib/linux/libclang_rt.hwasan-{0}-android.so']
       libs_want = [lib.format(target_arch) for lib in libs_want]
       RunCommand(['ninja'] + libs_want)
 
@@ -980,7 +990,7 @@
   if use_head_revision:
     # TODO(hans): Trunk version was updated; remove after the next roll.
     global VERSION
-    VERSION = '8.0.0'
+    VERSION = '9.0.0'
 
   if args.verify_version and args.verify_version != VERSION:
     print 'VERSION is %s but --verify-version argument was %s, exiting.' % (
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 260c1ef..5a25c9dd 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -6095,6 +6095,13 @@
   <description>The user triggered a used event in in-product help.</description>
 </action>
 
+<action name="InProductHelp.Promos.BubbleClicked">
+  <owner>collinbaker@chromium.org</owner>
+  <description>
+    The user clicked on an in-product help promo bubble.
+  </description>
+</action>
+
 <action name="InProductHelp.ShouldTriggerHelpUI.IPH" not_user_triggered="true">
   <owner>dtrainor@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
@@ -19782,6 +19789,15 @@
   </description>
 </action>
 
+<action name="TabContextMenu_AddToExistingGroup">
+  <owner>bsep@google.com</owner>
+  <owner>tbergquist@google.com</owner>
+  <description>
+    User selected an entry in the Add to existing group submenu from the tab
+    context menu.
+  </description>
+</action>
+
 <action name="TabContextMenu_AddToNewGroup">
   <owner>bsep@google.com</owner>
   <owner>tbergquist@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bbd4a8e..a52abd1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13049,7 +13049,7 @@
   <int value="18" label="Password entry: No screenlock state handler"/>
   <int value="19" label="Password entry: Phone locked and tx power too high"/>
   <int value="20" label="Password entry: Forced re-auth"/>
-  <int value="21" label="Password entry: Required for login"/>
+  <int value="21" label="Password entry: Sign-in with Smart Lock disabled"/>
 </enum>
 
 <enum name="EasyUnlockBluetoothType">
@@ -21295,6 +21295,7 @@
   <int value="2728" label="ElementTimingExplicitlyRequested"/>
   <int value="2729" label="V8HTMLMediaElement_CaptureStream_Method"/>
   <int value="2730" label="QuirkyLineBoxBackgroundSize"/>
+  <int value="2731" label="DirectlyCompositedImage"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -29879,14 +29880,18 @@
 
 <enum name="LinuxPasswordsMigrationToEncryptionStatus">
   <int value="0" label="Not attempted"/>
-  <int value="1" label="Failed"/>
+  <int value="1" label="(DEPRECATED) Failed"/>
   <int value="2" label="Copied everything"/>
   <int value="3" label="The standard login database is encrypted"/>
   <int value="4" label="The migration is about to be attempted"/>
   <int value="5" label="No access to the native backend"/>
-  <int value="6" label="Could not create or write into the temporary file"/>
+  <int value="6"
+      label="(DEPRECATED) Could not create or write into the temporary file"/>
   <int value="7" label="Could not read from the native backend"/>
   <int value="8" label="Could not replace old database"/>
+  <int value="9" label="Could not initialise temporary database"/>
+  <int value="10" label="Could not reset temporary database"/>
+  <int value="11" label="Could not write into temporary database"/>
 </enum>
 
 <enum name="LinuxSandboxStatus">
@@ -30260,6 +30265,7 @@
   <int value="-2113705745"
       label="CrossOriginMediaPlaybackRequiresUserGesture:enabled"/>
   <int value="-2108564200" label="AutofillUpstream:disabled"/>
+  <int value="-2104950596" label="HandwritingGesture:enabled"/>
   <int value="-2101682955" label="EnableNotificationIndicator:enabled"/>
   <int value="-2099457894" label="Mash:enabled"/>
   <int value="-2099035488" label="enable-data-reduction-proxy-bypass-warning"/>
@@ -31227,6 +31233,7 @@
   <int value="-488779992" label="blink-settings"/>
   <int value="-482259889" label="MacMDDownloadShelf:disabled"/>
   <int value="-478462945" label="enable-ephemeral-apps"/>
+  <int value="-477101783" label="HandwritingGesture:disabled"/>
   <int value="-475049740" label="disable-vr-shell"/>
   <int value="-474806100" label="DataReductionProxyMainMenu:enabled"/>
   <int value="-474322576" label="disable-quick-unlock-pin"/>
@@ -39699,6 +39706,11 @@
   </int>
 </enum>
 
+<enum name="OSXFastUserSwitchEvent">
+  <int value="0" label="User Became Active"/>
+  <int value="1" label="User Became Inactive"/>
+</enum>
+
 <enum name="OSXFilesystem">
   <int value="0" label="Unknown"/>
   <int value="1" label="Other"/>
@@ -43235,6 +43247,10 @@
     Failed to finish processing same version previously so skipping to avoid
     potential crash loop.
   </int>
+  <int value="3" label="Skipped hints processing">
+    Skipped processing the previews hints, likely because the hint cache already
+    contains the same version.
+  </int>
 </enum>
 
 <enum name="PreviewsServerLitePageBlacklistReason">
@@ -45380,6 +45396,12 @@
   <int value="99" label="IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE"/>
 </enum>
 
+<enum name="ReopenTabPromoStepAtDismissal">
+  <int value="0" label="Promo bubble shown"/>
+  <int value="1" label="App menu opened"/>
+  <int value="2" label="Tab reopened"/>
+</enum>
+
 <enum name="ReportingHeaderEndpointGroupOutcome">
   <obsolete>
     Moved to NetReportingHeaderEndpointGroupOutcome.
@@ -45732,6 +45754,13 @@
   <int value="3" label="Load"/>
 </enum>
 
+<enum name="Rollback_OobeRestoreResult">
+  <int value="0" label="Succeeded"/>
+  <int value="1" label="Stage 1 Failure"/>
+  <int value="2" label="Stage 2 Failure"/>
+  <int value="3" label="Stage 3 Failure"/>
+</enum>
+
 <enum name="RSAKeyUsage">
   <int value="0" label="Not an RSA key"/>
   <int value="1" label="OK (no extension)"/>
@@ -48509,7 +48538,7 @@
   <int value="14" label="No screenlock state handler"/>
   <int value="15" label="Phone locked and RSSI too low"/>
   <int value="16" label="Forced Reauthentication"/>
-  <int value="17" label="Required for login"/>
+  <int value="17" label="Sign-in with Smart Lock disabled"/>
   <int value="18" label="Phone not lockable"/>
 </enum>
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 25bad95..370038a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -16713,6 +16713,18 @@
   </summary>
 </histogram>
 
+<histogram name="ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration"
+    units="ms">
+  <owner>gangwu@chromium.org</owner>
+  <owner>fgorski@chromium.org</owner>
+  <summary>
+    Android: How long the content suggestion loading spinner is shown. This is
+    tracked based on when the spinner is enabled in the UI, not how long it is
+    actually visible on screen. Depending on the screen size, the used value
+    could be bigger.
+  </summary>
+</histogram>
+
 <histogram name="ContentSuggestions.Feed.Image.FetchResult"
     enum="FeedImageFetchResult" expires_after="2019-10-01">
   <owner>wylieb@chromium.org</owner>
@@ -16969,6 +16981,10 @@
 
 <histogram name="ContentSuggestions.FetchPendingSpinner.VisibleDuration"
     units="ms">
+  <obsolete>
+    Deprecated in favor of
+    ContentSuggestions.Feed.FetchPendingSpinner.VisibleDuration.
+  </obsolete>
   <owner>dgn@chromium.org</owner>
   <owner>ntp-dev+metrics@chromium.org</owner>
   <summary>
@@ -28586,16 +28602,14 @@
 </histogram>
 
 <histogram name="Event.Latency.HitTest" units="microseconds">
-  <obsolete>
-    Deprecated 01/2019 due to lack of usage.
-  </obsolete>
-  <owner>dtapuska@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
+  <owner>schenney@chromium.org</owner>
   <summary>
     Duration of a non-resurive hitTest operation. The hit test is non-recursive
     when the AllowChildFrameContent flag is clear. See also
     Event.Latency.HitTestRecusrive.
 
-    Team: input-dev@chromium.org.
+    Team: paint-dev@chromium.org.
 
     Warning: This metric may include reports from clients with low-resolution
     clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
@@ -28606,15 +28620,13 @@
 </histogram>
 
 <histogram name="Event.Latency.HitTestRecursive" units="microseconds">
-  <obsolete>
-    Deprecated 01/2019 due to lack of usage.
-  </obsolete>
-  <owner>dtapuska@chromium.org</owner>
+  <owner>paint-dev@chromium.org</owner>
+  <owner>schenney@chromium.org</owner>
   <summary>
     Duration of a recursive hitTest operation. The hit test is recursive when
     the AllowChildFrameContent flag is set. See also Event.Latency.HitTest.
 
-    Team: input-dev@chromium.org.
+    Team: paint-dev@chromium.org.
 
     Warning: This metric may include reports from clients with low-resolution
     clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
@@ -41076,6 +41088,19 @@
   </summary>
 </histogram>
 
+<histogram name="InProductHelp.Promos.IPH_ReopenTab.DismissedAt"
+    enum="ReopenTabPromoStepAtDismissal" expires_after="M74">
+  <owner>collinbaker@chromium.org</owner>
+  <summary>
+    Last step of in-product help (IPH) for reopening tabs that user followed
+    before finishing the IPH flow or dismissing it early. Recorded when the IPH
+    flow is dismissed, which is at one of three possible times: when the promo
+    bubble times out, when the menu is closed after the user opens it for IPH,
+    or when the user opens the last closed tab. The last is considered a
+    success.
+  </summary>
+</histogram>
+
 <histogram name="InProductHelp.ShouldTriggerHelpUI" enum="TriggerHelpUIResult">
   <owner>nyquist@chromium.org</owner>
   <owner>xingliu@chromium.org</owner>
@@ -41843,7 +41868,8 @@
 
 <histogram name="InstantExtended.CacheableNTPLoad"
     enum="InstantExtended_CacheableNTPLoad">
-  <owner>beaudoin@chromium.org</owner>
+  <owner>kmilka@chromium.org</owner>
+  <owner>ramyan@chromium.org</owner>
   <summary>
     Records a histogram for how often the Cacheable NTP fails to load.
   </summary>
@@ -43446,6 +43472,18 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.WKWebViewClobberedHistory" enum="Boolean"
+    expires_after="2019-11-01">
+  <owner>danyao@chromium.org</owner>
+  <summary>
+    Measures the number of main frame navigations in slim navigation manager
+    that are affected by a WKWebView bug that corrupts back/forward history
+    (http://crbug.com/887497). This is recorded during WKWebView's
+    decidePolicyForNavigationAction callback. True means the bug is triggered.
+    False means the bug is not triggered.
+  </summary>
+</histogram>
+
 <histogram name="IOS.WKWebViewFinishBeforeCommit" enum="Boolean">
   <owner>danyao@chromium.org</owner>
   <summary>
@@ -45445,7 +45483,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.StreamBrokerDisconnectReason"
-    enum="AudioInputStreamDisconnectReason" expires_after="2019-02-01">
+    enum="AudioInputStreamDisconnectReason" expires_after="2019-08-01">
   <owner>jonasolsson@chromium.org</owner>
   <owner>maxmorin@chromium.org</owner>
   <owner>olka@chromium.org</owner>
@@ -46095,7 +46133,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.StreamBrokerDisconnectReason"
-    enum="AudioOutputStreamDisconnectReason" expires_after="2019-02-01">
+    enum="AudioOutputStreamDisconnectReason" expires_after="2019-08-01">
   <obsolete>
     Deprecated 09/2018, and replaced by
     Media.Audio.Render.StreamBrokerDisconnectReason2.
@@ -52642,7 +52680,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     [iOS] When the OS sends a memory warning and the app evicts a tab, this
     histogram records the time since the evicted tab was active.
@@ -52660,7 +52697,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     [iOS] When the OS sends a memory warning and the app protects a tab, this
     histogram records the time since the protected tab was active.
@@ -52820,6 +52856,16 @@
   </summary>
 </histogram>
 
+<histogram name="Mobile.SystemNotification.Content.Click"
+    enum="SystemNotificationType">
+  <owner>xingliu@chromium.org</owner>
+  <summary>
+    Records when the type of notification when the user clicks the body of
+    Android notification. This does not include clicks on notification action
+    buttons.
+  </summary>
+</histogram>
+
 <histogram name="Mobile.SystemNotification.Shown" enum="SystemNotificationType">
   <owner>dtrainor@chromium.org</owner>
   <summary>
@@ -69084,7 +69130,6 @@
   <obsolete>
     Deprecated 2016-02 (and not recorded for some time before that).
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <owner>justincohen@chromium.org</owner>
   <owner>newt@chromium.org</owner>
   <summary>
@@ -69117,7 +69162,6 @@
   <obsolete>
     Deprecated 2016-02 (and not recorded for some time before that).
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <owner>justincohen@chromium.org</owner>
   <owner>newt@chromium.org</owner>
   <summary>
@@ -69163,7 +69207,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of tiles for which we relied on external tiles as a fallback
     because a local screenshot was not available to be used as a thumbnail.
@@ -69178,7 +69221,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of external tiles that are displayed on the NTP. External tiles
     are those for which the visuals are handled by the page itself, not by the
@@ -69192,7 +69234,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of tiles for which we displayed a gray tile with the domain name
     as a fallback because a local screenshot was not available to be used as a
@@ -69206,7 +69247,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of tiles for which no thumbnail was specified, but a domain was
     so we displayed a gray tile with the domain name in it. Recorded before
@@ -69219,7 +69259,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The total number of times the user hovered the mouse over Most Visited tile
     or title elements before changing focus away from the NTP, be it by
@@ -69245,7 +69284,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of thumbnails for which a local screenshot was not available so
     we were not able to display them on the Most Visited section of the NTP.
@@ -69258,7 +69296,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     The number of tiles for which we attempted to use a local screenshot as a
     thumbnail. Recorded before reloading the suggestions, navigating to a URL,
@@ -69897,7 +69934,6 @@
   <obsolete>
     Deprecated 2016-07.
   </obsolete>
-  <owner>beaudoin@chromium.org</owner>
   <summary>
     Indicate, for each impression of the New Tab Page, whether the suggestions
     were obtained from the client or server. Recorded before changing focus away
@@ -74790,6 +74826,14 @@
   <summary>Events seen by the OSX NSException swizzle.</summary>
 </histogram>
 
+<histogram name="OSX.FastUserSwitch" enum="OSXFastUserSwitchEvent"
+    expires_after="2019-12-31">
+  <owner>avi@chromium.org</owner>
+  <owner>rsesek@chromium.org</owner>
+  <owner>mark@chromium.org</owner>
+  <summary>Records the Fast User Switching events that occur.</summary>
+</histogram>
+
 <histogram name="OSX.Fullscreen.Enter" enum="OSXFullscreenParameters">
   <obsolete>
     Deprecated as of Chrome 40. See OSX.Fullscreen.Enter.Style,
@@ -75421,6 +75465,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.Aggregate.Network" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.Aggregate.Network.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of the resources loaded for all of the ad frames on the
@@ -75435,6 +75483,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.Aggregate.PercentNetwork" units="%">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.Aggregate.PercentNetwork.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The percentage of bytes loaded for all ad frames that were loaded over the
@@ -75449,6 +75501,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.Aggregate.Total" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.Aggregate.Total.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of the resources loaded for all of the ad frames on the
@@ -75463,6 +75519,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.PerFrame.Network" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.PerFrame.Network.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of the resources loaded for an ad frame from the network.
@@ -75476,6 +75536,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.PerFrame.PercentNetwork" units="%">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.PerFrame.PercentNetwork.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The percentage of bytes loaded for a single ad frame that were loaded over
@@ -75490,6 +75554,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.AdFrames.PerFrame.Total" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.AdFrames.PerFrame.Total.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of the resources loaded for an ad frame.
@@ -75503,6 +75571,9 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.FullPage.Network" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with PageLoad.Cients.Ads.Bytes.FullPage.Network.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of all of the page's resources that loaded over the
@@ -75515,6 +75586,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.FullPage.Network.PercentAds" units="%">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.FullPage.Network.PercentAds.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The percentage of bytes loaded for the page (from the network) that came
@@ -75529,6 +75604,9 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.FullPage.Total" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with PageLoad.Cients.Ads.Bytes.FullPage.Total.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of all of the page's resources.
@@ -75540,6 +75618,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.FullPage.Total.PercentAds" units="%">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.FullPage.Total.PercentAds.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The percentage of bytes loaded for the page that came from resource loads in
@@ -75554,6 +75636,10 @@
 </histogram>
 
 <histogram name="PageLoad.Bytes.NonAdFrames.Aggregate.Total" units="KB">
+  <obsolete>
+    Deprecated 01/19. Replaced with
+    PageLoad.Cients.Ads.Bytes.NonAdFrames.Aggregate.Total.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The size (in KB) of all of the page's resources except for those loaded in
@@ -75629,6 +75715,20 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="PageLoad.Clients.Ads.Bytes"
+    expires_after="2020-01-05">
+  <owner>jkarlin@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <summary>
+    Only recorded if the page has at least one identified ad frame. Recorded in
+    PageLoadMetrics when the page is destroyed. Bytes include all bytes used by
+    the network to load a resource and response body bytes for cached resources.
+    An ad frame consists of the identified ad frame and all of its children
+    (which may also be ads, but are counted as part of the ancestor ad frame).
+    Includes resources that did not finish loading.
+  </summary>
+</histogram>
+
 <histogram
     name="PageLoad.Clients.Ads.Google.FrameCounts.MainFrameParent.AdFrames"
     units="Frames">
@@ -75746,9 +75846,9 @@
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
-    Total number of bytes that went towards loading ad resources for a single
-    page over it's entire lifetime. This includes resources that did not finish
-    or were canceled. Only recorded for pages with non-zero ad bytes.
+    Total number of network bytes that went towards loading ad resources for a
+    single page over it's entire lifetime. This includes resources that did not
+    finish or were canceled. Only recorded for pages with non-zero ad bytes.
   </summary>
 </histogram>
 
@@ -75765,11 +75865,14 @@
 
 <histogram name="PageLoad.Clients.Ads.Resources.Bytes.Total" units="KB"
     expires_after="2019-09-05">
+  <obsolete>
+    Deprecated 01/19. Replaced with PageLoad.Clients.Ads.Bytes.FullPage.Network.
+  </obsolete>
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
-    Total number of bytes that were used to load resources on the page. This
-    includes resources that did not finish loading or were canceled. Only
+    Total number of network bytes that were used to load resources on the page.
+    This includes resources that did not finish loading or were canceled. Only
     recorded for pages with non-zero ad bytes.
   </summary>
 </histogram>
@@ -75779,10 +75882,10 @@
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <summary>
-    Total number of bytes that were used to load resources that did not finish
-    loading on the page (e.g. ongoing video). This includes resource loads that
-    were canceled or resource loads that were ongoing when the page was
-    destroyed. Only recorded for pages with non-zero ad bytes.
+    Total number of network bytes that were used to load resources that did not
+    finish loading on the page (e.g. ongoing video). This includes resource
+    loads that were canceled or resource loads that were ongoing when the page
+    was destroyed. Only recorded for pages with non-zero ad bytes.
   </summary>
 </histogram>
 
@@ -79246,6 +79349,15 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.LinuxBackendMigration.AttemptResult"
+    enum="LinuxPasswordsMigrationToEncryptionStatus" expires_after="2019-05-01">
+  <owner>cfroussios@chromium.org</owner>
+  <summary>
+    Whether the password store has been migrated to an encrypted login database.
+    This is recorded at the end of a migration attempt.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.LinuxBackendMigration.TimeIntoEncrypted"
     expires_after="2019-05-01">
   <owner>cfroussios@chromium.org</owner>
@@ -94014,6 +94126,13 @@
   </summary>
 </histogram>
 
+<histogram name="Rollback.OobeRestoreResult" enum="Rollback_OobeRestoreResult">
+  <owner>zentaro@chromium.org</owner>
+  <summary>
+    The result when attempting to restore data during a Chrome OS rollback.
+  </summary>
+</histogram>
+
 <histogram name="SadTab.Created" enum="SadTabKind">
   <obsolete>
     Replaced with Tabs.SadTab.* in R20.
@@ -114210,19 +114329,20 @@
 </histogram>
 
 <histogram name="Tab.AgeUponRestoreFromColdStart" units="minutes">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Age (time since the last display in previous sessions) of a tab being
-    restored due to the first tab switch after the browser cold start, recorded
-    upon such restore. When the browser is started from cold, this metric is not
-    recorded for the foreground, automatically restored tab, so that the metric
-    tracks only the restores triggered by direct user decision to switch tabs.
+    [Android] Age (time since the last display in previous sessions) of a tab
+    being restored due to the first tab switch after the browser cold start,
+    recorded upon such restore. When the browser is started from cold, this
+    metric is not recorded for the foreground, automatically restored tab, so
+    that the metric tracks only the restores triggered by direct user decision
+    to switch tabs.
   </summary>
 </histogram>
 
-<histogram name="Tab.AndroidCrashUpload" enum="BooleanSuccess">
-  <owner>hzl@google.com</owner>
-  <summary>Count of upload success/failures by crash type.</summary>
+<histogram base="true" name="Tab.AndroidCrashUpload" enum="BooleanSuccess">
+  <owner>wnwen@chromium.org</owner>
+  <summary>[Android] Count of upload success/failures by crash type.</summary>
 </histogram>
 
 <histogram name="Tab.BackgroundLoadStatus" enum="TabBackgroundLoadStatus">
@@ -114262,7 +114382,7 @@
   </summary>
 </histogram>
 
-<histogram name="Tab.Close.UnloadTime" units="ms" expires_after="2018-08-30">
+<histogram name="Tab.Close.UnloadTime" units="ms">
   <obsolete>
     Removed 01/2019.
   </obsolete>
@@ -114331,7 +114451,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     [iOS] When switching to an evicted tab, this histogram records whether or
     not the tab had ever been active. For example, the tab was opened via
@@ -114343,8 +114462,8 @@
 <histogram name="Tab.ExternalApplicationOpened" enum="ExternalLauncherOption">
   <owner>mrefaat@chromium.org</owner>
   <summary>
-    Used on External App launcher Prompt to determine if the user clicked open
-    or cancel.
+    [iOS] Used on External App launcher Prompt to determine if the user clicked
+    open or cancel.
   </summary>
 </histogram>
 
@@ -114352,7 +114471,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     A count of form activity (e.g. fields selected, characters typed) in a tab.
     Recorded only for tabs that are evicted due to memory pressure and then
@@ -114363,8 +114481,9 @@
 <histogram name="Tab.HorizontalSizeClassUsed" enum="iOSSizeClassForReporting">
   <owner>lpromero@chromium.org</owner>
   <summary>
-    Used on iOS 9+ iPad to report the usage of Compact or Regular horizontal
-    size class. This is logged at startup and on each size class change.
+    [iOS] Used on iOS 9+ iPad to report the usage of Compact or Regular
+    horizontal size class. This is logged at startup and on each size class
+    change.
   </summary>
 </histogram>
 
@@ -114378,8 +114497,8 @@
 </histogram>
 
 <histogram name="Tab.NewTab" enum="NewTabType">
-  <owner>lliabraa@chromium.org</owner>
-  <owner>beaudoin@chromium.org</owner>
+  <owner>tbergquist@chromium.org</owner>
+  <owner>bsep@chromium.org</owner>
   <summary>
     Tracks the different ways users are opening new tabs. Does not apply to
     opening existing links or searches in a new tab, only to brand new empty
@@ -114389,8 +114508,8 @@
 </histogram>
 
 <histogram name="Tab.NewTabDOMContentLoaded" units="ms">
-  <owner>lliabraa@chromium.org</owner>
-  <owner>beaudoin@chromium.org</owner>
+  <owner>kmilka@chromium.org</owner>
+  <owner>ramyan@chromium.org</owner>
   <summary>
     The time for the new tab page to fire the &quot;DOMContentLoaded&quot;
     event.
@@ -114399,6 +114518,8 @@
 
 <histogram name="Tab.NewTabOnload" units="ms">
   <owner>treib@chromium.org</owner>
+  <owner>kmilka@chromium.org</owner>
+  <owner>ramyan@chromium.org</owner>
   <summary>
     The time for the new tab page to fire the &quot;load&quot; event. Note: This
     is usually recorded with a suffix (.Local/Google/Other). The base version is
@@ -114408,8 +114529,8 @@
 </histogram>
 
 <histogram name="Tab.NewTabScriptStart" units="ms">
-  <owner>lliabraa@chromium.org</owner>
-  <owner>beaudoin@chromium.org</owner>
+  <owner>kmilka@chromium.org</owner>
+  <owner>ramyan@chromium.org</owner>
   <summary>
     The time for the new tab page to start executing JavaScript.
   </summary>
@@ -114444,29 +114565,30 @@
     enum="iOSSizeClassForReporting">
   <owner>lpromero@chromium.org</owner>
   <summary>
-    Used on iOS 9+ iPad to report the usage of Compact or Regular horizontal
-    size class. Recorded on page load.
+    [iOS] Used on iOS 9+ iPad to report the usage of Compact or Regular
+    horizontal size class. Recorded on page load.
   </summary>
 </histogram>
 
 <histogram name="Tab.PageLoadInPortrait" enum="DeviceOrientation">
-  <owner>jif@chromium.org</owner>
-  <summary>The orientation of the device. Recorded on page load.</summary>
+  <owner>marq@chromium.org</owner>
+  <summary>[iOS] The orientation of the device. Recorded on page load.</summary>
 </histogram>
 
 <histogram name="Tab.PageLoadsSinceLastSwitchToEvictedTab">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    The number of page loads since the last switch to an evicted tab on Android.
-    This was sampled each time an evicted tab was reloaded.
+    [Android] The number of page loads since the last switch to an evicted tab
+    on Android. This was sampled each time an evicted tab was reloaded.
   </summary>
 </histogram>
 
 <histogram name="Tab.PerceivedRestoreTime" units="ms">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    User-perceived load time for a successful tab restore, measured from the
-    first time the user sees the tab being restored until the load completes.
+    [Android] User-perceived load time for a successful tab restore, measured
+    from the first time the user sees the tab being restored until the load
+    completes.
   </summary>
 </histogram>
 
@@ -114518,8 +114640,8 @@
 <histogram name="Tab.RendererCrashStatus" enum="TabRendererCrashStatus">
   <owner>jaekyun@chromium.org</owner>
   <summary>
-    The status of a tab and an application when a renderer crashes. This is
-    recorded only for Android when a renderer crashes.
+    [Android] The status of a tab and an application when a renderer crashes.
+    This is recorded only for Android when a renderer crashes.
   </summary>
 </histogram>
 
@@ -114527,9 +114649,9 @@
     enum="ProcessDetailedExitStatus">
   <owner>wnwen@chromium.org</owner>
   <summary>
-    Breakdown of renderer exit status for renderers that have strong bindings.
-    An extension of the counts in Tab.RendererExitStatus. Only recorded on
-    Android.
+    [Android] Breakdown of renderer exit status for renderers that have strong
+    bindings. An extension of the counts in Tab.RendererExitStatus. Only
+    recorded on Android.
   </summary>
 </histogram>
 
@@ -114537,8 +114659,8 @@
     enum="ProcessDetailedExitStatus">
   <owner>wnwen@chromium.org</owner>
   <summary>
-    Breakdown of renderer exit status for renderers that do not have strong
-    bindings. An extension of the counts in Tab.RendererExitStatus. Only
+    [Android] Breakdown of renderer exit status for renderers that do not have
+    strong bindings. An extension of the counts in Tab.RendererExitStatus. Only
     recorded on Android.
   </summary>
 </histogram>
@@ -114546,8 +114668,8 @@
 <histogram name="Tab.RendererExitStatus" enum="TabRendererExitStatus">
   <owner>wnwen@chromium.org</owner>
   <summary>
-    The status of a renderer when the browser notices that the process has
-    exited. Only recorded on Android.
+    [Android] The status of a renderer when the browser notices that the process
+    has exited. Only recorded on Android.
   </summary>
 </histogram>
 
@@ -114578,24 +114700,26 @@
 </histogram>
 
 <histogram name="Tab.RestoreResult" enum="TabRestoreResult">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
+  <owner>marq@chromium.org</owner>
   <summary>
-    When the browser restores a tab, whether the load was successful. Loads can
-    fail for instance when there is no connectivity.
+    [Android and iOS] When the browser restores a tab, whether the load was
+    successful. Loads can fail for instance when there is no connectivity.
   </summary>
 </histogram>
 
 <histogram name="Tab.RestoreTime" units="ms">
-  <owner>lliabraa@chromium.org</owner>
-  <summary>Load time for a successful tab restore.</summary>
+  <owner>dtrainor@chromium.org</owner>
+  <owner>marq@chromium.org</owner>
+  <summary>[Android and iOS] Load time for a successful tab restore.</summary>
 </histogram>
 
 <histogram name="Tab.RestoreUserPersistence" enum="TabRestoreUserAction">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    When the browser restores a tab, whether the user waits for completion of
-    the load or if the user gives up by switching to another tab or leaving
-    Chrome.
+    [Android] When the browser restores a tab, whether the user waits for
+    completion of the load or if the user gives up by switching to another tab
+    or leaving Chrome.
   </summary>
 </histogram>
 
@@ -114633,7 +114757,9 @@
 </histogram>
 
 <histogram name="Tab.StatusWhenDisplayed" enum="TabStatus">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     The status of a tab collected each time the tab is displayed on Android,
     including user switching to the tab and displays of newly created tabs, such
@@ -114642,29 +114768,33 @@
 </histogram>
 
 <histogram name="Tab.StatusWhenSwitchedBackToForeground" enum="TabStatus">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>marq@chromium.org</owner>
   <summary>
-    The status of a tab collected each time the user switches to it on mobile.
-    That does not include tabs being created at the time the user switches to
-    them, such as NTP or tabs opened to handle intents.
+    [Android and iOS] The status of a tab collected each time the user switches
+    to it on mobile. That does not include tabs being created at the time the
+    user switches to them, such as NTP or tabs opened to handle intents.
   </summary>
 </histogram>
 
 <histogram name="Tab.StatusWhenSwitchedBackToForegroundDataProxyEnabled"
     enum="TabStatus">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <owner>marq@chromium.org</owner>
   <summary>
-    The status of a tab collected each time the user switches to it on mobile
-    with the data reduction proxy enabled. This is populated identically, and in
-    addition to Tab.StatusWhenSwitchedBackToForeground for any given tab
-    switching event if the proxy is enabled.
+    [Android and iOS] The status of a tab collected each time the user switches
+    to it on mobile with the data reduction proxy enabled. This is populated
+    identically, and in addition to Tab.StatusWhenSwitchedBackToForeground for
+    any given tab switching event if the proxy is enabled.
   </summary>
 </histogram>
 
 <histogram name="Tab.SwitchedToForegroundAge" units="ms">
-  <owner>lliabraa@chromium.org</owner>
-  <summary>Age (in ms) when the tab was switched to foreground.</summary>
+  <owner>dtrainor@chromium.org</owner>
+  <summary>
+    [Android] Age (in ms) when the tab was switched to foreground.
+  </summary>
 </histogram>
 
 <histogram name="Tab.SwitchedToForegroundLaunchedWithURL"
@@ -114672,7 +114802,6 @@
   <obsolete>
     Deprecated as of 04/2014.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     Each time a tab is brought to the foreground, this histogram indicates if
     chrome was launched without an URL (i.e., from the launcher), or with an URL
@@ -114684,14 +114813,15 @@
   <obsolete>
     Deprecated as of 04/2014.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     Rank in MRU order (0 being first) when the tab was switched to foreground.
   </summary>
 </histogram>
 
 <histogram name="Tab.SwitchedToForegroundNumTabs">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>Count of all tabs when a tab is switched.</summary>
 </histogram>
 
@@ -114700,7 +114830,6 @@
   <obsolete>
     Deprecated as of 04/2014.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     Each time a tab is brought to the foreground, this histogram indicates if
     this is the first viewing of the tab since Chrome was put into foreground,
@@ -114773,7 +114902,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     [iOS] When an existing tab becomes active, this histogram records the time
     since it was made inactive.
@@ -114784,7 +114912,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     [iOS] When an evicted tab becomes active, this histogram records the time
     since it was made inactive.
@@ -114795,7 +114922,6 @@
   <obsolete>
     Deprecated as of 10/2016.
   </obsolete>
-  <owner>lliabraa@chromium.org</owner>
   <summary>
     Time elapsed since there was form activity (e.g. fields selected, characters
     typed) in a tab. Recorded only for tabs that are evicted due to memory
@@ -114823,9 +114949,9 @@
 <histogram name="Tab.TotalTabCount.BeforeLeavingApp" units="tabs">
   <owner>jaekyun@chromium.org</owner>
   <summary>
-    The total count of tabs which were kept while Chrome process is in the
-    foreground. This is recorded only for Android right before Chrome process
-    goes into the background.
+    [Android] The total count of tabs which were kept while Chrome process is in
+    the foreground. This is recorded only for Android right before Chrome
+    process goes into the background.
   </summary>
 </histogram>
 
@@ -115590,15 +115716,15 @@
 </histogram>
 
 <histogram name="Tabs.CountAtResume" units="tabs">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>marq@chromium.org</owner>
   <summary>
-    The number of tabs open when the app comes out of the background.
+    [iOS] The number of tabs open when the app comes out of the background.
   </summary>
 </histogram>
 
 <histogram name="Tabs.CountAtStartup" units="tabs">
-  <owner>lliabraa@chromium.org</owner>
-  <summary>The number of tabs open at cold launch.</summary>
+  <owner>marq@chromium.org</owner>
+  <summary>[Android and iOS] The number of tabs open at cold launch.</summary>
 </histogram>
 
 <histogram name="Tabs.Discard.DiscardCount">
@@ -115804,10 +115930,10 @@
 </histogram>
 
 <histogram name="Tabs.ForegroundTabAgeAtStartup" units="minutes">
-  <owner>lliabraa@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Age (time since the last display in previous sessions) of the foreground tab
-    being restored on the browser cold start.
+    [Android] Age (time since the last display in previous sessions) of the
+    foreground tab being restored on the browser cold start.
   </summary>
 </histogram>
 
@@ -116006,7 +116132,9 @@
 
 <histogram name="Tabs.SpeculativeRestoreApplicability"
     enum="SpeculativeRestoreApplicability">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Applicability of speculative tab restore, recorded every time a tab is
     switched. This allows to estimate the fraction of tab restores experienced
@@ -116018,7 +116146,9 @@
 
 <histogram name="Tabs.SpeculativeRestorePredictionAccuracy.SideSwipe"
     enum="SpeculativeRestorePredictionAccuracy">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Accuracy of the tab switch predictions made when the user begins the side
     swipe gesture.
@@ -116027,7 +116157,9 @@
 
 <histogram name="Tabs.SpeculativeRestorePredictionAccuracy.TabSwitcher"
     enum="SpeculativeRestorePredictionAccuracy">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Accuracy of the tab switch predictions made when the user enters the tab
     switcher.
@@ -116036,14 +116168,18 @@
 
 <histogram name="Tabs.SpeculativeRestoreTargetStatus"
     enum="SpeculativeRestoreTabStatus">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Status of a tab recorded when the tab is targeted with speculative restore.
   </summary>
 </histogram>
 
 <histogram name="Tabs.SpeculativeRestoreTimeAhead.SideSwipe" units="ms">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Time between starting the speculative load and actual tab switch for correct
     speculative load predictions made when the user begins the side swipe
@@ -116052,7 +116188,9 @@
 </histogram>
 
 <histogram name="Tabs.SpeculativeRestoreTimeAhead.TabSwitcher" units="ms">
-  <owner>lliabraa@chromium.org</owner>
+  <obsolete>
+    No longer logged
+  </obsolete>
   <summary>
     Time between starting the speculative load and actual tab switch for correct
     speculative load predictions made when the user enters the tab switcher.
@@ -116112,30 +116250,34 @@
 </histogram>
 
 <histogram name="Tabs.SwitchFromCloseLatency" units="ms">
-  <owner>simonb@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Time between the event that closes a tab and the start of rendering.
+    [Android] Time between the event that closes a tab and the start of
+    rendering.
   </summary>
 </histogram>
 
 <histogram name="Tabs.SwitchFromExitLatency" units="ms">
-  <owner>simonb@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Time between the event that exits an app and the start of rendering.
+    [Android] Time between the event that exits an app and the start of
+    rendering.
   </summary>
 </histogram>
 
 <histogram name="Tabs.SwitchFromNewLatency" units="ms">
-  <owner>simonb@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Time between the event that creates a tab and the start of rendering.
+    [Android] Time between the event that creates a tab and the start of
+    rendering.
   </summary>
 </histogram>
 
 <histogram name="Tabs.SwitchFromUserLatency" units="ms">
-  <owner>simonb@chromium.org</owner>
+  <owner>dtrainor@chromium.org</owner>
   <summary>
-    Time between the event that selects a tab and the start of rendering.
+    [Android] Time between the event that selects a tab and the start of
+    rendering.
   </summary>
 </histogram>
 
@@ -129609,7 +129751,7 @@
     </obsolete>
   </suffix>
   <suffix name="Clients.Ads.SubresourceFilter"
-      label="Includes only ads discovered by the SubResourceFilter."/>
+      label="Includes only ads discovered by the SubresourceFilter."/>
   <affected-histogram name="PageLoad.Bytes.AdFrames.Aggregate.Network"/>
   <affected-histogram name="PageLoad.Bytes.AdFrames.Aggregate.PercentNetwork"/>
   <affected-histogram name="PageLoad.Bytes.AdFrames.Aggregate.Total"/>
@@ -129628,6 +129770,43 @@
   <affected-histogram name="PageLoad.FrameCounts.AnyParentFrame.AdFrames"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AdsPageLoadMetricsBytes" separator=".">
+  <suffix base="true" name="AdFrames.Aggregate"
+      label="Includes resources loaded across all ad frames on a page."/>
+  <suffix base="true" name="AdFrames.PerFrame"
+      label="Includes resources loaded for a single ad frame."/>
+  <suffix base="true" name="FullPage"
+      label="Includes all resources loaded by the page."/>
+  <suffix name="NonAdFrames.Aggregate.Total"
+      label="The size (in KB) of all of the page's resources except for those
+             loaded in ad frames."/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="AdsPageLoadMetricsNetworkBytes" separator=".">
+  <suffix name="Network"
+      label="The size (in KB) of all of resources that loaded over the
+             network."/>
+  <suffix name="Total" label="The size (in KB) of the resources loaded."/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate"/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame"/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.FullPage"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="AdsPageLoadMetricsPercentAds" separator=".">
+  <suffix name="PercentAds"
+      label="The percentage of bytes loaded from within ad frames."/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.FullPage.Network"/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.FullPage.Total"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="AdsPageLoadMetricsPercentNetwork" separator=".">
+  <suffix name="PercentNetwork"
+      label="The percentage of bytes that were loaded over the network."/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate"/>
+  <affected-histogram name="PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AffiliationDummyData" separator=".">
   <suffix name="OnStartup"
       label="with the dummy data being requested shortly after start-up"/>
@@ -133978,6 +134157,7 @@
   <suffix name="ImageManager" label="Databases for ImageManager"/>
   <suffix name="OfflinePageMetadataStore"
       label="Databases for OfflinePageMetadataStore"/>
+  <suffix name="PreviewsHintCacheStore" label="Databases for Previews Hints"/>
   <suffix name="UsageReportsBufferBackend"
       label="The result of the first attempt to open the usage reports buffer
              backend database."/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 13264185..8a66f63 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3363,6 +3363,9 @@
   </metric>
   <metric
       name="Experimental.PaintTiming.NavigationToLargestImagePaint.BeforeUserInput">
+    <obsolete>
+      Deprecated 01/2019
+    </obsolete>
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the page first paints the largest image within
@@ -3380,6 +3383,9 @@
   </metric>
   <metric
       name="Experimental.PaintTiming.NavigationToLargestTextPaint.BeforeUserInput">
+    <obsolete>
+      Deprecated 01/2019
+    </obsolete>
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the page first paints the largest text within
diff --git a/tools/perf/page_sets/system_health/accessibility_stories.py b/tools/perf/page_sets/system_health/accessibility_stories.py
index 3fdda64..6ebff46 100644
--- a/tools/perf/page_sets/system_health/accessibility_stories.py
+++ b/tools/perf/page_sets/system_health/accessibility_stories.py
@@ -17,10 +17,15 @@
   ABSTRACT_STORY = True
   SUPPORTED_PLATFORMS = platforms.DESKTOP_ONLY
 
-  def __init__(self, story_set, take_memory_measurement):
+  def __init__(self, story_set, take_memory_measurement,
+    extra_browser_args=None):
+    FORCE_A11Y = '--force-renderer-accessibility'
+    if extra_browser_args is None:
+      extra_browser_args = [FORCE_A11Y]
+    else:
+      extra_browser_args.append(FORCE_A11Y)
     super(_AccessibilityStory, self).__init__(
-        story_set, take_memory_measurement,
-        extra_browser_args=['--force-renderer-accessibility'])
+        story_set, take_memory_measurement, extra_browser_args)
 
 
 class AccessibilityScrollingCodeSearchStory(_AccessibilityStory):
@@ -81,6 +86,14 @@
   TAGS = [story_tags.ACCESSIBILITY, story_tags.KEYBOARD_INPUT,
           story_tags.YEAR_2016]
 
+  # TODO(yoichio): Remove this flags when YouTube finish V0 migration.
+  # crbug.com/911943.
+  def __init__(self, story_set, take_memory_measurement):
+    super(AccessibilityYouTubeHomepageStory, self).__init__(
+        story_set, take_memory_measurement,
+        extra_browser_args=[
+          '--enable-blink-features=HTMLImports,CustomElementsV0'])
+
   def RunNavigateSteps(self, action_runner):
     action_runner.Navigate('https://www.youtube.com/')
     action_runner.tab.WaitForDocumentReadyStateToBeComplete()
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 086688e..ce5f98e 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -636,6 +636,14 @@
   PLATFORM_SPECIFIC = True
   TAGS = [story_tags.JAVASCRIPT_HEAVY, story_tags.YEAR_2018]
 
+  # TODO(yoichio): Remove this flags when YouTube finish V0 migration.
+  # crbug.com/911943.
+  def __init__(self, story_set, take_memory_measurement):
+    super(YouTubeDesktopStory2018, self).__init__(
+        story_set, take_memory_measurement,
+        extra_browser_args=[
+          '--enable-blink-features=HTMLImports,CustomElementsV0'])
+
 
 class FacebookPhotosMobileStory(_MediaBrowsingStory):
   """Load a photo page from Rihanna's facebook page then navigate a few next
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index d80b9d3..ae1d74f 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -137,6 +137,7 @@
  <item id="http_server_error_response" hash_code="32197336" type="0" content_hash_code="61082230" os_list="linux,windows" file_path="net/server/http_server.cc"/>
  <item id="icon_cacher" hash_code="103133150" type="0" content_hash_code="116368348" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/>
  <item id="icon_catcher_get_large_icon" hash_code="44494884" type="0" content_hash_code="98262037" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/>
+ <item id="image_annotation" hash_code="107881858" type="0" content_hash_code="121106764" os_list="linux,windows" file_path="services/image_annotation/annotator.cc"/>
  <item id="indexed_db_internals_handler" hash_code="131180348" type="0" content_hash_code="59026406" os_list="linux,windows" file_path="content/browser/indexed_db/indexed_db_internals_ui.cc"/>
  <item id="interest_feed_send" hash_code="76717919" type="0" content_hash_code="34678180" os_list="linux,windows" file_path="components/feed/core/feed_networking_host.cc"/>
  <item id="intranet_redirect_detector" hash_code="21785164" type="0" content_hash_code="62025595" os_list="linux,windows" file_path="chrome/browser/intranet_redirect_detector.cc"/>
diff --git a/tools/web_dev_style/css_checker.py b/tools/web_dev_style/css_checker.py
index ec5680ad..da17bd8 100644
--- a/tools/web_dev_style/css_checker.py
+++ b/tools/web_dev_style/css_checker.py
@@ -13,6 +13,10 @@
 # TODO(dbeam): Real CSS parser? https://github.com/danbeam/css-py/tree/css3
 
 class CSSChecker(object):
+  DISABLE_PREFIX = 'csschecker-disable'
+  DISABLE_FORMAT = DISABLE_PREFIX + '(-[a-z]+)+ [a-z-]+(-[a-z-]+)*'
+  DISABLE_LINE = DISABLE_PREFIX + '-line'
+
   def __init__(self, input_api, output_api, file_filter=None):
     self.input_api = input_api
     self.output_api = output_api
@@ -31,7 +35,7 @@
     def _remove_all(s):
       s = _remove_grit(s)  # Must be done first.
       s = _remove_ats(s)
-      s = _remove_comments(s)
+      s = _remove_comments_except_for_disables(s)
       s = _remove_mixins_and_valid_vars(s)
       s = _remove_template_expressions(s)
       return s
@@ -47,8 +51,9 @@
           .*?}                    # stuff up to the first end curly }
           """, r'\1', s, flags=re.DOTALL | re.VERBOSE)
 
-    def _remove_comments(s):
-      return re.sub(r'/\*.*?\*/', '', s, flags=re.DOTALL)
+    def _remove_comments_except_for_disables(s):
+      return re.sub(r'/\*(?! %s \*/$).*?\*/' % self.DISABLE_FORMAT,'', s,
+                    flags=re.DOTALL | re.MULTILINE)
 
     def _remove_grit(s):
       return re.sub(r"""
@@ -63,6 +68,11 @@
       mixin_or_value = r'({.*?}|[^;}]+);?\s*'
       return re.sub(valid_vars + mixin_or_value, '', s, flags=re.DOTALL)
 
+    def _remove_disable(content, lstrip=False):
+      prefix_reg = ('\s*' if lstrip else '')
+      disable_reg = '/\* %s \*/' % self.DISABLE_FORMAT
+      return re.sub(prefix_reg + disable_reg, '', content, re.MULTILINE)
+
     def _remove_template_expressions(s):
       return re.sub(r'\$i18n(Raw)?{[^}]*}', '', s, flags=re.DOTALL)
 
@@ -79,8 +89,9 @@
     def alphabetize_props(contents):
       errors = []
       # TODO(dbeam): make this smart enough to detect issues in mixins.
+      strip_rule = lambda t: _remove_disable(t).strip()
       for rule in re.finditer(r'{(.*?)}', contents, re.DOTALL):
-        semis = map(lambda t: t.strip(), rule.group(1).split(';'))[:-1]
+        semis = map(strip_rule, rule.group(1).split(';'))[:-1]
         rules = filter(lambda r: ': ' in r, semis)
         props = map(lambda r: r[0:r.find(':')], rules)
         if props != sorted(props):
@@ -176,6 +187,7 @@
       return re.search('url\s*\(\s*["\']', line, re.IGNORECASE)
 
     def one_rule_per_line(line):
+      line = _remove_disable(line)
       one_rule_reg = re.compile(r"""
           [\w-](?<!data):  # a rule: but no data URIs
           (?!//)[^;]+;     # value; ignoring colons in protocols:// and };
@@ -285,9 +297,9 @@
         """, re.VERBOSE)
 
     def suggest_unprefixed_logical_axis(line):
-      preffix, prop = prefixed_logical_axis_reg.search(line).groups()
+      prefix, prop = prefixed_logical_axis_reg.search(line).groups()
       block_or_inline = 'block' if prop == 'height' else 'inline'
-      return ' (replace with %s)' % (preffix + block_or_inline + '-size')
+      return ' (replace with %s)' % (prefix + block_or_inline + '-size')
 
     def prefixed_logical_axis(line):
       return prefixed_logical_axis_reg.search(line)
@@ -313,6 +325,23 @@
     def prefixed_logical_side(line):
       return prefixed_logical_side_reg.search(line)
 
+    _LEFT_RIGHT_REG = '(?:(border|margin|padding)-|(text-align): )' \
+                      '(left|right)' \
+                      '(?:(-[a-z-^:]+):)?(?!.*/\* %s left-right \*/)' % \
+                      self.DISABLE_LINE
+
+    def start_end_instead_of_left_right(line):
+      return re.search(_LEFT_RIGHT_REG, line, re.IGNORECASE)
+
+    def suggest_start_end_from_left_right(line):
+      groups = re.search(_LEFT_RIGHT_REG, line, re.IGNORECASE).groups()
+      prop_start, text_align, left_right, prop_end = groups
+      start_end = {'left': 'start', 'right': 'end'}[left_right]
+      if text_align:
+        return ' (replace with text-align: %s)' % start_end
+      prop = '%s-inline-%s%s' % (prop_start, start_end, prop_end or '')
+      return ' (replace with %s)' % prop
+
     def zero_width_lengths(contents):
       hsl_reg = re.compile(r"""
           hsl\([^\)]*       # hsl(maybestuff
@@ -401,6 +430,13 @@
           'test': prefixed_logical_side,
           'after': suggest_unprefixed_logical_side,
         },
+        {
+          'desc': 'Use -start/end instead of -left/right ' \
+                  '(https://goo.gl/gQYY7z, add /* %s left-right */ to ' \
+                  'suppress)' % self.DISABLE_LINE,
+          'test': start_end_instead_of_left_right,
+          'after': suggest_start_end_from_left_right,
+        },
         { 'desc': 'Use "0" for zero-width lengths (i.e. 0px -> 0)',
           'test': zero_width_lengths,
           'multiline': True,
@@ -445,7 +481,7 @@
           lines = f[1].splitlines()
           for lnum, line in enumerate(lines):
             if check['test'](line):
-              error = '    ' + line.strip()
+              error = '    ' + _remove_disable(line, lstrip=True).strip()
               if 'after' in check:
                 error += check['after'](line)
               check_errors.append(error)
diff --git a/tools/web_dev_style/css_checker_test.py b/tools/web_dev_style/css_checker_test.py
index fa066195..a122afd 100755
--- a/tools/web_dev_style/css_checker_test.py
+++ b/tools/web_dev_style/css_checker_test.py
@@ -133,11 +133,11 @@
   def testCssAlphaWithLongerDashedProps(self):
     self.VerifyContentsProducesOutput("""
 div {
-  border-left: 5px;  /* A hopefully removed comment. */
+  border-inline-start: 5px;  /* A hopefully removed comment. */
   border: 5px solid red;
 }""", """
 - Alphabetize properties and list vendor specific (i.e. -webkit) above standard.
-    border-left: 5px;
+    border-inline-start: 5px;
     border: 5px solid red;""")
 
   def testCssAlphaWithVariables(self):
@@ -466,6 +466,24 @@
     -webkit-padding-end: 3px; (replace with padding-inline-end)
     -webkit-padding-start: 4px; (replace with padding-inline-start)""")
 
+  def testStartEndInsteadOfLeftRight(self):
+    self.VerifyContentsProducesOutput("""
+.inline-node {
+  --var-is-ignored-left: 10px;
+  --var-is-ignored-right: 10px;
+  border-left-color: black;
+  border-right: 1px solid blue;  /* csschecker-disable-line left-right */
+  margin-right: 5px;
+  padding-left: 10px;    /* csschecker-disable-line some-other-thing */
+  text-align: right;
+}""", """
+- Use -start/end instead of -left/right (https://goo.gl/gQYY7z, add /* csschecker-disable-line left-right */ to suppress)
+    border-left-color: black; (replace with border-inline-start-color)
+    margin-right: 5px; (replace with margin-inline-end)
+    padding-left: 10px; (replace with padding-inline-start)
+    text-align: right; (replace with text-align: end)
+""")
+
   def testCssZeroWidthLengths(self):
     self.VerifyContentsProducesOutput("""
 @-webkit-keyframe anim {
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index ffb5653..13e462df 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -1103,7 +1103,7 @@
         result += " autocomplete=" + value;
         break;
       case ax::mojom::StringAttribute::kChildTreeId:
-        result += " child_tree_id=" + value;
+        result += " child_tree_id=" + value.substr(0, 8);
         break;
       case ax::mojom::StringAttribute::kClassName:
         result += " class_name=" + value;
diff --git a/ui/accessibility/ax_tree_data.cc b/ui/accessibility/ax_tree_data.cc
index ec452ba..ee2a8e13 100644
--- a/ui/accessibility/ax_tree_data.cc
+++ b/ui/accessibility/ax_tree_data.cc
@@ -24,11 +24,11 @@
   std::string result;
 
   if (tree_id != AXTreeIDUnknown())
-    result += " tree_id=" + tree_id.ToString();
+    result += " tree_id=" + tree_id.ToString().substr(0, 8);
   if (parent_tree_id != AXTreeIDUnknown())
-    result += " parent_tree_id=" + parent_tree_id.ToString();
+    result += " parent_tree_id=" + parent_tree_id.ToString().substr(0, 8);
   if (focused_tree_id != AXTreeIDUnknown())
-    result += " focused_tree_id=" + focused_tree_id.ToString();
+    result += " focused_tree_id=" + focused_tree_id.ToString().substr(0, 8);
 
   if (!doctype.empty())
     result += " doctype=" + doctype;
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 3255be93..b55785e 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -265,12 +265,19 @@
 }
 
 static gint AXPlatformNodeAuraLinuxGetIndexInParent(AtkObject* atk_object) {
-  AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object);
-
-  if (!obj)
+  AtkObject* parent = atk_object_get_parent(atk_object);
+  if (!parent)
     return -1;
 
-  return obj->GetIndexInParent();
+  int n_children = atk_object_get_n_accessible_children(parent);
+  for (int i = 0; i < n_children; i++) {
+    AtkObject* child = atk_object_ref_accessible_child(parent, i);
+    g_object_unref(child);
+    if (child == atk_object)
+      return i;
+  }
+
+  return -1;
 }
 
 static AtkObject* AXPlatformNodeAuraLinuxGetParent(AtkObject* atk_object) {
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index d25ef60c..8356f56 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -306,9 +306,23 @@
     "java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java",
     "java/src/org/chromium/ui/modaldialog/ModalDialogManager.java",
     "java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java",
+    "java/src/org/chromium/ui/modelutil/ForwardingListObservable.java",
+    "java/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcp.java",
+    "java/src/org/chromium/ui/modelutil/ListModel.java",
+    "java/src/org/chromium/ui/modelutil/ListModelBase.java",
+    "java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java",
+    "java/src/org/chromium/ui/modelutil/ListObservable.java",
+    "java/src/org/chromium/ui/modelutil/ListObservableImpl.java",
+    "java/src/org/chromium/ui/modelutil/ModelListAdapter.java",
     "java/src/org/chromium/ui/modelutil/PropertyKey.java",
+    "java/src/org/chromium/ui/modelutil/PropertyListModel.java",
     "java/src/org/chromium/ui/modelutil/PropertyModel.java",
+    "java/src/org/chromium/ui/modelutil/PropertyModelChangeProcessor.java",
     "java/src/org/chromium/ui/modelutil/PropertyObservable.java",
+    "java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java",
+    "java/src/org/chromium/ui/modelutil/SimpleList.java",
+    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java",
+    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java",
     "java/src/org/chromium/ui/resources/HandleViewResources.java",
     "java/src/org/chromium/ui/resources/LayoutResource.java",
     "java/src/org/chromium/ui/resources/Resource.java",
@@ -351,6 +365,7 @@
     "//base:base_java",
     "//third_party/android_deps:android_support_annotations_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:android_support_v7_recyclerview_java",
   ]
   srcjar_deps = [
     ":java_enums_srcjar",
@@ -365,11 +380,13 @@
     "javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java",
     "javatests/src/org/chromium/ui/test/util/UiRestriction.java",
     "javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java",
+    "javatests/src/org/chromium/ui/test/util/modelutil/FakeViewProvider.java",
   ]
   deps = [
     ":ui_java",
     "//base:base_java",
     "//base:base_java_test_support",
+    "//third_party/junit",
   ]
 }
 
@@ -388,6 +405,9 @@
     "junit/src/org/chromium/ui/base/SelectFileDialogTest.java",
     "junit/src/org/chromium/ui/drawable/StateListDrawableBuilderTest.java",
     "junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java",
+    "junit/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcpTest.java",
+    "junit/src/org/chromium/ui/modelutil/PropertyModelTest.java",
+    "junit/src/org/chromium/ui/modelutil/SimpleListObservableTest.java",
     "junit/src/org/chromium/ui/text/SpanApplierTest.java",
     "junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java",
     "junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java",
@@ -397,6 +417,7 @@
   ]
   deps = [
     ":ui_java",
+    ":ui_java_test_support",
     ":ui_javatest_resources",
     "//base:base_java",
     "//base:base_java_test_support",
diff --git a/ui/android/java/res/values/ids.xml b/ui/android/java/res/values/ids.xml
new file mode 100644
index 0000000..efb2f14
--- /dev/null
+++ b/ui/android/java/res/values/ids.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Dropdown Popup Window -->
+    <item type="id" name="dropdown_popup_window" />
+
+    <!-- Model Utils -->
+    <item type="id" name="view_model" />
+    <item type="id" name="view_type" />
+</resources>
\ No newline at end of file
diff --git a/ui/android/java/res/values/values.xml b/ui/android/java/res/values/values.xml
index 26627ae..da39978 100644
--- a/ui/android/java/res/values/values.xml
+++ b/ui/android/java/res/values/values.xml
@@ -6,7 +6,6 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <item type="id" name="dropdown_popup_window" />
     <!-- Whether the device is a phone, tablet or large tablet. Used to determine which resources
          the OS loaded. -->
     <integer name="min_screen_width_bucket">1</integer>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java b/ui/android/java/src/org/chromium/ui/modelutil/ForwardingListObservable.java
similarity index 81%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ForwardingListObservable.java
index 510d203..9c92519a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ForwardingListObservable.java
@@ -1,19 +1,18 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-
 /**
  * Helper class for implementing a {@link ListObserver} that just forwards to its own observers.
  * @param <P> The payload type for partial updates, or {@link Void} if the class doesn't support
  *         partial updates.
  * TODO(bauerb): Remove this class if it turns out we can shortcut notifications
  */
-public class ForwardingListObservable<P> extends ListObservableImpl<P> implements ListObserver<P> {
+public class ForwardingListObservable<P>
+        extends ListObservableImpl<P> implements ListObservable.ListObserver<P> {
     @Override
     public void onItemRangeInserted(ListObservable source, int index, int count) {
         notifyItemRangeInserted(index, count);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java b/ui/android/java/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcp.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java
rename to ui/android/java/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcp.java
index 5180423..f24eb40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcp.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcp.java
@@ -2,14 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
 import org.chromium.ui.ViewProvider;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java b/ui/android/java/src/org/chromium/ui/modelutil/ListModel.java
similarity index 92%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ListModel.java
index 9929017..f1493a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModel.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListModel.java
@@ -1,7 +1,7 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 /**
  * Base class for a {@link ListObservable} containing a {@link SimpleList} of items.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java b/ui/android/java/src/org/chromium/ui/modelutil/ListModelBase.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ListModelBase.java
index fdafc09..94d5260 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelBase.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListModelBase.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.NonNull;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelChangeProcessor.java b/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelChangeProcessor.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
index a8d5c200..8685e54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListModelChangeProcessor.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java b/ui/android/java/src/org/chromium/ui/modelutil/ListObservable.java
similarity index 93%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ListObservable.java
index 316b0b6..7708534 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListObservable.java
@@ -1,7 +1,7 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
@@ -60,8 +60,7 @@
          * @param count The number of changed items.
          * @param payload Optional parameter, use {@code null} to identify a "full" update.
          */
-        default void onItemRangeChanged(ListObservable<P> source, int index, int count,
-                @Nullable P payload) {
-        }
+        default void onItemRangeChanged(
+                ListObservable<P> source, int index, int count, @Nullable P payload) {}
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java b/ui/android/java/src/org/chromium/ui/modelutil/ListObservableImpl.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ListObservableImpl.java
index b828ade..ba2784e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservableImpl.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListObservableImpl.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java b/ui/android/java/src/org/chromium/ui/modelutil/ModelListAdapter.java
similarity index 91%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java
rename to ui/android/java/src/org/chromium/ui/modelutil/ModelListAdapter.java
index d34b3b2a5..bb69ee0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ModelListAdapter.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ModelListAdapter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.content.Context;
 import android.util.Pair;
@@ -11,10 +11,7 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.R;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
@@ -40,7 +37,8 @@
 
     private final Context mContext;
     private final List<Pair<Integer, PropertyModel>> mSuggestionItems = new ArrayList<>();
-    private final SparseArray<Pair<ViewBuilder, ViewBinder>> mViewBuilderMap = new SparseArray<>();
+    private final SparseArray<Pair<ViewBuilder, PropertyModelChangeProcessor.ViewBinder>>
+            mViewBuilderMap = new SparseArray<>();
 
     public ModelListAdapter(Context context) {
         mContext = context;
@@ -77,8 +75,8 @@
      * @param builder A mechanism for building new views of the specified type.
      * @param binder A means of binding a model to the provided view.
      */
-    public <T extends View> void registerType(
-            int typeId, ViewBuilder<T> builder, ViewBinder<PropertyModel, T, PropertyKey> binder) {
+    public <T extends View> void registerType(int typeId, ViewBuilder<T> builder,
+            PropertyModelChangeProcessor.ViewBinder<PropertyModel, T, PropertyKey> binder) {
         assert mViewBuilderMap.valueAt(typeId) == null;
         mViewBuilderMap.put(typeId, new Pair<>(builder, binder));
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyListModel.java
similarity index 93%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
rename to ui/android/java/src/org/chromium/ui/modelutil/PropertyListModel.java
index 9e98b7ec..0221a12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyListModel.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyListModel.java
@@ -2,12 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 
-import org.chromium.ui.modelutil.PropertyObservable;
-
 import java.util.Collection;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModelChangeProcessor.java
similarity index 90%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
rename to ui/android/java/src/org/chromium/ui/modelutil/PropertyModelChangeProcessor.java
index 854aaad..69ac149f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModelChangeProcessor.java
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
-import org.chromium.ui.modelutil.PropertyObservable;
 import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
 
 /**
@@ -52,8 +51,8 @@
      * @param view The view to which data will be bound.
      * @param viewBinder A class that binds the model to the view.
      */
-    public static <M extends PropertyObservable<P>, V, P>
-    PropertyModelChangeProcessor<M, V, P> create(M model, V view, ViewBinder<M, V, P> viewBinder) {
+    public static <M extends PropertyObservable<P>, V, P> PropertyModelChangeProcessor<M, V, P>
+    create(M model, V view, ViewBinder<M, V, P> viewBinder) {
         return new PropertyModelChangeProcessor<>(model, view, viewBinder);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java b/ui/android/java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java
rename to ui/android/java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java
index 7fa7ce9..7449e2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
@@ -10,8 +10,6 @@
 import android.view.ViewGroup;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 
 import java.util.Collections;
 import java.util.List;
@@ -27,7 +25,7 @@
  * partial updates.
  */
 public class RecyclerViewAdapter<VH extends ViewHolder, P>
-        extends RecyclerView.Adapter<VH> implements ListObserver<P> {
+        extends RecyclerView.Adapter<VH> implements ListObservable.ListObserver<P> {
     /**
      * Delegate interface for the adapter.
      * @param <VH> The {@link ViewHolder} type for the {@link RecyclerView}.
@@ -69,15 +67,14 @@
          * @param viewHolder A view holder that will be recycled.
          * @see RecyclerView.Adapter#onViewRecycled(ViewHolder)
          */
-        default void
-            onViewRecycled(VH viewHolder) {}
+        default void onViewRecycled(VH viewHolder) {}
 
         /**
          * @param position The position of an item to be dismissed.
          * @return The set of item positions that should be dismissed simultaneously when dismissing
          *         the item at the given {@code position} (including the position itself), or an
          *         empty set if the item can't be dismissed.
-         * @see NewTabPageAdapter#getItemDismissalGroup
+         * @see org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter#getItemDismissalGroup
          */
         default Set<Integer> getItemDismissalGroup(int position) {
             return Collections.emptySet();
@@ -88,7 +85,7 @@
          * @param position The position of the item to be dismissed.
          * @param itemRemovedCallback Should be called with the title of the dismissed item, to
          * announce it for accessibility purposes.
-         * @see NewTabPageAdapter#dismissItem
+         * @see org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter#dismissItem
          */
         default void dismissItem(int position, Callback<String> itemRemovedCallback) {
             assert false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleList.java b/ui/android/java/src/org/chromium/ui/modelutil/SimpleList.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleList.java
rename to ui/android/java/src/org/chromium/ui/modelutil/SimpleList.java
index 9ab77c0a1..f3d9471 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleList.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/SimpleList.java
@@ -1,7 +1,7 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.NonNull;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java b/ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java
rename to ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java
index 080c63f..0d2a300 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcp.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java
@@ -1,7 +1,7 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java b/ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java
rename to ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java
index e2412ae..736cf82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/SimpleRecyclerViewMcpBase.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/modelutil/FakeViewProvider.java b/ui/android/javatests/src/org/chromium/ui/test/util/modelutil/FakeViewProvider.java
similarity index 96%
rename from chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/modelutil/FakeViewProvider.java
rename to ui/android/javatests/src/org/chromium/ui/test/util/modelutil/FakeViewProvider.java
index 6f37e32..e069db91 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/modelutil/FakeViewProvider.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/modelutil/FakeViewProvider.java
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
-package org.chromium.chrome.test.util.browser.modelutil;
+package org.chromium.ui.test.util.modelutil;
 
 import static org.junit.Assert.assertTrue;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java b/ui/android/junit/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcpTest.java
similarity index 94%
rename from chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java
rename to ui/android/junit/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcpTest.java
index d0b3082..b87dcc2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/LazyConstructionPropertyMcpTest.java
+++ b/ui/android/junit/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcpTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertFalse;
@@ -27,14 +27,11 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
-import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
-import org.chromium.ui.modelutil.PropertyObservable;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
+import org.chromium.ui.test.util.modelutil.FakeViewProvider;
 
 /**
  * Unit tests for LazyConstructionPropertyMcp.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java b/ui/android/junit/src/org/chromium/ui/modelutil/PropertyModelTest.java
similarity index 97%
rename from chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java
rename to ui/android/junit/src/org/chromium/ui/modelutil/PropertyModelTest.java
index 3747456..4f68cf4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java
+++ b/ui/android/junit/src/org/chromium/ui/modelutil/PropertyModelTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -17,8 +17,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java b/ui/android/junit/src/org/chromium/ui/modelutil/SimpleListObservableTest.java
similarity index 96%
rename from chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java
rename to ui/android/junit/src/org/chromium/ui/modelutil/SimpleListObservableTest.java
index 89ea3457..06dd009 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java
+++ b/ui/android/junit/src/org/chromium/ui/modelutil/SimpleListObservableTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.modelutil;
+package org.chromium.ui.modelutil;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
@@ -18,7 +18,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 
 /**
  * Basic test ensuring the {@link ListModel} notifies listeners properly.
diff --git a/ui/aura/mus/drag_drop_controller_mus.cc b/ui/aura/mus/drag_drop_controller_mus.cc
index 9670df44..719abb1 100644
--- a/ui/aura/mus/drag_drop_controller_mus.cc
+++ b/ui/aura/mus/drag_drop_controller_mus.cc
@@ -75,17 +75,19 @@
 
 uint32_t DragDropControllerMus::OnDragEnter(WindowMus* window,
                                             uint32_t event_flags,
-                                            const gfx::Point& screen_location,
+                                            const gfx::PointF& location_in_root,
+                                            const gfx::PointF& location,
                                             uint32_t effect_bitmask) {
-  return HandleDragEnterOrOver(window, event_flags, screen_location,
+  return HandleDragEnterOrOver(window, event_flags, location_in_root, location,
                                effect_bitmask, true);
 }
 
 uint32_t DragDropControllerMus::OnDragOver(WindowMus* window,
                                            uint32_t event_flags,
-                                           const gfx::Point& screen_location,
+                                           const gfx::PointF& location_in_root,
+                                           const gfx::PointF& location,
                                            uint32_t effect_bitmask) {
-  return HandleDragEnterOrOver(window, event_flags, screen_location,
+  return HandleDragEnterOrOver(window, event_flags, location_in_root, location,
                                effect_bitmask, false);
 }
 
@@ -101,7 +103,8 @@
 uint32_t DragDropControllerMus::OnCompleteDrop(
     WindowMus* window,
     uint32_t event_flags,
-    const gfx::Point& screen_location,
+    const gfx::PointF& location_in_root,
+    const gfx::PointF& location,
     uint32_t effect_bitmask) {
   if (drop_target_window_tracker_.windows().empty())
     return ws::mojom::kDropEffectNone;
@@ -109,8 +112,9 @@
   DCHECK(window);
   Window* current_target = drop_target_window_tracker_.Pop();
   DCHECK_EQ(window->GetWindow(), current_target);
-  std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(
-      window->GetWindow(), event_flags, screen_location, effect_bitmask);
+  std::unique_ptr<ui::DropTargetEvent> event =
+      CreateDropTargetEvent(window->GetWindow(), event_flags, location_in_root,
+                            location, effect_bitmask);
   return client::GetDragDropDelegate(current_target)->OnPerformDrop(*event);
 }
 
@@ -193,7 +197,8 @@
 uint32_t DragDropControllerMus::HandleDragEnterOrOver(
     WindowMus* window,
     uint32_t event_flags,
-    const gfx::Point& screen_location,
+    const gfx::PointF& location_in_root,
+    const gfx::PointF& location,
     uint32_t effect_bitmask,
     bool is_enter) {
   client::DragDropDelegate* drag_drop_delegate =
@@ -207,28 +212,26 @@
   }
   drop_target_window_tracker_.Add(window->GetWindow());
 
-  std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(
-      window->GetWindow(), event_flags, screen_location, effect_bitmask);
+  std::unique_ptr<ui::DropTargetEvent> event =
+      CreateDropTargetEvent(window->GetWindow(), event_flags, location_in_root,
+                            location, effect_bitmask);
   if (is_enter)
     drag_drop_delegate->OnDragEntered(*event);
   return drag_drop_delegate->OnDragUpdated(*event);
 }
 
 std::unique_ptr<ui::DropTargetEvent>
-DragDropControllerMus::CreateDropTargetEvent(Window* window,
-                                             uint32_t event_flags,
-                                             const gfx::Point& screen_location,
-                                             uint32_t effect_bitmask) {
-  DCHECK(window->GetHost());
-  gfx::Point root_location = screen_location;
-  window->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
-  gfx::PointF location(root_location);
-  Window::ConvertPointToTarget(window->GetRootWindow(), window, &location);
+DragDropControllerMus::CreateDropTargetEvent(
+    Window* window,
+    uint32_t event_flags,
+    const gfx::PointF& location_in_root,
+    const gfx::PointF& location,
+    uint32_t effect_bitmask) {
   std::unique_ptr<ui::DropTargetEvent> event =
       std::make_unique<ui::DropTargetEvent>(
           current_drag_state_ ? current_drag_state_->drag_data
                               : *(os_exchange_data_.get()),
-          location, gfx::PointF(root_location), effect_bitmask);
+          location, location_in_root, effect_bitmask);
   event->set_flags(event_flags);
   ui::Event::DispatcherApi(event.get()).set_target(window);
   return event;
diff --git a/ui/aura/mus/drag_drop_controller_mus.h b/ui/aura/mus/drag_drop_controller_mus.h
index 0bcb808a..cc95f60 100644
--- a/ui/aura/mus/drag_drop_controller_mus.h
+++ b/ui/aura/mus/drag_drop_controller_mus.h
@@ -17,6 +17,10 @@
 #include "ui/aura/window_tracker.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 
+namespace gfx {
+class PointF;
+}
+
 namespace ws {
 namespace mojom {
 class WindowTree;
@@ -51,16 +55,19 @@
   void OnDragDropStart(std::map<std::string, std::vector<uint8_t>> data);
   uint32_t OnDragEnter(WindowMus* window,
                        uint32_t event_flags,
-                       const gfx::Point& screen_location,
+                       const gfx::PointF& location_in_root,
+                       const gfx::PointF& location,
                        uint32_t effect_bitmask);
   uint32_t OnDragOver(WindowMus* window,
                       uint32_t event_flags,
-                      const gfx::Point& screen_location,
+                      const gfx::PointF& location_in_root,
+                      const gfx::PointF& location,
                       uint32_t effect_bitmask);
   void OnDragLeave(WindowMus* window);
   uint32_t OnCompleteDrop(WindowMus* window,
                           uint32_t event_flags,
-                          const gfx::Point& screen_location,
+                          const gfx::PointF& location_in_root,
+                          const gfx::PointF& location,
                           uint32_t effect_bitmask);
   void OnPerformDragDropCompleted(uint32_t action_taken);
   void OnDragDropDone();
@@ -83,14 +90,16 @@
   // Called from OnDragEnter() and OnDragOver().
   uint32_t HandleDragEnterOrOver(WindowMus* window,
                                  uint32_t event_flags,
-                                 const gfx::Point& screen_location,
+                                 const gfx::PointF& location_in_root,
+                                 const gfx::PointF& location,
                                  uint32_t effect_bitmask,
                                  bool is_enter);
 
   std::unique_ptr<ui::DropTargetEvent> CreateDropTargetEvent(
       Window* window,
       uint32_t event_flags,
-      const gfx::Point& screen_location,
+      const gfx::PointF& location_in_root,
+      const gfx::PointF& location,
       uint32_t effect_bitmask);
 
   DragDropControllerHost* drag_drop_controller_host_;
diff --git a/ui/aura/mus/drag_drop_controller_mus_unittest.cc b/ui/aura/mus/drag_drop_controller_mus_unittest.cc
index 753d7c9d..a48bd08 100644
--- a/ui/aura/mus/drag_drop_controller_mus_unittest.cc
+++ b/ui/aura/mus/drag_drop_controller_mus_unittest.cc
@@ -64,9 +64,10 @@
  private:
   void DragMoveAndDrop() {
     WindowMus* const window_mus = WindowMus::Get(window_.get());
-    controller_->OnDragEnter(window_mus, 0, gfx::Point(5, 20), 0);
-    controller_->OnDragOver(window_mus, 0, gfx::Point(5, 20), 0);
-    controller_->OnCompleteDrop(window_mus, 0, gfx::Point(5, 20), 0);
+    const gfx::PointF point(5, 20);
+    controller_->OnDragEnter(window_mus, 0, point, point, 0);
+    controller_->OnDragOver(window_mus, 0, point, point, 0);
+    controller_->OnCompleteDrop(window_mus, 0, point, point, 0);
     controller_->OnPerformDragDropCompleted(0);
   }
 
diff --git a/ui/aura/mus/focus_synchronizer_unittest.cc b/ui/aura/mus/focus_synchronizer_unittest.cc
index 08eb346..83feb7d 100644
--- a/ui/aura/mus/focus_synchronizer_unittest.cc
+++ b/ui/aura/mus/focus_synchronizer_unittest.cc
@@ -24,8 +24,10 @@
   ~TestFocusRules() override = default;
 
   // wm::BaseFocusRules overrides:
-  bool SupportsChildActivation(Window* window) const override { return true; }
-  bool CanActivateWindow(Window* window) const override { return true; }
+  bool SupportsChildActivation(const Window* window) const override {
+    return true;
+  }
+  bool CanActivateWindow(const Window* window) const override { return true; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 1d9419e34..a5676c55 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -1387,20 +1387,24 @@
 
 void WindowTreeClient::OnDragEnter(ws::Id window_id,
                                    uint32_t key_state,
-                                   const gfx::Point& position,
+                                   const gfx::PointF& location_in_root,
+                                   const gfx::PointF& location,
                                    uint32_t effect_bitmask,
                                    OnDragEnterCallback callback) {
   std::move(callback).Run(drag_drop_controller_->OnDragEnter(
-      GetWindowByServerId(window_id), key_state, position, effect_bitmask));
+      GetWindowByServerId(window_id), key_state, location_in_root, location,
+      effect_bitmask));
 }
 
 void WindowTreeClient::OnDragOver(ws::Id window_id,
                                   uint32_t key_state,
-                                  const gfx::Point& position,
+                                  const gfx::PointF& location_in_root,
+                                  const gfx::PointF& location,
                                   uint32_t effect_bitmask,
                                   OnDragOverCallback callback) {
   std::move(callback).Run(drag_drop_controller_->OnDragOver(
-      GetWindowByServerId(window_id), key_state, position, effect_bitmask));
+      GetWindowByServerId(window_id), key_state, location_in_root, location,
+      effect_bitmask));
 }
 
 void WindowTreeClient::OnDragLeave(ws::Id window_id) {
@@ -1413,11 +1417,13 @@
 
 void WindowTreeClient::OnCompleteDrop(ws::Id window_id,
                                       uint32_t key_state,
-                                      const gfx::Point& position,
+                                      const gfx::PointF& location_in_root,
+                                      const gfx::PointF& location,
                                       uint32_t effect_bitmask,
                                       OnCompleteDropCallback callback) {
   std::move(callback).Run(drag_drop_controller_->OnCompleteDrop(
-      GetWindowByServerId(window_id), key_state, position, effect_bitmask));
+      GetWindowByServerId(window_id), key_state, location_in_root, location,
+      effect_bitmask));
 }
 
 void WindowTreeClient::OnPerformDragDropCompleted(uint32_t change_id,
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index 5a2b4e1d..f9f9ec4 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -436,18 +436,21 @@
                            mime_data) override;
   void OnDragEnter(ws::Id window_id,
                    uint32_t event_flags,
-                   const gfx::Point& position,
+                   const gfx::PointF& location_in_root,
+                   const gfx::PointF& location,
                    uint32_t effect_bitmask,
                    OnDragEnterCallback callback) override;
   void OnDragOver(ws::Id window_id,
                   uint32_t event_flags,
-                  const gfx::Point& position,
+                  const gfx::PointF& location_in_root,
+                  const gfx::PointF& location,
                   uint32_t effect_bitmask,
                   OnDragOverCallback callback) override;
   void OnDragLeave(ws::Id window_id) override;
   void OnCompleteDrop(ws::Id window_id,
                       uint32_t event_flags,
-                      const gfx::Point& position,
+                      const gfx::PointF& location_in_root,
+                      const gfx::PointF& location,
                       uint32_t effect_bitmask,
                       OnCompleteDropCallback callback) override;
   void OnPerformDragDropCompleted(uint32_t change_id,
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 6509aacf..a8afdfc9 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -900,6 +900,7 @@
       "cocoa/touch_bar_util_unittest.mm",
       "cocoa/tracking_area_unittest.mm",
       "cocoa/weak_ptr_nsobject_unittest.mm",
+      "cursor/cursor_util_unittest.cc",
       "models/list_model_unittest.cc",
       "models/list_selection_model_unittest.cc",
       "models/tree_node_model_unittest.cc",
@@ -1019,8 +1020,6 @@
   }
 
   if (use_x11) {
-    sources += [ "cursor/cursor_loader_x11_unittest.cc" ]
-
     configs += [ "//build/config/linux:x11" ]
 
     deps += [
diff --git a/ui/base/cursor/cursor_loader_x11_unittest.cc b/ui/base/cursor/cursor_loader_x11_unittest.cc
deleted file mode 100644
index b1e393e..0000000
--- a/ui/base/cursor/cursor_loader_x11_unittest.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 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/cursor/cursor_loader_x11.h"
-
-#include "base/logging.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/cursor/cursor_util.h"
-#include "ui/display/display.h"
-
-namespace ui {
-
-TEST(CursorLoaderX11Test, ScaleAndRotate) {
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(10, 5);
-  bitmap.eraseColor(SK_ColorBLACK);
-
-  gfx::Point hotpoint(3,4);
-
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_0,
-                                        &bitmap, &hotpoint);
-  EXPECT_EQ(10, bitmap.width());
-  EXPECT_EQ(5, bitmap.height());
-  EXPECT_EQ("3,4", hotpoint.ToString());
-
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_90,
-                                        &bitmap, &hotpoint);
-
-  EXPECT_EQ(5, bitmap.width());
-  EXPECT_EQ(10, bitmap.height());
-  EXPECT_EQ("1,3", hotpoint.ToString());
-
-  ScaleAndRotateCursorBitmapAndHotpoint(2.0f, display::Display::ROTATE_180,
-                                        &bitmap, &hotpoint);
-  EXPECT_EQ(10, bitmap.width());
-  EXPECT_EQ(20, bitmap.height());
-  EXPECT_EQ("8,14", hotpoint.ToString());
-
-  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_270,
-                                        &bitmap, &hotpoint);
-  EXPECT_EQ(20, bitmap.width());
-  EXPECT_EQ(10, bitmap.height());
-  EXPECT_EQ("14,2", hotpoint.ToString());
-}
-
-}  // namespace ui
diff --git a/ui/base/cursor/cursor_util.cc b/ui/base/cursor/cursor_util.cc
index 3735bf6..caca928 100644
--- a/ui/base/cursor/cursor_util.cc
+++ b/ui/base/cursor/cursor_util.cc
@@ -5,7 +5,6 @@
 #include "ui/base/cursor/cursor_util.h"
 
 #include "base/logging.h"
-#include "skia/ext/image_operations.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -39,7 +38,7 @@
   return true;
 }
 
-} // namespace
+}  // namespace
 
 void ScaleAndRotateCursorBitmapAndHotpoint(float scale,
                                            display::Display::Rotation rotation,
@@ -90,11 +89,17 @@
   gfx::Size scaled_size = gfx::ScaleToFlooredSize(
       gfx::Size(bitmap->width(), bitmap->height()), scale);
 
-  *bitmap = skia::ImageOperations::Resize(
-      *bitmap,
-      skia::ImageOperations::RESIZE_BETTER,
-      scaled_size.width(),
-      scaled_size.height());
+  // TODO(crbug.com/919866): skia::ImageOperations::Resize() doesn't support
+  // unpremultiplied alpha bitmaps.
+  SkBitmap scaled_bitmap;
+  scaled_bitmap.setInfo(
+      bitmap->info().makeWH(scaled_size.width(), scaled_size.height()));
+  if (scaled_bitmap.tryAllocPixels()) {
+    bitmap->pixmap().scalePixels(scaled_bitmap.pixmap(),
+                                 kMedium_SkFilterQuality);
+  }
+
+  *bitmap = scaled_bitmap;
   *hotpoint = gfx::ScaleToFlooredPoint(*hotpoint, scale);
 }
 
diff --git a/ui/base/cursor/cursor_util_unittest.cc b/ui/base/cursor/cursor_util_unittest.cc
new file mode 100644
index 0000000..360f7fa
--- /dev/null
+++ b/ui/base/cursor/cursor_util_unittest.cc
@@ -0,0 +1,70 @@
+// 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 "ui/base/cursor/cursor_util.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace ui {
+namespace {
+
+// Parameterized test for cursor bitmaps with premultiplied and unpremultiplied
+// alpha.
+class CursorUtilTest : public testing::TestWithParam<bool> {
+ public:
+  SkColor GetPixelColor() {
+    return GetParam() ? SkColorSetARGB(128, 255, 0, 0)
+                      : SkColorSetARGB(128, 128, 0, 0);
+  }
+  SkImageInfo GetImageInfo() {
+    return GetParam() ? SkImageInfo::MakeN32(10, 5, kUnpremul_SkAlphaType)
+                      : SkImageInfo::MakeN32(10, 5, kPremul_SkAlphaType);
+  }
+};
+
+TEST_P(CursorUtilTest, ScaleAndRotate) {
+  const SkColor pixel_color = GetPixelColor();
+
+  SkBitmap bitmap;
+  bitmap.setInfo(GetImageInfo());
+  bitmap.allocPixels();
+  bitmap.eraseColor(pixel_color);
+
+  gfx::Point hotpoint(3, 4);
+
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_0,
+                                        &bitmap, &hotpoint);
+  EXPECT_EQ(10, bitmap.width());
+  EXPECT_EQ(5, bitmap.height());
+  EXPECT_EQ("3,4", hotpoint.ToString());
+  EXPECT_EQ(pixel_color, bitmap.pixmap().getColor(0, 0));
+
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_90,
+                                        &bitmap, &hotpoint);
+  EXPECT_EQ(5, bitmap.width());
+  EXPECT_EQ(10, bitmap.height());
+  EXPECT_EQ("1,3", hotpoint.ToString());
+  EXPECT_EQ(pixel_color, bitmap.pixmap().getColor(0, 0));
+
+  ScaleAndRotateCursorBitmapAndHotpoint(2.0f, display::Display::ROTATE_180,
+                                        &bitmap, &hotpoint);
+  EXPECT_EQ(10, bitmap.width());
+  EXPECT_EQ(20, bitmap.height());
+  EXPECT_EQ("8,14", hotpoint.ToString());
+  EXPECT_EQ(pixel_color, bitmap.pixmap().getColor(0, 0));
+
+  ScaleAndRotateCursorBitmapAndHotpoint(1.0f, display::Display::ROTATE_270,
+                                        &bitmap, &hotpoint);
+  EXPECT_EQ(20, bitmap.width());
+  EXPECT_EQ(10, bitmap.height());
+  EXPECT_EQ("14,2", hotpoint.ToString());
+  EXPECT_EQ(pixel_color, bitmap.pixmap().getColor(0, 0));
+}
+
+INSTANTIATE_TEST_CASE_P(, CursorUtilTest, testing::Bool());
+
+}  // namespace
+}  // namespace ui
diff --git a/ui/base/ime/linux/fake_input_method_context_factory.cc b/ui/base/ime/linux/fake_input_method_context_factory.cc
index f8d3102e..aba2f76 100644
--- a/ui/base/ime/linux/fake_input_method_context_factory.cc
+++ b/ui/base/ime/linux/fake_input_method_context_factory.cc
@@ -8,7 +8,9 @@
 
 namespace ui {
 
-FakeInputMethodContextFactory::FakeInputMethodContextFactory() {}
+FakeInputMethodContextFactory::FakeInputMethodContextFactory() = default;
+
+FakeInputMethodContextFactory::~FakeInputMethodContextFactory() = default;
 
 std::unique_ptr<LinuxInputMethodContext>
 FakeInputMethodContextFactory::CreateInputMethodContext(
diff --git a/ui/base/ime/linux/fake_input_method_context_factory.h b/ui/base/ime/linux/fake_input_method_context_factory.h
index 12bb7661..1d5f8b8 100644
--- a/ui/base/ime/linux/fake_input_method_context_factory.h
+++ b/ui/base/ime/linux/fake_input_method_context_factory.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
+#include "ui/base/ime/linux/ui_base_ime_linux_export.h"
 
 namespace ui {
 
@@ -16,6 +17,7 @@
     : public LinuxInputMethodContextFactory {
  public:
   FakeInputMethodContextFactory();
+  ~FakeInputMethodContextFactory() override;
 
   // LinuxInputMethodContextFactory:
   std::unique_ptr<LinuxInputMethodContext> CreateInputMethodContext(
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index fb958b18..bb53c04 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -112,10 +112,6 @@
   // Returns the model for the submenu at the specified index.
   virtual MenuModel* GetSubmenuModelAt(int index) const = 0;
 
-  // Called when the highlighted menu item changes to the item at the specified
-  // index.
-  virtual void HighlightChangedTo(int index) = 0;
-
   // Called when the item at the specified index has been activated.
   virtual void ActivatedAt(int index) = 0;
 
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 4aa19f6..b9fbad7 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -50,9 +50,6 @@
   return false;
 }
 
-void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) {
-}
-
 void SimpleMenuModel::Delegate::OnMenuWillShow(SimpleMenuModel* /*source*/) {}
 
 void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) {
@@ -422,11 +419,6 @@
          items_[ValidateItemIndex(index)].visible;
 }
 
-void SimpleMenuModel::HighlightChangedTo(int index) {
-  if (delegate_)
-    delegate_->CommandIdHighlighted(GetCommandIdAt(index));
-}
-
 void SimpleMenuModel::ActivatedAt(int index) {
   ActivatedAt(index, 0);
 }
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index ac1e1a7..b9f7b9b 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -46,10 +46,6 @@
     virtual bool GetIconForCommandId(int command_id,
                                      gfx::Image* icon) const;
 
-    // Notifies the delegate that the item with the specified command id was
-    // visually highlighted within the menu.
-    virtual void CommandIdHighlighted(int command_id);
-
     // Performs the action associates with the specified command id.
     // The passed |event_flags| are the flags from the event which issued this
     // command and they can be examined to find modifier keys.
@@ -185,7 +181,6 @@
   ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override;
   bool IsEnabledAt(int index) const override;
   bool IsVisibleAt(int index) const override;
-  void HighlightChangedTo(int index) override;
   void ActivatedAt(int index) override;
   void ActivatedAt(int index, int event_flags) override;
   MenuModel* GetSubmenuModelAt(int index) const override;
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 9a132426..71bd2fb 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -83,7 +83,7 @@
 #if defined(OS_WIN)
 // Enables InputPane API for controlling on screen keyboard.
 const base::Feature kInputPaneOnScreenKeyboard = {
-    "InputPaneOnScreenKeyboard", base::FEATURE_DISABLED_BY_DEFAULT};
+    "InputPaneOnScreenKeyboard", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables using WM_POINTER instead of WM_TOUCH for touch events.
 const base::Feature kPointerEventsForTouch = {"PointerEventsForTouch",
@@ -178,4 +178,8 @@
 
 const base::Feature kDarkMode = {"DarkMode", base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_CHROMEOS)
+const base::Feature kHandwritingGesture = {"HandwritingGesture",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 }  // namespace features
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 5bf2fed..614dc06 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -103,6 +103,9 @@
 // macOS Mojave/Windows 10.
 UI_BASE_EXPORT extern const base::Feature kDarkMode;
 
+#if defined(OS_CHROMEOS)
+UI_BASE_EXPORT extern const base::Feature kHandwritingGesture;
+#endif
 }  // namespace features
 
 #endif  // UI_BASE_UI_BASE_FEATURES_H_
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index d29845c..e3df93b 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1009,7 +1009,7 @@
       new_displays.push_back(new_display);
       ++curr_iter;
       ++new_info_iter;
-    } else if (curr_iter->id() < new_info_iter->id()) {
+    } else if (CompareDisplayIds(curr_iter->id(), new_info_iter->id())) {
       // more displays in current list between ids, which means it is deleted.
       removed_displays.push_back(*curr_iter);
       ++curr_iter;
diff --git a/ui/display/manager/touch_transform_controller.cc b/ui/display/manager/touch_transform_controller.cc
index 4b7602d..50b55ffa 100644
--- a/ui/display/manager/touch_transform_controller.cc
+++ b/ui/display/manager/touch_transform_controller.cc
@@ -9,7 +9,6 @@
 
 #include "third_party/skia/include/core/SkMatrix44.h"
 #include "ui/display/display_layout.h"
-#include "ui/display/manager/display_configurator.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/display/manager/touch_device_manager.h"
@@ -281,11 +280,9 @@
 }
 
 TouchTransformController::TouchTransformController(
-    DisplayConfigurator* display_configurator,
     DisplayManager* display_manager,
     std::unique_ptr<TouchTransformSetter> setter)
-    : display_configurator_(display_configurator),
-      display_manager_(display_manager),
+    : display_manager_(display_manager),
       touch_transform_setter_(std::move(setter)) {}
 
 TouchTransformController::~TouchTransformController() {}
diff --git a/ui/display/manager/touch_transform_controller.h b/ui/display/manager/touch_transform_controller.h
index 55de79d..00c7914 100644
--- a/ui/display/manager/touch_transform_controller.h
+++ b/ui/display/manager/touch_transform_controller.h
@@ -22,7 +22,6 @@
 
 namespace display {
 
-class DisplayConfigurator;
 class DisplayManager;
 class ManagedDisplayInfo;
 class TouchTransformSetter;
@@ -38,8 +37,7 @@
 // and input-device space.
 class DISPLAY_MANAGER_EXPORT TouchTransformController {
  public:
-  TouchTransformController(DisplayConfigurator* display_configurator,
-                           DisplayManager* display_manager,
+  TouchTransformController(DisplayManager* display_manager,
                            std::unique_ptr<TouchTransformSetter> setter);
   ~TouchTransformController();
 
@@ -103,9 +101,7 @@
                             const ManagedDisplayInfo& target_display,
                             UpdateData* update_data) const;
 
-  // Both |display_configurator_| and |display_manager_| are not owned and must
-  // outlive TouchTransformController.
-  DisplayConfigurator* display_configurator_;
+  // |display_manager_| are not owned and must outlive TouchTransformController.
   DisplayManager* display_manager_;
 
   bool is_calibrating_ = false;
diff --git a/ui/display/manager/touch_transform_controller_unittest.cc b/ui/display/manager/touch_transform_controller_unittest.cc
index ba7cdd1..5cff31f 100644
--- a/ui/display/manager/touch_transform_controller_unittest.cc
+++ b/ui/display/manager/touch_transform_controller_unittest.cc
@@ -126,7 +126,7 @@
     display_manager_ = std::make_unique<DisplayManager>(std::move(screen));
     touch_device_manager_ = display_manager_->touch_device_manager();
     touch_transform_controller_ = std::make_unique<TouchTransformController>(
-        nullptr, display_manager_.get(),
+        display_manager_.get(),
         std::make_unique<DefaultTouchTransformSetter>());
   }
 
diff --git a/ui/events/win/media_keyboard_hook_win_unittest.cc b/ui/events/win/media_keyboard_hook_win_unittest.cc
index d043877..1d65ccb 100644
--- a/ui/events/win/media_keyboard_hook_win_unittest.cc
+++ b/ui/events/win/media_keyboard_hook_win_unittest.cc
@@ -92,6 +92,7 @@
       next_time_stamp()));
 }
 
+namespace {
 void VerifyKeyEvent(KeyEvent* key_event,
                     KeyboardCode non_located_key_code,
                     DomCode dom_code,
@@ -107,6 +108,7 @@
   ASSERT_EQ(key_event->key_code(), non_located_key_code);
   ASSERT_EQ(key_event->code(), dom_code);
 }
+}  // namespace
 
 TEST_F(MediaKeyboardHookWinTest, SimpleKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_MEDIA_PLAY_PAUSE;
diff --git a/ui/events/win/modifier_keyboard_hook_win_unittest.cc b/ui/events/win/modifier_keyboard_hook_win_unittest.cc
index ff1dcf4a1..71bd3f0 100644
--- a/ui/events/win/modifier_keyboard_hook_win_unittest.cc
+++ b/ui/events/win/modifier_keyboard_hook_win_unittest.cc
@@ -101,6 +101,7 @@
       next_time_stamp()));
 }
 
+namespace {
 void VerifyKeyEvent(KeyEvent* key_event,
                     KeyboardCode non_located_key_code,
                     DomCode dom_code,
@@ -116,6 +117,7 @@
   ASSERT_EQ(key_event->key_code(), non_located_key_code);
   ASSERT_EQ(key_event->code(), dom_code);
 }
+}  // namespace
 
 TEST_F(ModifierKeyboardHookWinTest, SimpleLeftControlKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
diff --git a/ui/file_manager/audio_player/elements/audio_player.js b/ui/file_manager/audio_player/elements/audio_player.js
index e2ceac8..68a352f 100644
--- a/ui/file_manager/audio_player/elements/audio_player.js
+++ b/ui/file_manager/audio_player/elements/audio_player.js
@@ -24,6 +24,7 @@
       type: Boolean,
       observer: 'playingChanged',
       reflectToAttribute: true,
+      notify: true
     },
 
     /**
diff --git a/ui/file_manager/audio_player/js/BUILD.gn b/ui/file_manager/audio_player/js/BUILD.gn
index ddf897af..5d9d85a 100644
--- a/ui/file_manager/audio_player/js/BUILD.gn
+++ b/ui/file_manager/audio_player/js/BUILD.gn
@@ -18,6 +18,7 @@
   sources = []
   externs_list = [
     "$externs_path/chrome_extensions.js",
+    "$externs_path/mediasession.js",
     "../../externs/audio_player_foreground.js",
     "../../externs/platform.js",
   ]
@@ -28,6 +29,7 @@
     "../elements:audio_player",
     "../elements:track_list",
     "//ui/file_manager/base/js:filtered_volume_manager",
+    "//ui/file_manager/base/js:mediasession_types",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/foreground/js/metadata:content_metadata_provider",
     "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model",
diff --git a/ui/file_manager/audio_player/js/audio_player.js b/ui/file_manager/audio_player/js/audio_player.js
index 7da26813..8cd3601b 100644
--- a/ui/file_manager/audio_player/js/audio_player.js
+++ b/ui/file_manager/audio_player/js/audio_player.js
@@ -95,6 +95,9 @@
     this.onTrackInfoExpandedChanged_(event.detail.value);
   }.bind(this));
 
+  this.player_.addEventListener(
+      'playing-changed', this.updateMediaSessionPlaybackState_.bind(this));
+
   // Run asynchronously after an event of model change is delivered.
   setTimeout(function() {
     this.errorString_ = '';
@@ -441,6 +444,22 @@
   }
 };
 
+/**
+ * Updates the Media Session API with the current playback state of the audio
+ * player.
+ * @param {Event} event The playing event.
+ * @private
+ */
+AudioPlayer.prototype.updateMediaSessionPlaybackState_ = function(event) {
+  if (!navigator.mediaSession) {
+    return;
+  }
+
+  navigator.mediaSession.playbackState = event.detail.value ?
+      MediaSessionPlaybackState.PLAYING :
+      MediaSessionPlaybackState.PAUSED;
+};
+
 /* Keep the below constants in sync with the CSS. */
 
 /**
diff --git a/ui/file_manager/audio_player/js/audio_player_scripts.js b/ui/file_manager/audio_player/js/audio_player_scripts.js
index d1ddff66..5cdbdf64 100644
--- a/ui/file_manager/audio_player/js/audio_player_scripts.js
+++ b/ui/file_manager/audio_player/js/audio_player_scripts.js
@@ -27,6 +27,7 @@
 // <include src="../../file_manager/common/js/async_util.js">
 // <include src="../../file_manager/common/js/file_type.js">
 // <include src="../../file_manager/common/js/util.js">
+// <include src="../../base/js/mediasession_types.js">
 // <include src="../../base/js/volume_manager_types.js">
 // <include src="../../base/js/filtered_volume_manager.js">
 
diff --git a/ui/file_manager/base/js/BUILD.gn b/ui/file_manager/base/js/BUILD.gn
index 2103bf7..607ba92 100644
--- a/ui/file_manager/base/js/BUILD.gn
+++ b/ui/file_manager/base/js/BUILD.gn
@@ -78,3 +78,6 @@
     ":volume_manager_types_unittest",
   ]
 }
+
+js_library("mediasession_types") {
+}
diff --git a/ui/file_manager/base/js/mediasession_types.js b/ui/file_manager/base/js/mediasession_types.js
new file mode 100644
index 0000000..48d36dd
--- /dev/null
+++ b/ui/file_manager/base/js/mediasession_types.js
@@ -0,0 +1,13 @@
+// Copyright 2018 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.
+
+/**
+ * @see https://wicg.github.io/mediasession/#enumdef-mediasessionplaybackstate
+ * @enum {string}
+ */
+const MediaSessionPlaybackState = {
+  NONE: 'none',
+  PAUSED: 'paused',
+  PLAYING: 'playing'
+};
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index fe337eb..6698d447 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -441,7 +441,7 @@
     // new window. If not found, then launch with the default url.
     this.findFocusedWindow_().then(function(key) {
       if (!key) {
-        launcher.launchFileManager(appState);
+        launcher.launchFileManager();
         return;
       }
       var appState = {
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index b2d6e60..de5b199 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -188,7 +188,7 @@
               new Event(VolumeManagerCommon.VOLUME_ALREADY_MOUNTED);
           navigationEvent.volumeId = event.volumeMetadata.volumeId;
           this.dispatchEvent(navigationEvent);
-          this.finishRequest_(requestKey, event.status, volumeInfo);
+          this.finishRequest_(requestKey, event.status);
           callback();
         } else {
           console.warn('Failed to mount a volume: ' + event.status);
diff --git a/ui/file_manager/file_manager/common/js/unittest_util.js b/ui/file_manager/file_manager/common/js/unittest_util.js
index 1d84694..fd1bec2e 100644
--- a/ui/file_manager/file_manager/common/js/unittest_util.js
+++ b/ui/file_manager/file_manager/common/js/unittest_util.js
@@ -114,6 +114,7 @@
   /** @type {Object<?>} */
   this.state = {};
 
+  /** @suppress {const} */
   window.chrome = window.chrome || {};
   /** @suppress {const} */
   window.chrome.runtime = window.chrome.runtime || {};  // For lastError.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 54ae8aa2..55e9285 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -360,6 +360,17 @@
   ]
 }
 
+js_unittest("list_container_unittest") {
+  deps = [
+    ":list_container",
+    "../../../common/js:util",
+    "//ui/file_manager/base/js:test_error_reporting",
+    "//ui/webui/resources/js:webui_resource_test",
+    "//ui/webui/resources/js/cr/ui:context_menu_handler",
+    "//ui/webui/resources/js/cr/ui:menu",
+  ]
+}
+
 js_library("location_line") {
   deps = [
     "../../../common/js:files_app_entry_types",
@@ -454,6 +465,7 @@
     ":file_list_selection_model_unittest",
     ":file_table_unittest",
     ":file_tap_handler_unittest",
+    ":list_container_unittest",
     ":multi_menu_unittest",
   ]
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/list_container.js b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
index 8c1ba1b77..c92eb78f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/list_container.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
@@ -332,11 +332,36 @@
 };
 
 /**
+ * Check if our context menu has any items that can be activated
+ * @return {boolean} True if the menu has action item. Otherwise, false.
+ * @private
+ */
+ListContainer.prototype.contextMenuHasActions_ = function() {
+  const menu = document.querySelector('#file-context-menu');
+  const menuItems = menu.querySelectorAll('cr-menu-item, hr');
+  for (const item of menuItems) {
+    if (!item.hasAttribute('hidden') && !item.hasAttribute('disabled') &&
+        (window.getComputedStyle(item).display != 'none')) {
+      return true;
+    }
+  }
+  return false;
+};
+
+/**
  * Contextmenu event handler to prevent change of focus on long-tapping the
  * header of the file list.
+ * @param {!Event} e Menu event.
  * @private
  */
 ListContainer.prototype.onContextMenu_ = function(e) {
+  // Inhibit the context menu being shown if it only hosts
+  // disabled items https://crbug.com/917975
+  if (this.contextMenuHasActions_() === false) {
+    e.preventDefault();
+    e.stopPropagation();
+    return;
+  }
   if (!this.allowContextMenuByTouch_ && e.sourceCapabilities &&
       e.sourceCapabilities.firesTouchEvents) {
     this.focus();
diff --git a/ui/file_manager/file_manager/foreground/js/ui/list_container_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/list_container_unittest.js
new file mode 100644
index 0000000..92972a2
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/ui/list_container_unittest.js
@@ -0,0 +1,101 @@
+// 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.
+
+'use strict';
+
+/** @type {cr.ui.Menu} */
+let contextMenu;
+
+/** @type {!ListContainer} */
+let listContainer;
+
+/** @type {!FileTable} */
+let table;
+
+/** @type {!FileGrid} */
+let grid;
+
+// Set up test components.
+function setUp() {
+  // Install cr.ui <command> elements and <cr-menu>s on the page.
+  document.body.innerHTML = [
+    '<style>',
+    '  .hide {',
+    '    display: none;',
+    '  }',
+    '</style>',
+    '<command id="default-task">',
+    '<cr-menu id="file-context-menu" hidden>',
+    '  <cr-menu-item id="item" command="#default-task"></cr-menu-item>',
+    '</cr-menu>',
+    '<div id="list-container">',
+    '  <div id="detail-table">',
+    '    <list id="file-list" contextmenu="#file-context-menu">',
+    '    </list>',
+    '  </div>',
+    '  <grid id="file-grid" contextmenu="#file-context-menu" hidden>',
+    '  </grid>',
+    '  <paper-progress class="loading-indicator" hidden></paper-progress>',
+    '</div>',
+  ].join('');
+
+  // Initialize cr.ui.Command with the <command>s.
+  cr.ui.decorate('command', cr.ui.Command);
+  // Setup the listContainer and its dependencies
+  contextMenu = util.queryDecoratedElement('#file-context-menu', cr.ui.Menu);
+  table = /** @type {!FileTable} */
+      (queryRequiredElement('#detail-table', undefined));
+  table.list = document.querySelector('#file-list');
+  grid = /** @type {!FileGrid} */
+      (queryRequiredElement('#file-grid', undefined));
+  listContainer = new ListContainer(
+      queryRequiredElement('#list-container', undefined), table, grid);
+
+  cr.ui.contextMenuHandler.setContextMenu(table, contextMenu);
+}
+
+/**
+ * Send a 'contextmenu' event to the element target of a query.
+ * @param {string} targetQuery Query to specify the element.
+ */
+function sendContextMenu(targetQuery) {
+  const event = new MouseEvent('contextmenu', {
+    bubbles: true,
+    composed: true,  // Allow the event to bubble past shadow DOM root.
+  });
+  const target = document.querySelector(targetQuery);
+  assertTrue(!!target);
+  return target.dispatchEvent(event);
+}
+
+/**
+ *  Tests that sending a 'contextmenu' event will show the menu
+ *  if it contains cr-menu-item(s) that are actionable.
+ */
+function testShowMenuWithActionsOpensContextMenu() {
+  sendContextMenu('#file-list');
+  assertFalse(contextMenu.hasAttribute('hidden'));
+}
+
+/**
+ *  Tests that sending a 'contextmenu' event will hide the menu
+ *  if it doesn't contain cr-menu-item(s) that are actionable.
+ */
+function testShowMenuWithNoActionsHidesContextMenu() {
+  const menuItem = document.querySelector('#item');
+
+  menuItem.setAttribute('hidden', '');
+  sendContextMenu('#file-list');
+  assertTrue(contextMenu.hasAttribute('hidden'));
+  menuItem.removeAttribute('hidden');
+
+  menuItem.setAttribute('disabled', 'disabled');
+  sendContextMenu('#file-list');
+  assertTrue(contextMenu.hasAttribute('hidden'));
+  menuItem.removeAttribute('disabled');
+
+  menuItem.setAttribute('class', 'hide');
+  sendContextMenu('#file-list');
+  assertTrue(contextMenu.hasAttribute('hidden'));
+}
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 1c05081..f53f46e 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -501,6 +501,17 @@
     typeText: 'OGG video'
   }),
 
+  video: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'video_long.ogv',
+    targetPath: 'video_long.ogv',
+    mimeType: 'video/ogg',
+    lastModifiedTime: 'Jan 14, 2019, 16:01 PM',
+    nameText: 'video_long.ogv',
+    sizeText: '166 KB',
+    typeText: 'OGG video'
+  }),
+
   unsupported: new TestEntryInfo({
     type: EntryType.FILE,
     sourceFileName: 'random.bin',
diff --git a/ui/file_manager/integration_tests/video_player/click_control_buttons.js b/ui/file_manager/integration_tests/video_player/click_control_buttons.js
index 4704f56..2a4fd08 100644
--- a/ui/file_manager/integration_tests/video_player/click_control_buttons.js
+++ b/ui/file_manager/integration_tests/video_player/click_control_buttons.js
@@ -88,3 +88,45 @@
             appId, '#controls:not([fullscreen])');
       });
 };
+
+/**
+ * Confirms that native media keys are dispatched correctly.
+ * @return {Promise} Promise to be fulfilled on success.
+ */
+testcase.mediaKeyNative = function() {
+  const openVideo = openSingleVideo('local', 'downloads', ENTRIES.video);
+  let appId;
+  function ensurePlaying() {
+    return waitForFunctionResult('isPlaying', 'video_long.ogv', true);
+  }
+  function ensurePaused() {
+    return waitForFunctionResult('isPlaying', 'video_long.ogv', false);
+  }
+  function sendMediaKey() {
+    return sendTestMessage({name: 'dispatchNativeMediaKey'}).then((result) => {
+      chrome.test.assertEq(
+          result, 'mediaKeyDispatched', 'Key dispatch failure');
+    });
+  }
+  function pauseAndUnpause() {
+    // Video player should be playing when this is called,
+    return Promise.resolve()
+        .then(ensurePlaying)
+        .then(sendMediaKey)
+        .then(ensurePaused)
+        .then(sendMediaKey)
+        .then(ensurePlaying);
+  }
+  function enableTabletMode() {
+    return sendTestMessage({name: 'enableTabletMode'}).then((result) => {
+      chrome.test.assertEq(result, 'tabletModeEnabled');
+    });
+  }
+  return openVideo
+      .then((args) => {
+        appId = args[0];
+      })
+      .then(pauseAndUnpause)
+      .then(enableTabletMode)
+      .then(pauseAndUnpause);
+};
diff --git a/ui/file_manager/video_player/js/BUILD.gn b/ui/file_manager/video_player/js/BUILD.gn
index a01ac3e1..6b93e19 100644
--- a/ui/file_manager/video_player/js/BUILD.gn
+++ b/ui/file_manager/video_player/js/BUILD.gn
@@ -22,6 +22,7 @@
     "$externs_path/chrome_extensions.js",
     "$externs_path/media_player_private.js",
     "$externs_path/metrics_private.js",
+    "$externs_path/mediasession.js",
     "../../externs/chrome_cast.js",
     "../../externs/platform.js",
   ]
@@ -58,6 +59,7 @@
     "cast:cast_video_element",
     "cast:media_manager",
     "//ui/file_manager/base/js:filtered_volume_manager",
+    "//ui/file_manager/base/js:mediasession_types",
     "//ui/file_manager/file_manager/common/js:metrics_base",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/image_loader:image_loader_client",
diff --git a/ui/file_manager/video_player/js/cast/BUILD.gn b/ui/file_manager/video_player/js/cast/BUILD.gn
index 02cddc5..7305df1 100644
--- a/ui/file_manager/video_player/js/cast/BUILD.gn
+++ b/ui/file_manager/video_player/js/cast/BUILD.gn
@@ -19,6 +19,7 @@
   externs_list = [
     "$externs_path/chrome_extensions.js",
     "$externs_path/media_player_private.js",
+    "$externs_path/mediasession.js",
     "../../../externs/app_window_common.js",
     "../../../externs/background/volume_manager_factory.js",
     "../../../externs/chrome_cast.js",
diff --git a/ui/file_manager/video_player/js/video_player.js b/ui/file_manager/video_player/js/video_player.js
index 24d7497..6edb7f8 100644
--- a/ui/file_manager/video_player/js/video_player.js
+++ b/ui/file_manager/video_player/js/video_player.js
@@ -492,10 +492,12 @@
           this.videoElement_.addEventListener('play', function() {
             chrome.power.requestKeepAwake('display');
             this.updateInactivityWatcherState_();
+            this.updateMediaSessionPlaybackState_();
           }.wrap(this));
           this.videoElement_.addEventListener('pause', function() {
             chrome.power.releaseKeepAwake();
             this.updateInactivityWatcherState_();
+            this.updateMediaSessionPlaybackState_();
           }.wrap(this));
           this.controls.attachMedia(this.videoElement_);
           this.videoElement_.load();
@@ -853,6 +855,22 @@
       videoPlayerElement.hasAttribute('disabled');
 };
 
+/**
+ * Updates the Media Session API with the current playback state of the video
+ * element.
+ * @private
+ */
+VideoPlayer.prototype.updateMediaSessionPlaybackState_ = function() {
+  if (!navigator.mediaSession) {
+    return;
+  }
+
+  navigator.mediaSession.playbackState =
+      (this.videoElement_ && !this.videoElement_.paused) ?
+      MediaSessionPlaybackState.PLAYING :
+      MediaSessionPlaybackState.PAUSED;
+};
+
 var player = new VideoPlayer();
 
 /**
diff --git a/ui/file_manager/video_player/js/video_player_scripts.js b/ui/file_manager/video_player/js/video_player_scripts.js
index 1fc4d55..61fb124d 100644
--- a/ui/file_manager/video_player/js/video_player_scripts.js
+++ b/ui/file_manager/video_player/js/video_player_scripts.js
@@ -42,6 +42,7 @@
 // <include src="../../file_manager/common/js/async_util.js">
 // <include src="../../file_manager/common/js/file_type.js">
 // <include src="../../file_manager/common/js/util.js">
+// <include src="../../base/js/mediasession_types.js">
 // <include src="../../base/js/volume_manager_types.js">
 // <include src="../../base/js/filtered_volume_manager.js">
 
diff --git a/ui/gfx/color_utils.cc b/ui/gfx/color_utils.cc
index e8a8626..ce403f37 100644
--- a/ui/gfx/color_utils.cc
+++ b/ui/gfx/color_utils.cc
@@ -96,8 +96,8 @@
   float r = SkColorGetR(c) / 255.0f;
   float g = SkColorGetG(c) / 255.0f;
   float b = SkColorGetB(c) / 255.0f;
-  float vmax = std::max(std::max(r, g), b);
-  float vmin = std::min(std::min(r, g), b);
+  float vmax = std::max({r, g, b});
+  float vmin = std::min({r, g, b});
   float delta = vmax - vmin;
   hsl->l = (vmax + vmin) / 2;
   if (SkColorGetR(c) == SkColorGetG(c) && SkColorGetR(c) == SkColorGetB(c)) {
@@ -310,21 +310,6 @@
   return AlphaBlend(GetColorWithMaxContrast(color), color, alpha);
 }
 
-SkColor GetThemedAssetColor(SkColor theme_color) {
-  // Minimum theme light color contrast.
-  constexpr float kContrastLightItemThreshold = 3.0f;
-
-  // The amount to darken a light theme color by for use as foreground color.
-  constexpr float kThemedForegroundBlackFraction = 0.64f;
-
-  // This mimics |shouldUseLightForegroundOnBackground| from ColorUtils.java.
-  bool use_light_color = GetContrastRatio(SK_ColorWHITE, theme_color) >=
-                         kContrastLightItemThreshold;
-  if (use_light_color)
-    return SK_ColorWHITE;
-  return AlphaBlend(SK_ColorBLACK, theme_color, kThemedForegroundBlackFraction);
-}
-
 SkColor PickContrastingColor(SkColor foreground1,
                              SkColor foreground2,
                              SkColor background) {
diff --git a/ui/gfx/color_utils.h b/ui/gfx/color_utils.h
index 548ddd5..5f43d5c 100644
--- a/ui/gfx/color_utils.h
+++ b/ui/gfx/color_utils.h
@@ -122,9 +122,6 @@
 // Blends towards the color with max contrast by |alpha|.
 GFX_EXPORT SkColor BlendTowardMaxContrast(SkColor color, SkAlpha alpha);
 
-// This is a copy of |getThemedAssetColor()| in ColorUtils.java.
-GFX_EXPORT SkColor GetThemedAssetColor(SkColor theme_color);
-
 // Returns whichever of |foreground1| or |foreground2| has higher contrast with
 // |background|.
 GFX_EXPORT SkColor PickContrastingColor(SkColor foreground1,
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index c447ed27..3e61b5f 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -61,6 +61,11 @@
 #define EGL_GL_COLORSPACE_DISPLAY_P3_EXT 0x3363
 #endif /* EGL_EXT_gl_colorspace_display_p3 */
 
+#ifndef EGL_EXT_gl_colorspace_display_p3_passthrough
+#define EGL_EXT_gl_colorspace_display_p3_passthrough 1
+#define EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT 0x3490
+#endif /* EGL_EXT_gl_colorspace_display_p3_passthrough */
+
 // From ANGLE's egl/eglext.h.
 
 #ifndef EGL_ANGLE_platform_angle
@@ -150,6 +155,7 @@
 bool g_egl_context_priority_supported = false;
 bool g_egl_khr_colorspace = false;
 bool g_egl_ext_colorspace_display_p3 = false;
+bool g_egl_ext_colorspace_display_p3_passthrough = false;
 bool g_egl_flexible_surface_compatibility_supported = false;
 bool g_egl_robust_resource_init_supported = false;
 bool g_egl_display_texture_share_group_supported = false;
@@ -694,6 +700,8 @@
   g_egl_khr_colorspace = HasEGLExtension("EGL_KHR_gl_colorspace");
   g_egl_ext_colorspace_display_p3 =
       HasEGLExtension("EGL_EXT_gl_colorspace_display_p3");
+  g_egl_ext_colorspace_display_p3_passthrough =
+      HasEGLExtension("EGL_EXT_gl_colorspace_display_p3_passthrough");
   // According to https://source.android.com/compatibility/android-cdd.html the
   // EGL_IMG_context_priority extension is mandatory for Virtual Reality High
   // Performance support, but due to a bug in Android Nougat the extension
@@ -1041,9 +1049,20 @@
       // with the P3 gamut instead of the the sRGB gamut.
       // COLORSPACE_DISPLAY_P3_LINEAR has a linear transfer function, and is
       // intended for use with 16-bit formats.
-      if (g_egl_khr_colorspace && g_egl_ext_colorspace_display_p3) {
+      bool p3_supported = g_egl_ext_colorspace_display_p3 ||
+                          g_egl_ext_colorspace_display_p3_passthrough;
+      if (g_egl_khr_colorspace && p3_supported) {
         egl_window_attributes.push_back(EGL_GL_COLORSPACE_KHR);
-        egl_window_attributes.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+        // Chrome relied on incorrect Android behavior when dealing with P3 /
+        // framebuffer_srgb interactions. This behavior was fixed in Q, which
+        // causes invalid Chrome rendering. To achieve Android-P behavior in Q+,
+        // use EGL_GL_COLORSPACE_P3_PASSTHROUGH_EXT where possible.
+        if (g_egl_ext_colorspace_display_p3_passthrough) {
+          egl_window_attributes.push_back(
+              EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
+        } else {
+          egl_window_attributes.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+        }
       }
       break;
   }
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn
index a0f1ff8..b3babf7 100644
--- a/ui/keyboard/BUILD.gn
+++ b/ui/keyboard/BUILD.gn
@@ -88,6 +88,7 @@
   sources = [
     "test/keyboard_test_util.cc",
     "test/keyboard_test_util.h",
+    "test/test_keyboard_layout_delegate.cc",
     "test/test_keyboard_layout_delegate.h",
     "test/test_keyboard_ui.cc",
     "test/test_keyboard_ui.h",
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index 51102fa..b3cfc934 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -267,6 +267,9 @@
 
   for (KeyboardControllerObserver& observer : observer_list_)
     observer.OnKeyboardEnabledChanged(true);
+
+  ActivateKeyboardInContainer(
+      layout_delegate_->GetContainerForDefaultDisplay());
 }
 
 void KeyboardController::DisableKeyboard() {
@@ -340,6 +343,17 @@
   return parent_container_ ? parent_container_->GetRootWindow() : nullptr;
 }
 
+void KeyboardController::MoveToParentContainer(aura::Window* parent) {
+  DCHECK(parent);
+  if (parent_container_ == parent)
+    return;
+
+  TRACE_EVENT0("vk", "MoveKeyboardToDisplayInternal");
+
+  DeactivateKeyboard();
+  ActivateKeyboardInContainer(parent);
+}
+
 // private
 void KeyboardController::NotifyKeyboardBoundsChanging(
     const gfx::Rect& new_bounds) {
@@ -676,14 +690,14 @@
 void KeyboardController::ShowKeyboard(bool lock) {
   DVLOG(1) << "ShowKeyboard";
   set_keyboard_locked(lock);
-  ShowKeyboardInternal(display::Display());
+  ShowKeyboardInternal(layout_delegate_->GetContainerForDefaultDisplay());
 }
 
 void KeyboardController::ShowKeyboardInDisplay(
     const display::Display& display) {
   DVLOG(1) << "ShowKeyboardInDisplay: " << display.id();
   set_keyboard_locked(true);
-  ShowKeyboardInternal(display);
+  ShowKeyboardInternal(layout_delegate_->GetContainerForDisplay(display));
 }
 
 void KeyboardController::LoadKeyboardWindowInBackground() {
@@ -693,7 +707,8 @@
   if (state_ != KeyboardControllerState::INITIAL)
     return;
 
-  PopulateKeyboardContent(display::Display(), false);
+  PopulateKeyboardContent(layout_delegate_->GetContainerForDefaultDisplay(),
+                          false);
 }
 
 ui::InputMethod* KeyboardController::GetInputMethodForTest() {
@@ -816,18 +831,17 @@
   DVLOG(1) << "OnShowVirtualKeyboardIfEnabled: " << IsKeyboardEnableRequested();
   // Calling |ShowKeyboardInternal| may move the keyboard to another display.
   if (IsKeyboardEnableRequested() && !keyboard_locked_)
-    ShowKeyboardInternal(display::Display());
+    ShowKeyboardInternal(layout_delegate_->GetContainerForDefaultDisplay());
 }
 
-void KeyboardController::ShowKeyboardInternal(const display::Display& display) {
+void KeyboardController::ShowKeyboardInternal(aura::Window* target_container) {
   MarkKeyboardLoadStarted();
-  PopulateKeyboardContent(display, true);
+  PopulateKeyboardContent(target_container, true);
   UpdateInputMethodObserver();
 }
 
-void KeyboardController::PopulateKeyboardContent(
-    const display::Display& display,
-    bool show_keyboard) {
+void KeyboardController::PopulateKeyboardContent(aura::Window* target_container,
+                                                 bool show_keyboard) {
   DCHECK(show_keyboard || state_ == KeyboardControllerState::INITIAL);
 
   DVLOG(1) << "PopulateKeyboardContent: " << StateToStr(state_);
@@ -848,10 +862,7 @@
     parent_container_->AddChild(keyboard_window);
   }
 
-  if (display.is_valid())
-    layout_delegate_->MoveKeyboardToDisplay(display);
-  else
-    layout_delegate_->MoveKeyboardToTouchableDisplay();
+  MoveToParentContainer(target_container);
 
   aura::Window* keyboard_window = GetKeyboardWindow();
   DCHECK(keyboard_window);
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index b158d84..b60125c 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -96,16 +96,6 @@
   // Does nothing if the keyboard is already disabled.
   void DisableKeyboard();
 
-  // Attach the keyboard window as a child of the given parent window.
-  // Can only be called when the keyboard is not activated. |parent| must not
-  // have any children.
-  void ActivateKeyboardInContainer(aura::Window* parent);
-
-  // Detach the keyboard window from its parent container window.
-  // Can only be called when the keyboard is activated. Explicitly hides the
-  // keyboard if it is currently visible.
-  void DeactivateKeyboard();
-
   // Returns the keyboard window, or null if the keyboard window has not been
   // created yet.
   aura::Window* GetKeyboardWindow() const;
@@ -114,6 +104,10 @@
   // null if the keyboard has not been attached to any root window.
   aura::Window* GetRootWindow();
 
+  // Move the keyboard window to a different parent container. |parent| must not
+  // be null.
+  void MoveToParentContainer(aura::Window* parent);
+
   // Sets the bounds of the keyboard window.
   void SetKeyboardWindowBounds(const gfx::Rect& new_bounds);
 
@@ -318,9 +312,19 @@
   void OnTextInputStateChanged(const ui::TextInputClient* client) override;
   void OnShowVirtualKeyboardIfEnabled() override;
 
+  // Attach the keyboard window as a child of the given parent window.
+  // Can only be called when the keyboard is not activated. |parent| must not
+  // have any children.
+  void ActivateKeyboardInContainer(aura::Window* parent);
+
+  // Detach the keyboard window from its parent container window.
+  // Can only be called when the keyboard is activated. Explicitly hides the
+  // keyboard if it is currently visible.
+  void DeactivateKeyboard();
+
   // Show virtual keyboard immediately with animation.
-  void ShowKeyboardInternal(const display::Display& display);
-  void PopulateKeyboardContent(const display::Display& display,
+  void ShowKeyboardInternal(aura::Window* target_container);
+  void PopulateKeyboardContent(aura::Window* target_container,
                                bool show_keyboard);
 
   // Returns true if keyboard is scheduled to hide.
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 3a050d80..4ab8c573 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -153,14 +153,13 @@
     aura::test::AuraTestBase::SetUp();
     new wm::DefaultActivationClient(root_window());
     focus_controller_.reset(new TestFocusController(root_window()));
-    layout_delegate_.reset(new TestKeyboardLayoutDelegate());
+    layout_delegate_.reset(new TestKeyboardLayoutDelegate(root_window()));
 
     // Force enable the virtual keyboard.
     keyboard::SetTouchKeyboardEnabled(true);
     controller_.EnableKeyboard(
         std::make_unique<TestKeyboardUI>(host()->GetInputMethod()),
         layout_delegate_.get());
-    controller_.ActivateKeyboardInContainer(root_window());
     controller_.AddObserver(this);
   }
 
@@ -684,22 +683,6 @@
   EXPECT_EQ(1.0, layer->opacity());
 }
 
-TEST_F(KeyboardControllerAnimationTest,
-       SetKeyboardWindowBoundsOnDeactivatedKeyboard) {
-  // Ensure keyboard ui is populated
-  ui::Layer* layer = keyboard_window()->layer();
-  ShowKeyboard();
-  RunAnimationForLayer(layer);
-
-  ASSERT_TRUE(keyboard_window());
-
-  controller().DeactivateKeyboard();
-
-  // lingering handle to the contents window is adjusted.
-  // container_window's LayoutManager should abort silently and not crash.
-  keyboard_window()->SetBounds(gfx::Rect());
-}
-
 TEST_F(KeyboardControllerTest, DisplayChangeShouldNotifyBoundsChange) {
   ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT);
 
diff --git a/ui/keyboard/keyboard_layout_delegate.h b/ui/keyboard/keyboard_layout_delegate.h
index 5e01cde8..1274e1a 100644
--- a/ui/keyboard/keyboard_layout_delegate.h
+++ b/ui/keyboard/keyboard_layout_delegate.h
@@ -13,6 +13,10 @@
 class Display;
 }
 
+namespace aura {
+class Window;
+}
+
 namespace keyboard {
 
 // A delegate class to control the virtual keyboard layout
@@ -20,11 +24,14 @@
  public:
   virtual ~KeyboardLayoutDelegate() {}
 
-  virtual void MoveKeyboardToDisplay(const display::Display& display) = 0;
+  // Get the container window where the virtual keyboard show appear by default.
+  // Usually, this would be a touchable display with input focus.
+  // This function must not return null.
+  virtual aura::Window* GetContainerForDefaultDisplay() = 0;
 
-  // Move the keyboard to the touchable display which has the input focus, or
-  // the first touchable display.
-  virtual void MoveKeyboardToTouchableDisplay() = 0;
+  // Get the container window for a particular display. |display| must be valid.
+  virtual aura::Window* GetContainerForDisplay(
+      const display::Display& display) = 0;
 };
 
 }  // namespace keyboard
diff --git a/ui/keyboard/keyboard_util_unittest.cc b/ui/keyboard/keyboard_util_unittest.cc
index a86e7a6e..333e0a3 100644
--- a/ui/keyboard/keyboard_util_unittest.cc
+++ b/ui/keyboard/keyboard_util_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/test/aura_test_base.h"
 #include "ui/base/ime/dummy_input_method.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/keyboard/keyboard_ui.h"
@@ -17,7 +18,7 @@
 namespace keyboard {
 namespace {
 
-class KeyboardUtilTest : public testing::Test {
+class KeyboardUtilTest : public aura::test::AuraTestBase {
  public:
   KeyboardUtilTest() {}
   ~KeyboardUtilTest() override {}
@@ -53,7 +54,10 @@
     ClearEnableFlag(mojom::KeyboardEnableFlag::kExtensionEnabled);
   }
 
-  void SetUp() override { ResetAllFlags(); }
+  void SetUp() override {
+    aura::test::AuraTestBase::SetUp();
+    ResetAllFlags();
+  }
 
  protected:
   void SetEnableFlag(mojom::KeyboardEnableFlag flag) {
@@ -154,6 +158,11 @@
 TEST_F(KeyboardUtilTest, IsOverscrollEnabled) {
   ResetAllFlags();
 
+  ui::DummyInputMethod input_method;
+  TestKeyboardLayoutDelegate layout_delegate(root_window());
+  keyboard_controller_.EnableKeyboard(
+      std::make_unique<TestKeyboardUI>(&input_method), &layout_delegate);
+
   // Return false when keyboard is disabled.
   EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled());
 
@@ -168,19 +177,17 @@
   keyboard_controller_.UpdateKeyboardConfig(config);
   EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled());
 
+  // Set default overscroll flag.
   config.overscroll_behavior =
       keyboard::mojom::KeyboardOverscrollBehavior::kDefault;
   keyboard_controller_.UpdateKeyboardConfig(config);
   EXPECT_TRUE(keyboard_controller_.IsKeyboardOverscrollEnabled());
 
   // Set keyboard_locked() to true.
-  ui::DummyInputMethod input_method;
-  TestKeyboardLayoutDelegate layout_delegate;
-  keyboard_controller_.EnableKeyboard(
-      std::make_unique<TestKeyboardUI>(&input_method), &layout_delegate);
   keyboard_controller_.set_keyboard_locked(true);
   EXPECT_TRUE(keyboard_controller_.keyboard_locked());
   EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled());
+
   keyboard_controller_.DisableKeyboard();
 }
 
diff --git a/ui/keyboard/test/test_keyboard_layout_delegate.cc b/ui/keyboard/test/test_keyboard_layout_delegate.cc
new file mode 100644
index 0000000..48b1359
--- /dev/null
+++ b/ui/keyboard/test/test_keyboard_layout_delegate.cc
@@ -0,0 +1,24 @@
+// 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 "ui/keyboard/test/test_keyboard_layout_delegate.h"
+
+#include "ui/aura/window.h"
+
+namespace keyboard {
+
+TestKeyboardLayoutDelegate::TestKeyboardLayoutDelegate(
+    aura::Window* root_window)
+    : root_window_(root_window) {}
+
+aura::Window* TestKeyboardLayoutDelegate::GetContainerForDefaultDisplay() {
+  return root_window_;
+}
+
+aura::Window* TestKeyboardLayoutDelegate::GetContainerForDisplay(
+    const display::Display& display) {
+  return root_window_;
+}
+
+}  // namespace keyboard
diff --git a/ui/keyboard/test/test_keyboard_layout_delegate.h b/ui/keyboard/test/test_keyboard_layout_delegate.h
index 522e5fd..996baec 100644
--- a/ui/keyboard/test/test_keyboard_layout_delegate.h
+++ b/ui/keyboard/test/test_keyboard_layout_delegate.h
@@ -5,20 +5,30 @@
 #ifndef UI_KEYBOARD_TEST_TEST_KEYBOARD_LAYOUT_DELEGATE_H_
 #define UI_KEYBOARD_TEST_TEST_KEYBOARD_LAYOUT_DELEGATE_H_
 
+#include "base/macros.h"
 #include "ui/keyboard/keyboard_layout_delegate.h"
 
+namespace aura {
+class Window;
+};
+
 namespace keyboard {
 
 class TestKeyboardLayoutDelegate : public KeyboardLayoutDelegate {
  public:
-  TestKeyboardLayoutDelegate() = default;
+  // |root_window| is the window that is always returned by the
+  // KeyboardLayoutDelegate methods.
+  explicit TestKeyboardLayoutDelegate(aura::Window* root_window);
   ~TestKeyboardLayoutDelegate() override = default;
 
   // Overridden from keyboard::KeyboardLayoutDelegate
-  void MoveKeyboardToDisplay(const display::Display& display) override {}
-  void MoveKeyboardToTouchableDisplay() override {}
+  aura::Window* GetContainerForDefaultDisplay() override;
+  aura::Window* GetContainerForDisplay(
+      const display::Display& display) override;
 
  private:
+  aura::Window* root_window_;
+
   DISALLOW_COPY_AND_ASSIGN(TestKeyboardLayoutDelegate);
 };
 
diff --git a/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
index 9a04434..1a90e57 100644
--- a/ui/ozone/platform/scenic/ozone_platform_scenic.cc
+++ b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
@@ -124,17 +124,19 @@
 
     base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
 
-    surface_factory_ =
-        std::make_unique<ScenicSurfaceFactory>(window_manager_.get());
-
     scenic_gpu_host_ = std::make_unique<ScenicGpuHost>(window_manager_.get());
+    scenic_gpu_host_ptr_ = scenic_gpu_host_->CreateHostProcessSelfBinding();
+
+    surface_factory_ =
+        std::make_unique<ScenicSurfaceFactory>(scenic_gpu_host_ptr_.get());
   }
 
   void InitializeGPU(const InitParams& params) override {
-    scenic_gpu_service_ = std::make_unique<ScenicGpuService>();
+    scenic_gpu_service_ = std::make_unique<ScenicGpuService>(
+        mojo::MakeRequest(&scenic_gpu_host_ptr_));
     DCHECK(!surface_factory_);
     surface_factory_ =
-        std::make_unique<ScenicSurfaceFactory>(scenic_gpu_service_.get());
+        std::make_unique<ScenicSurfaceFactory>(scenic_gpu_host_ptr_.get());
   }
 
   base::MessageLoop::Type GetMessageLoopTypeForGpu() override {
@@ -165,6 +167,8 @@
   std::unique_ptr<ScenicGpuService> scenic_gpu_service_;
   std::unique_ptr<ScenicSurfaceFactory> surface_factory_;
 
+  mojom::ScenicGpuHostPtr scenic_gpu_host_ptr_;
+
   DISALLOW_COPY_AND_ASSIGN(OzonePlatformScenic);
 };
 
diff --git a/ui/ozone/platform/scenic/scenic_gpu_host.cc b/ui/ozone/platform/scenic/scenic_gpu_host.cc
index afb7c6e..ad21783d 100644
--- a/ui/ozone/platform/scenic/scenic_gpu_host.cc
+++ b/ui/ozone/platform/scenic/scenic_gpu_host.cc
@@ -35,7 +35,8 @@
 
 ScenicGpuHost::ScenicGpuHost(ScenicWindowManager* scenic_window_manager)
     : scenic_window_manager_(scenic_window_manager),
-      binding_(this),
+      host_binding_(this),
+      gpu_binding_(this),
       ui_thread_runner_(base::ThreadTaskRunnerHandle::Get()),
       weak_ptr_factory_(this) {
   DETACH_FROM_THREAD(io_thread_checker_);
@@ -45,6 +46,13 @@
   DCHECK_CALLED_ON_VALID_THREAD(ui_thread_checker_);
 }
 
+mojom::ScenicGpuHostPtr ScenicGpuHost::CreateHostProcessSelfBinding() {
+  DCHECK(!host_binding_.is_bound());
+  mojom::ScenicGpuHostPtr gpu_host;
+  host_binding_.Bind(mojo::MakeRequest(&gpu_host));
+  return gpu_host;
+}
+
 void ScenicGpuHost::ExportParent(int32_t surface_handle,
                                  mojo::ScopedHandle export_token_mojo) {
   DCHECK_CALLED_ON_VALID_THREAD(ui_thread_checker_);
@@ -87,8 +95,8 @@
   DCHECK_CALLED_ON_VALID_THREAD(ui_thread_checker_);
 
   mojom::ScenicGpuHostPtr gpu_host;
-  binding_.Close();
-  binding_.Bind(mojo::MakeRequest(&gpu_host));
+  gpu_binding_.Close();
+  gpu_binding_.Bind(mojo::MakeRequest(&gpu_host));
 
   gpu_service_.Bind(std::move(gpu_service_ptr_info));
   gpu_service_->Initialize(std::move(gpu_host));
diff --git a/ui/ozone/platform/scenic/scenic_gpu_host.h b/ui/ozone/platform/scenic/scenic_gpu_host.h
index 5c0cdea..26bc402 100644
--- a/ui/ozone/platform/scenic/scenic_gpu_host.h
+++ b/ui/ozone/platform/scenic/scenic_gpu_host.h
@@ -32,6 +32,10 @@
   ScenicGpuHost(ScenicWindowManager* scenic_window_manager);
   ~ScenicGpuHost() override;
 
+  // Creates browser process binding. This is used to create a software output
+  // on the UI thread.
+  mojom::ScenicGpuHostPtr CreateHostProcessSelfBinding();
+
   // mojom::ScenicGpuHost:
   void ExportParent(int32_t surface_handle,
                     mojo::ScopedHandle export_token_mojo) override;
@@ -58,7 +62,8 @@
                      mojom::ScenicGpuHostRequest scenic_gpu_host_request);
 
   ScenicWindowManager* const scenic_window_manager_;
-  mojo::Binding<mojom::ScenicGpuHost> binding_;
+  mojo::Binding<mojom::ScenicGpuHost> host_binding_;
+  mojo::Binding<mojom::ScenicGpuHost> gpu_binding_;
 
   mojom::ScenicGpuServicePtr gpu_service_;
   scoped_refptr<base::SingleThreadTaskRunner> ui_thread_runner_;
diff --git a/ui/ozone/platform/scenic/scenic_gpu_service.cc b/ui/ozone/platform/scenic/scenic_gpu_service.cc
index 2d10d525..49a8c5a 100644
--- a/ui/ozone/platform/scenic/scenic_gpu_service.cc
+++ b/ui/ozone/platform/scenic/scenic_gpu_service.cc
@@ -29,9 +29,8 @@
 
 }  // namespace
 
-ScenicGpuService::ScenicGpuService()
-    : gpu_host_request_(mojo::MakeRequest(&gpu_host_)),
-      weak_ptr_factory_(this) {}
+ScenicGpuService::ScenicGpuService(mojom::ScenicGpuHostRequest gpu_host_request)
+    : gpu_host_request_(std::move(gpu_host_request)), weak_ptr_factory_(this) {}
 
 ScenicGpuService::~ScenicGpuService() {}
 
diff --git a/ui/ozone/platform/scenic/scenic_gpu_service.h b/ui/ozone/platform/scenic/scenic_gpu_service.h
index 8bb6745..07774e0c 100644
--- a/ui/ozone/platform/scenic/scenic_gpu_service.h
+++ b/ui/ozone/platform/scenic/scenic_gpu_service.h
@@ -22,11 +22,9 @@
 // so that surfaces can present to Scenic views managed by the browser.
 class ScenicGpuService : public mojom::ScenicGpuService {
  public:
-  ScenicGpuService();
+  ScenicGpuService(mojom::ScenicGpuHostRequest gpu_host_request);
   ~ScenicGpuService() override;
 
-  mojom::ScenicGpuHost* gpu_host() { return gpu_host_.get(); }
-
   base::RepeatingCallback<void(mojom::ScenicGpuServiceRequest)>
   GetBinderCallback();
 
@@ -36,7 +34,6 @@
  private:
   void AddBinding(mojom::ScenicGpuServiceRequest request);
 
-  mojom::ScenicGpuHostPtr gpu_host_;
   mojom::ScenicGpuHostRequest gpu_host_request_;
 
   mojo::BindingSet<mojom::ScenicGpuService> binding_set_;
diff --git a/ui/ozone/platform/scenic/scenic_surface.cc b/ui/ozone/platform/scenic/scenic_surface.cc
index aa1c0d2..ae540c1 100644
--- a/ui/ozone/platform/scenic/scenic_surface.cc
+++ b/ui/ozone/platform/scenic/scenic_surface.cc
@@ -13,16 +13,15 @@
 
 namespace ui {
 
-ScenicSurface::ScenicSurface(ScenicSurfaceFactory* scenic_surface_factory,
-                             fuchsia::ui::scenic::Scenic* scenic,
-                             mojom::ScenicGpuHost* gpu_host,
-                             gfx::AcceleratedWidget window)
-    : scenic_session_(scenic),
+ScenicSurface::ScenicSurface(
+    ScenicSurfaceFactory* scenic_surface_factory,
+    gfx::AcceleratedWidget window,
+    scenic::SessionPtrAndListenerRequest sesion_and_listener_request)
+    : scenic_session_(std::move(sesion_and_listener_request)),
       parent_(&scenic_session_),
       shape_(&scenic_session_),
       material_(&scenic_session_),
       scenic_surface_factory_(scenic_surface_factory),
-      gpu_host_(gpu_host),
       window_(window) {
   shape_.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f));
   shape_.SetMaterial(material_);
@@ -42,24 +41,26 @@
       image_pipe_id, std::move(image_pipe_request)));
   material_.SetTexture(image_pipe_id);
   scenic_session_.ReleaseResource(image_pipe_id);
+  scenic_session_.Present(
+      /*presentation_time=*/0, [](fuchsia::images::PresentationInfo info) {});
 }
 
-void ScenicSurface::LinkToParent() {
+void ScenicSurface::SetTextureToImage(const scenic::Image& image) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  material_.SetTexture(image);
+}
+
+mojo::ScopedHandle ScenicSurface::CreateParentExportToken() {
   // Scenic does not care about order here; it's totally fine for imports to
   // cause exports, and that's what's done here.
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   zx::eventpair export_token;
   parent_.BindAsRequest(&export_token);
   parent_.AddChild(shape_);
-  gpu_host_->ExportParent(
-      window_,
-      mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(export_token))));
-}
-
-void ScenicSurface::Commit() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   scenic_session_.Present(
       /*presentation_time=*/0, [](fuchsia::images::PresentationInfo info) {});
+  return mojo::WrapPlatformHandle(
+      mojo::PlatformHandle(std::move(export_token)));
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/scenic_surface.h b/ui/ozone/platform/scenic/scenic_surface.h
index e8bc5ba..080739b 100644
--- a/ui/ozone/platform/scenic/scenic_surface.h
+++ b/ui/ozone/platform/scenic/scenic_surface.h
@@ -11,15 +11,12 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/system/handle.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/public/platform_window_surface.h"
 
 namespace ui {
 
-namespace mojom {
-class ScenicGpuHost;
-}
-
 class ScenicSurfaceFactory;
 
 // Holder for Scenic resources backing rendering surface.
@@ -31,21 +28,30 @@
 // The texture is updated through an image pipe.
 class ScenicSurface : public ui::PlatformWindowSurface {
  public:
-  ScenicSurface(ScenicSurfaceFactory* scenic_surface_factory,
-                fuchsia::ui::scenic::Scenic* scenic,
-                mojom::ScenicGpuHost* gpu_host,
-                gfx::AcceleratedWidget window);
+  ScenicSurface(
+      ScenicSurfaceFactory* scenic_surface_factory,
+      gfx::AcceleratedWidget window,
+      scenic::SessionPtrAndListenerRequest sesion_and_listener_request);
   ~ScenicSurface() override;
 
   // Sets the texture of the surface to a new image pipe.
   void SetTextureToNewImagePipe(
       fidl::InterfaceRequest<fuchsia::images::ImagePipe> image_pipe_request);
 
-  // Links the surface to the window in the browser process.
-  void LinkToParent();
+  // Sets the texture of the surface to an image resource.
+  void SetTextureToImage(const scenic::Image& image);
 
-  // Flushes commands to scenic & executes them.
-  void Commit();
+  // Creates token to links the surface to the window in the browser process.
+  mojo::ScopedHandle CreateParentExportToken();
+
+  void AssertBelongsToCurrentThread() {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  }
+
+  scenic::Session* scenic_session() {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    return &scenic_session_;
+  }
 
  private:
   scenic::Session scenic_session_;
@@ -54,7 +60,6 @@
   scenic::Material material_;
 
   ScenicSurfaceFactory* const scenic_surface_factory_;
-  mojom::ScenicGpuHost* const gpu_host_;
   const gfx::AcceleratedWidget window_;
 
   THREAD_CHECKER(thread_checker_);
diff --git a/ui/ozone/platform/scenic/scenic_surface_factory.cc b/ui/ozone/platform/scenic/scenic_surface_factory.cc
index 4846fbed..212a174c 100644
--- a/ui/ozone/platform/scenic/scenic_surface_factory.cc
+++ b/ui/ozone/platform/scenic/scenic_surface_factory.cc
@@ -106,25 +106,14 @@
 
 }  // namespace
 
-ScenicSurfaceFactory::ScenicSurfaceFactory(ScenicWindowManager* window_manager)
-    : window_manager_(window_manager),
-      egl_implementation_(std::make_unique<GLOzoneEGLScenic>()) {}
+ScenicSurfaceFactory::ScenicSurfaceFactory(mojom::ScenicGpuHost* gpu_host)
+    : gpu_host_(gpu_host),
+      egl_implementation_(std::make_unique<GLOzoneEGLScenic>()),
+      main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      weak_ptr_factory_(this) {}
 
-ScenicSurfaceFactory::ScenicSurfaceFactory(ScenicGpuService* scenic_gpu_service)
-    : scenic_gpu_service_(scenic_gpu_service),
-      egl_implementation_(std::make_unique<GLOzoneEGLScenic>()) {}
-
-ScenicSurfaceFactory::~ScenicSurfaceFactory() = default;
-
-fuchsia::ui::scenic::Scenic* ScenicSurfaceFactory::GetScenic() {
-  if (!scenic_) {
-    scenic_ = base::fuchsia::ComponentContext::GetDefault()
-                  ->ConnectToService<fuchsia::ui::scenic::Scenic>();
-    scenic_.set_error_handler([](zx_status_t status) {
-      ZX_LOG(FATAL, status) << "Scenic connection failed";
-    });
-  }
-  return scenic_.get();
+ScenicSurfaceFactory::~ScenicSurfaceFactory() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 std::vector<gl::GLImplementation>
@@ -145,19 +134,19 @@
 std::unique_ptr<PlatformWindowSurface>
 ScenicSurfaceFactory::CreatePlatformWindowSurface(
     gfx::AcceleratedWidget widget) {
-  return std::make_unique<ScenicSurface>(
-      this, GetScenic(), scenic_gpu_service_->gpu_host(), widget);
+  auto surface =
+      std::make_unique<ScenicSurface>(this, widget, CreateScenicSession());
+  main_thread_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&ScenicSurfaceFactory::LinkSurfaceToParent,
+                                weak_ptr_factory_.GetWeakPtr(), widget,
+                                surface->CreateParentExportToken()));
+  return surface;
 }
 
 std::unique_ptr<SurfaceOzoneCanvas> ScenicSurfaceFactory::CreateCanvasForWidget(
     gfx::AcceleratedWidget widget) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!window_manager_)
-    LOG(FATAL) << "Software output not supported from GPU process";
-  ScenicWindow* window = window_manager_->GetWindow(widget);
-  if (!window)
-    return nullptr;
-  return std::make_unique<ScenicWindowCanvas>(GetScenic(), window);
+  ScenicSurface* surface = GetSurface(widget);
+  return std::make_unique<ScenicWindowCanvas>(surface);
 }
 
 scoped_refptr<gfx::NativePixmap> ScenicSurfaceFactory::CreateNativePixmap(
@@ -172,7 +161,7 @@
 std::unique_ptr<gpu::VulkanImplementation>
 ScenicSurfaceFactory::CreateVulkanImplementation() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!scenic_gpu_service_)
+  if (!gpu_host_)
     LOG(FATAL) << "Vulkan implementation requires InitializeForGPU";
 
   return std::make_unique<ui::VulkanImplementationScenic>(this);
@@ -181,22 +170,62 @@
 
 void ScenicSurfaceFactory::AddSurface(gfx::AcceleratedWidget widget,
                                       ScenicSurface* surface) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::AutoLock lock(surface_lock_);
   DCHECK(!base::ContainsKey(surface_map_, widget));
+  surface->AssertBelongsToCurrentThread();
   surface_map_.insert(std::make_pair(widget, surface));
 }
 
 void ScenicSurfaceFactory::RemoveSurface(gfx::AcceleratedWidget widget) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(base::ContainsKey(surface_map_, widget));
-  surface_map_.erase(widget);
+  base::AutoLock lock(surface_lock_);
+  auto it = surface_map_.find(widget);
+  DCHECK(it != surface_map_.end());
+  ScenicSurface* surface = it->second;
+  surface->AssertBelongsToCurrentThread();
+  surface_map_.erase(it);
 }
 
 ScenicSurface* ScenicSurfaceFactory::GetSurface(gfx::AcceleratedWidget widget) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::AutoLock lock(surface_lock_);
   auto it = surface_map_.find(widget);
   DCHECK(it != surface_map_.end());
-  return it->second;
+  ScenicSurface* surface = it->second;
+  surface->AssertBelongsToCurrentThread();
+  return surface;
+}
+
+scenic::SessionPtrAndListenerRequest
+ScenicSurfaceFactory::CreateScenicSession() {
+  fuchsia::ui::scenic::SessionPtr session;
+  fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener_handle;
+  auto listener_request = listener_handle.NewRequest();
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ScenicSurfaceFactory::CreateScenicSessionOnMainThread,
+                     weak_ptr_factory_.GetWeakPtr(), session.NewRequest(),
+                     listener_handle.Bind()));
+  return {std::move(session), std::move(listener_request)};
+}
+
+void ScenicSurfaceFactory::CreateScenicSessionOnMainThread(
+    fidl::InterfaceRequest<fuchsia::ui::scenic::Session> session_request,
+    fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!scenic_) {
+    scenic_ = base::fuchsia::ComponentContext::GetDefault()
+                  ->ConnectToService<fuchsia::ui::scenic::Scenic>();
+    scenic_.set_error_handler([](zx_status_t status) {
+      ZX_LOG(FATAL, status) << "Scenic connection failed";
+    });
+  }
+  scenic_->CreateSession(std::move(session_request), std::move(listener));
+}
+
+void ScenicSurfaceFactory::LinkSurfaceToParent(
+    gfx::AcceleratedWidget widget,
+    mojo::ScopedHandle export_token_mojo) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  gpu_host_->ExportParent(widget, std::move(export_token_mojo));
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/scenic_surface_factory.h b/ui/ozone/platform/scenic/scenic_surface_factory.h
index eec04f77..8ee99a3f 100644
--- a/ui/ozone/platform/scenic/scenic_surface_factory.h
+++ b/ui/ozone/platform/scenic/scenic_surface_factory.h
@@ -6,26 +6,28 @@
 #define UI_OZONE_PLATFORM_SCENIC_SCENIC_SURFACE_FACTORY_H_
 
 #include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/ui/scenic/cpp/session.h>
 #include <memory>
 #include <vector>
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_annotations.h"
 #include "base/threading/thread_checker.h"
 #include "gpu/vulkan/buildflags.h"
+#include "mojo/public/cpp/system/handle.h"
 #include "ui/ozone/public/gl_ozone.h"
+#include "ui/ozone/public/interfaces/scenic_gpu_host.mojom.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 
 namespace ui {
 
-class ScenicWindowManager;
-class ScenicGpuService;
 class ScenicSurface;
 
 class ScenicSurfaceFactory : public SurfaceFactoryOzone {
  public:
-  explicit ScenicSurfaceFactory(ScenicWindowManager* window_manager);
-  explicit ScenicSurfaceFactory(ScenicGpuService* scenic_gpu_service);
+  explicit ScenicSurfaceFactory(mojom::ScenicGpuHost* gpu_host);
   ~ScenicSurfaceFactory() override;
 
   // SurfaceFactoryOzone implementation.
@@ -45,22 +47,53 @@
       override;
 #endif
 
-  void AddSurface(gfx::AcceleratedWidget widget, ScenicSurface* surface);
-  void RemoveSurface(gfx::AcceleratedWidget widget);
-  ScenicSurface* GetSurface(gfx::AcceleratedWidget widget);
+  // Registers a surface for a |widget|.
+  //
+  // Must be called on the thread that owns the surface.
+  void AddSurface(gfx::AcceleratedWidget widget, ScenicSurface* surface)
+      LOCKS_EXCLUDED(surface_lock_);
+
+  // Removes a surface for a |widget|.
+  //
+  // Must be called on the thread that owns the surface.
+  void RemoveSurface(gfx::AcceleratedWidget widget)
+      LOCKS_EXCLUDED(surface_lock_);
+
+  // Returns the surface for a |widget|.
+  //
+  // Must be called on the thread that owns the surface.
+  ScenicSurface* GetSurface(gfx::AcceleratedWidget widget)
+      LOCKS_EXCLUDED(surface_lock_);
 
  private:
-  fuchsia::ui::scenic::Scenic* GetScenic();
+  // Creates a new scenic session on any thread.
+  scenic::SessionPtrAndListenerRequest CreateScenicSession();
 
-  base::flat_map<gfx::AcceleratedWidget, ScenicSurface*> surface_map_;
+  // Creates a new scenic session on the main thread.
+  void CreateScenicSessionOnMainThread(
+      fidl::InterfaceRequest<fuchsia::ui::scenic::Session> session_request,
+      fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener);
 
-  ScenicWindowManager* const window_manager_ = nullptr;
-  ScenicGpuService* scenic_gpu_service_ = nullptr;
+  // Links a surface to its parent in the host process.
+  void LinkSurfaceToParent(gfx::AcceleratedWidget widget,
+                           mojo::ScopedHandle export_token_mojo);
+
+  base::flat_map<gfx::AcceleratedWidget, ScenicSurface*> surface_map_
+      GUARDED_BY(surface_lock_);
+  base::Lock surface_lock_;
+
+  mojom::ScenicGpuHost* const gpu_host_;
   std::unique_ptr<GLOzone> egl_implementation_;
+
   fuchsia::ui::scenic::ScenicPtr scenic_;
 
+  // Task runner for thread that |scenic_| and |gpu_host_| are bound on.
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
   THREAD_CHECKER(thread_checker_);
 
+  base::WeakPtrFactory<ScenicSurfaceFactory> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ScenicSurfaceFactory);
 };
 
diff --git a/ui/ozone/platform/scenic/scenic_window_canvas.cc b/ui/ozone/platform/scenic/scenic_window_canvas.cc
index 439e73ea..1559148 100644
--- a/ui/ozone/platform/scenic/scenic_window_canvas.cc
+++ b/ui/ozone/platform/scenic/scenic_window_canvas.cc
@@ -66,21 +66,8 @@
   dirty_region.setEmpty();
 }
 
-ScenicWindowCanvas::ScenicWindowCanvas(fuchsia::ui::scenic::Scenic* scenic,
-                                       ScenicWindow* window)
-    : window_(window),
-      scenic_session_(scenic),
-      parent_(&scenic_session_),
-      material_(&scenic_session_) {
-  scenic::ShapeNode shape(&scenic_session_);
-  shape.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f));
-  shape.SetMaterial(material_);
-
-  zx::eventpair export_token;
-  parent_.BindAsRequest(&export_token);
-  parent_.AddChild(shape);
-  window_->ExportRenderingEntity(std::move(export_token));
-}
+ScenicWindowCanvas::ScenicWindowCanvas(ScenicSurface* scenic_surface)
+    : scenic_surface_(scenic_surface) {}
 
 ScenicWindowCanvas::~ScenicWindowCanvas() = default;
 
@@ -90,7 +77,7 @@
 
   // Allocate new buffers with the new size.
   for (int i = 0; i < kNumBuffers; ++i) {
-    frames_[i].Initialize(viewport_size_, &scenic_session_);
+    frames_[i].Initialize(viewport_size_, scenic_surface_->scenic_session());
   }
 }
 
@@ -151,7 +138,8 @@
       viewport_size_.width() * SkColorTypeBytesPerPixel(kN32_SkColorType);
   scenic::Image image(*frames_[current_frame_].scenic_memory, 0,
                       std::move(info));
-  material_.SetTexture(image);
+  // TODO(spang): Consider using ImagePipe for consistency with vulkan path.
+  scenic_surface_->SetTextureToImage(image);
 
   // Create release fence for the current buffer or reset it if it already
   // exists.
@@ -172,9 +160,10 @@
   auto status = frames_[current_frame_].release_fence.duplicate(
       ZX_RIGHT_SAME_RIGHTS, &release_fence_dup);
   ZX_CHECK(status == ZX_OK, status);
-  scenic_session_.EnqueueReleaseFence(std::move(release_fence_dup));
-  scenic_session_.Present(/*presentation_time=*/0,
-                          [](fuchsia::images::PresentationInfo info) {});
+  scenic_surface_->scenic_session()->EnqueueReleaseFence(
+      std::move(release_fence_dup));
+  scenic_surface_->scenic_session()->Present(
+      /*presentation_time=*/0, [](fuchsia::images::PresentationInfo info) {});
 
   // Move to the next buffer.
   current_frame_ = (current_frame_ + 1) % kNumBuffers;
diff --git a/ui/ozone/platform/scenic/scenic_window_canvas.h b/ui/ozone/platform/scenic/scenic_window_canvas.h
index 8d366fb..f385a96 100644
--- a/ui/ozone/platform/scenic/scenic_window_canvas.h
+++ b/ui/ozone/platform/scenic/scenic_window_canvas.h
@@ -12,6 +12,7 @@
 #include "third_party/skia/include/core/SkRegion.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/scenic/scenic_surface.h"
 #include "ui/ozone/platform/scenic/scenic_surface_factory.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
 
@@ -27,10 +28,9 @@
 // ScenicWindow.
 class ScenicWindowCanvas : public SurfaceOzoneCanvas {
  public:
-  // |window| must outlive the surface. ScenicWindow owns the scenic::Session
-  // used in this class for all drawing operations.
-  explicit ScenicWindowCanvas(fuchsia::ui::scenic::Scenic* scenic,
-                              ScenicWindow* window);
+  // |scenic_surface| must outlive the canvas. ScenicSurface owns the
+  // scenic::Session used in this class for all drawing operations.
+  explicit ScenicWindowCanvas(ScenicSurface* scenic_surface);
   ~ScenicWindowCanvas() override;
 
   // SurfaceOzoneCanvas implementation.
@@ -73,8 +73,6 @@
     SkRegion dirty_region;
   };
 
-  ScenicWindow* const window_;
-
   Frame frames_[kNumBuffers];
 
   // Buffer index in |frames_| for the frame that's currently being rendered.
@@ -83,9 +81,7 @@
   // View size in device pixels.
   gfx::Size viewport_size_;
 
-  scenic::Session scenic_session_;
-  scenic::ImportNode parent_;
-  scenic::Material material_;
+  ScenicSurface* const scenic_surface_;
 
   DISALLOW_COPY_AND_ASSIGN(ScenicWindowCanvas);
 };
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
index 3243992..d2a4379d 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -70,12 +70,8 @@
 
 std::unique_ptr<gpu::VulkanSurface>
 VulkanImplementationScenic::CreateViewSurface(gfx::AcceleratedWidget window) {
-  ScenicSurface* scenic_surface = scenic_surface_factory_->GetSurface(window);
-
-  // Attach the surface to the window.
-  scenic_surface->LinkToParent();
-
   fuchsia::images::ImagePipePtr image_pipe;
+  ScenicSurface* scenic_surface = scenic_surface_factory_->GetSurface(window);
   scenic_surface->SetTextureToNewImagePipe(image_pipe.NewRequest());
 
   VkSurfaceKHR surface;
@@ -94,11 +90,6 @@
     LOG(FATAL) << "vkCreateImagePipeSurfaceFUCHSIA failed: " << result;
   }
 
-  // Execute the initialization commands. Once this is done we won't need to
-  // make any further changes to ScenicSurface other than to keep it alive; the
-  // texture can be replaced through the vulkan swapchain API.
-  scenic_surface->Commit();
-
   return std::make_unique<gpu::VulkanSurface>(GetVulkanInstance(), surface);
 }
 
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 62a28d66..7863d712 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -185,8 +185,6 @@
     return model_->IsItemEnabledAt(index);
   }
 
-  void HighlightChangedTo(int index) override {}
-
   void ActivatedAt(int index) override {
     owner_->selected_index_ = index;
     owner_->OnPerformAction();
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 8751786..d813c85 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -1249,9 +1249,6 @@
         (selection_types & SELECTION_OPEN_SUBMENU) != 0);
   }
 
-  if (menu_item && menu_item->GetDelegate())
-    menu_item->GetDelegate()->SelectionChanged(menu_item);
-
   DCHECK(menu_item || (selection_types & SELECTION_EXIT) != 0);
 
   pending_state_.item = menu_item;
diff --git a/ui/views/controls/menu/menu_delegate.h b/ui/views/controls/menu/menu_delegate.h
index 69a8ed5..234ef7e 100644
--- a/ui/views/controls/menu/menu_delegate.h
+++ b/ui/views/controls/menu/menu_delegate.h
@@ -194,10 +194,6 @@
   // Views that are not MenuItemViews.
   virtual bool ShouldCloseOnDragComplete();
 
-  // Notification that the user has highlighted the specified item.
-  virtual void SelectionChanged(MenuItemView* menu) {
-  }
-
   // Notification the menu has closed. This will not be called if MenuRunner is
   // deleted during calls to ExecuteCommand().
   virtual void OnMenuClosed(MenuItemView* menu) {}
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index 08132c52..c91415cb 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -220,22 +220,6 @@
   return false;
 }
 
-void MenuModelAdapter::SelectionChanged(MenuItemView* menu) {
-  // Ignore selection of the root menu.
-  if (menu == menu->GetRootMenuItem())
-    return;
-
-  const int id = menu->GetCommand();
-  ui::MenuModel* model = menu_model_;
-  int index = 0;
-  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
-    model->HighlightChangedTo(index);
-    return;
-  }
-
-  NOTREACHED();
-}
-
 void MenuModelAdapter::WillShowMenu(MenuItemView* menu) {
   // Look up the menu model for this menu.
   const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
diff --git a/ui/views/controls/menu/menu_model_adapter.h b/ui/views/controls/menu/menu_model_adapter.h
index e52edfe..a3598719 100644
--- a/ui/views/controls/menu/menu_model_adapter.h
+++ b/ui/views/controls/menu/menu_model_adapter.h
@@ -75,7 +75,6 @@
   bool IsCommandEnabled(int id) const override;
   bool IsCommandVisible(int id) const override;
   bool IsItemChecked(int id) const override;
-  void SelectionChanged(MenuItemView* menu) override;
   void WillShowMenu(MenuItemView* menu) override;
   void WillHideMenu(MenuItemView* menu) override;
   void OnMenuClosed(MenuItemView* menu) override;
diff --git a/ui/views/controls/menu/menu_model_adapter_unittest.cc b/ui/views/controls/menu/menu_model_adapter_unittest.cc
index 392f6c3e..2d95b3cd 100644
--- a/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -82,8 +82,6 @@
     return items_[index].submenu;
   }
 
-  void HighlightChangedTo(int index) override {}
-
   void ActivatedAt(int index) override { set_last_activation(index); }
 
   void ActivatedAt(int index, int event_flags) override { ActivatedAt(index); }
@@ -341,11 +339,6 @@
   const int actionable_submenu_index = 5;
   CheckSubmenu(model, menu, &delegate, kRootIdBase + actionable_submenu_index,
                2, actionable_submenu_index, kActionableSubmenuIdBase);
-
-  // Check that selecting the root item is safe.  The MenuModel does
-  // not care about the root so MenuModelAdapter should do nothing
-  // (not hit the NOTREACHED check) when the root is selected.
-  static_cast<views::MenuDelegate*>(&delegate)->SelectionChanged(menu);
 }
 
 }  // namespace views
diff --git a/ui/views/controls/views_text_services_context_menu_base.cc b/ui/views/controls/views_text_services_context_menu_base.cc
index ae5da49..33950f58 100644
--- a/ui/views/controls/views_text_services_context_menu_base.cc
+++ b/ui/views/controls/views_text_services_context_menu_base.cc
@@ -14,6 +14,7 @@
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/widget/widget.h"
 
 namespace views {
 
@@ -79,7 +80,7 @@
 
 void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) {
   if (command_id == IDS_CONTENT_CONTEXT_EMOJI) {
-    ui::ShowEmojiPanel();
+    client()->GetWidget()->ShowEmojiPanel();
     UMA_HISTOGRAM_BOOLEAN(kViewsTextServicesContextMenuEmoji, true);
   }
 }
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index 3f963df..56c57cf 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -216,7 +216,7 @@
       mojo::ConvertTo<TransportType>(
           static_cast<PrimitiveType>(init_params.keep_on_top));
 
-  properties[WindowManager::kRemoveStandardFrame_InitProperty] =
+  properties[WindowManager::kClientProvidesFrame_InitProperty] =
       mojo::ConvertTo<TransportType>(init_params.remove_standard_frame);
 
   if (init_params.corner_radius) {
diff --git a/ui/views/widget/desktop_aura/desktop_focus_rules.cc b/ui/views/widget/desktop_aura/desktop_focus_rules.cc
index a9b0444..d9396d0 100644
--- a/ui/views/widget/desktop_aura/desktop_focus_rules.cc
+++ b/ui/views/widget/desktop_aura/desktop_focus_rules.cc
@@ -16,7 +16,7 @@
 
 DesktopFocusRules::~DesktopFocusRules() {}
 
-bool DesktopFocusRules::CanActivateWindow(aura::Window* window) const {
+bool DesktopFocusRules::CanActivateWindow(const aura::Window* window) const {
   if (window && content_window_->GetRootWindow()->Contains(window) &&
       wm::WindowStateIs(window->GetRootWindow(), ui::SHOW_STATE_MINIMIZED)) {
     return true;
@@ -28,29 +28,30 @@
   return !window || content_window_->GetRootWindow()->Contains(window);
 }
 
-bool DesktopFocusRules::CanFocusWindow(aura::Window* window,
+bool DesktopFocusRules::CanFocusWindow(const aura::Window* window,
                                        const ui::Event* event) const {
   return BaseFocusRules::CanFocusWindow(window, event) ||
          wm::WindowStateIs(window->GetRootWindow(), ui::SHOW_STATE_MINIMIZED);
 }
 
-bool DesktopFocusRules::SupportsChildActivation(aura::Window* window) const {
+bool DesktopFocusRules::SupportsChildActivation(
+    const aura::Window* window) const {
   // In Desktop-Aura, only the content_window or children of the RootWindow are
   // activatable.
   return window->IsRootWindow();
 }
 
 bool DesktopFocusRules::IsWindowConsideredVisibleForActivation(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   // |content_window_| is initially hidden and made visible from Show(). Even in
   // this state we still want it to be active.
   return BaseFocusRules::IsWindowConsideredVisibleForActivation(window) ||
       (window == content_window_);
 }
 
-aura::Window* DesktopFocusRules::GetToplevelWindow(
-    aura::Window* window) const {
-  aura::Window* top_level_window =
+const aura::Window* DesktopFocusRules::GetToplevelWindow(
+    const aura::Window* window) const {
+  const aura::Window* top_level_window =
       wm::BaseFocusRules::GetToplevelWindow(window);
   // In Desktop-Aura, only the content_window or children of the RootWindow are
   // considered as top level windows.
diff --git a/ui/views/widget/desktop_aura/desktop_focus_rules.h b/ui/views/widget/desktop_aura/desktop_focus_rules.h
index 96fc7f9..2fef603 100644
--- a/ui/views/widget/desktop_aura/desktop_focus_rules.h
+++ b/ui/views/widget/desktop_aura/desktop_focus_rules.h
@@ -17,13 +17,14 @@
 
  private:
   // Overridden from wm::BaseFocusRules:
-  bool CanActivateWindow(aura::Window* window) const override;
-  bool CanFocusWindow(aura::Window* window,
+  bool CanActivateWindow(const aura::Window* window) const override;
+  bool CanFocusWindow(const aura::Window* window,
                       const ui::Event* event) const override;
-  bool SupportsChildActivation(aura::Window* window) const override;
+  bool SupportsChildActivation(const aura::Window* window) const override;
   bool IsWindowConsideredVisibleForActivation(
-      aura::Window* window) const override;
-  aura::Window* GetToplevelWindow(aura::Window* window) const override;
+      const aura::Window* window) const override;
+  const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const override;
   aura::Window* GetNextActivatableWindow(aura::Window* window) const override;
 
   // The content window. This is an activatable window even though it is a
diff --git a/ui/views/widget/native_widget_aura_interactive_uitest.cc b/ui/views/widget/native_widget_aura_interactive_uitest.cc
index ab7acdf..651cff7 100644
--- a/ui/views/widget/native_widget_aura_interactive_uitest.cc
+++ b/ui/views/widget/native_widget_aura_interactive_uitest.cc
@@ -25,11 +25,11 @@
   void set_can_activate(bool can_activate) { can_activate_ = can_activate; }
 
   // wm::BaseFocusRules overrides:
-  bool SupportsChildActivation(aura::Window* window) const override {
+  bool SupportsChildActivation(const aura::Window* window) const override {
     return true;
   }
 
-  bool CanActivateWindow(aura::Window* window) const override {
+  bool CanActivateWindow(const aura::Window* window) const override {
     return can_activate_;
   }
 
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index ee5f354..98510e62 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -50,11 +50,11 @@
   void set_can_activate(bool can_activate) { can_activate_ = can_activate; }
 
   // wm::BaseFocusRules overrides:
-  bool SupportsChildActivation(aura::Window* window) const override {
+  bool SupportsChildActivation(const aura::Window* window) const override {
     return true;
   }
 
-  bool CanActivateWindow(aura::Window* window) const override {
+  bool CanActivateWindow(const aura::Window* window) const override {
     return can_activate_;
   }
 
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index 64eba8978..409fe0b 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -150,6 +150,7 @@
                     ui::DragDropTypes::DragEventSource source) override;
   void SchedulePaintInRect(const gfx::Rect& rect) override;
   void SetCursor(gfx::NativeCursor cursor) override;
+  void ShowEmojiPanel() override;
   bool IsMouseEventsEnabled() const override;
   bool IsMouseButtonDown() const override;
   void ClearNativeFocus() override;
@@ -168,6 +169,11 @@
   void OnSizeConstraintsChanged() override;
   std::string GetName() const override;
 
+  // Calls |callback| with the newly created NativeWidget whenever a
+  // NativeWidget is created.
+  static void SetInitNativeWidgetCallback(
+      const base::RepeatingCallback<void(NativeWidgetMac*)>& callback);
+
  protected:
   virtual void PopulateCreateWindowParams(
       const Widget::InitParams& widget_params,
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index eec61ad..d4459e7 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -42,6 +42,9 @@
 base::LazyInstance<ui::GestureRecognizerImplMac>::Leaky
     g_gesture_recognizer_instance = LAZY_INSTANCE_INITIALIZER;
 
+static base::RepeatingCallback<void(NativeWidgetMac*)>*
+    g_init_native_widget_callback = nullptr;
+
 NSInteger StyleMaskForParams(const Widget::InitParams& params) {
   // If the Widget is modal, it will be displayed as a sheet. This works best if
   // it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the
@@ -168,6 +171,9 @@
   }
 
   bridge_host_->CreateCompositor(params);
+
+  if (g_init_native_widget_callback)
+    g_init_native_widget_callback->Run(this);
 }
 
 void NativeWidgetMac::OnWidgetInitDone() {
@@ -544,6 +550,13 @@
     bridge_impl()->SetCursor(cursor);
 }
 
+void NativeWidgetMac::ShowEmojiPanel() {
+  // We must plumb the call to ui::ShowEmojiPanel() over the bridge so that it
+  // is called from the correct process.
+  if (bridge())
+    bridge()->ShowEmojiPanel();
+}
+
 bool NativeWidgetMac::IsMouseEventsEnabled() const {
   // On platforms with touch, mouse events get disabled and calls to this method
   // can affect hover states. Since there is no touch on desktop Mac, this is
@@ -638,6 +651,21 @@
   return name_;
 }
 
+// static
+void NativeWidgetMac::SetInitNativeWidgetCallback(
+    const base::RepeatingCallback<void(NativeWidgetMac*)>& callback) {
+  DCHECK(!g_init_native_widget_callback || callback.is_null());
+  if (callback.is_null()) {
+    if (g_init_native_widget_callback) {
+      delete g_init_native_widget_callback;
+      g_init_native_widget_callback = nullptr;
+    }
+    return;
+  }
+  g_init_native_widget_callback =
+      new base::RepeatingCallback<void(NativeWidgetMac*)>(callback);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // NativeWidgetMac, protected:
 
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 629182c..9bcfb2c4 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -2409,6 +2409,33 @@
   delegate->GetWidget()->CloseNow();
 }
 
+TEST_F(NativeWidgetMacTest, InitCallback) {
+  NativeWidget* observed_native_widget = nullptr;
+  const auto callback = base::BindRepeating(
+      [](NativeWidget** observed, NativeWidgetMac* native_widget) {
+        *observed = native_widget;
+      },
+      &observed_native_widget);
+  NativeWidgetMac::SetInitNativeWidgetCallback(callback);
+
+  Widget* widget_a = CreateTopLevelPlatformWidget();
+  EXPECT_EQ(observed_native_widget, widget_a->native_widget());
+  Widget* widget_b = CreateTopLevelPlatformWidget();
+  EXPECT_EQ(observed_native_widget, widget_b->native_widget());
+
+  auto empty = base::RepeatingCallback<void(NativeWidgetMac*)>();
+  DCHECK(empty.is_null());
+  NativeWidgetMac::SetInitNativeWidgetCallback(empty);
+  observed_native_widget = nullptr;
+  Widget* widget_c = CreateTopLevelPlatformWidget();
+  // The original callback from above should no longer be firing.
+  EXPECT_EQ(observed_native_widget, nullptr);
+
+  widget_a->CloseNow();
+  widget_b->CloseNow();
+  widget_c->CloseNow();
+}
+
 }  // namespace test
 }  // namespace views
 
diff --git a/ui/views/widget/native_widget_private.cc b/ui/views/widget/native_widget_private.cc
index e8ad491..a5933a8 100644
--- a/ui/views/widget/native_widget_private.cc
+++ b/ui/views/widget/native_widget_private.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/widget/native_widget_private.h"
 
+#include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
@@ -21,5 +22,9 @@
   return new_bounds;
 }
 
+void NativeWidgetPrivate::ShowEmojiPanel() {
+  ui::ShowEmojiPanel();
+}
+
 }  // namespace internal
 }  // namespace views
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h
index 1867d1906..e4af6f1 100644
--- a/ui/views/widget/native_widget_private.h
+++ b/ui/views/widget/native_widget_private.h
@@ -212,6 +212,7 @@
                             ui::DragDropTypes::DragEventSource source) = 0;
   virtual void SchedulePaintInRect(const gfx::Rect& rect) = 0;
   virtual void SetCursor(gfx::NativeCursor cursor) = 0;
+  virtual void ShowEmojiPanel();
   virtual bool IsMouseEventsEnabled() const = 0;
   // Returns true if any mouse button is currently down.
   virtual bool IsMouseButtonDown() const = 0;
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 16d18d4..496e724 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -384,6 +384,10 @@
   native_widget_->OnWidgetInitDone();
 }
 
+void Widget::ShowEmojiPanel() {
+  native_widget_->ShowEmojiPanel();
+}
+
 // Unconverted methods (see header) --------------------------------------------
 
 gfx::NativeView Widget::GetNativeView() const {
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index b46f249e80..0323bbb 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -647,6 +647,9 @@
   // Tell the window to update its icon from the delegate.
   void UpdateWindowIcon();
 
+  // Shows the platform specific emoji picker for this widget.
+  void ShowEmojiPanel();
+
   // Retrieves the focus traversable for this widget.
   FocusTraversable* GetFocusTraversable();
 
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 902164eb..ca9e496 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -3867,7 +3867,7 @@
    public:
     TestFocusRules() = default;
 
-    bool SupportsChildActivation(aura::Window* window) const override {
+    bool SupportsChildActivation(const aura::Window* window) const override {
       return true;
     }
 
diff --git a/ui/views/window/frame_caption_button.cc b/ui/views/window/frame_caption_button.cc
index d15c945..49026c9 100644
--- a/ui/views/window/frame_caption_button.cc
+++ b/ui/views/window/frame_caption_button.cc
@@ -47,7 +47,6 @@
     : Button(listener),
       icon_(icon),
       background_color_(SK_ColorWHITE),
-      color_mode_(ColorMode::kDefault),
       paint_as_active_(false),
       alpha_(255),
       ink_drop_corner_radius_(kCaptionButtonInkDropDefaultCornerRadius),
@@ -70,14 +69,22 @@
 FrameCaptionButton::~FrameCaptionButton() = default;
 
 // static
-SkColor FrameCaptionButton::GetButtonColor(ColorMode color_mode,
-                                           SkColor background_color) {
-  if (color_mode == ColorMode::kThemed)
-    return color_utils::GetThemedAssetColor(background_color);
-
-  DCHECK_EQ(color_mode, ColorMode::kDefault);
-  return color_utils::PickContrastingColor(
-      gfx::kGoogleGrey200, gfx::kGoogleGrey700, background_color);
+SkColor FrameCaptionButton::GetButtonColor(SkColor background_color) {
+  // Use IsDark() to change target colors instead of PickContrastingColor(), so
+  // that DefaultFrameHeader::GetTitleColor() (which uses different target
+  // colors) can change between light/dark targets at the same time.  It looks
+  // bad when the title and caption buttons disagree about whether to be light
+  // or dark.
+  const SkColor source = color_utils::IsDark(background_color)
+                             ? gfx::kGoogleGrey200
+                             : gfx::kGoogleGrey700;
+  const SkColor target = color_utils::GetColorWithMaxContrast(background_color);
+  // Guarantee the caption buttons reach at least contrast ratio 3; this ratio
+  // matches that used for focus indicators, large text, and other "have to see
+  // it but perhaps don't have to read fine detail" cases.
+  const SkAlpha alpha = color_utils::GetBlendValueWithMinimumContrast(
+      source, target, background_color, 3.0f);
+  return color_utils::AlphaBlend(target, source, alpha);
 }
 
 // static
@@ -88,8 +95,8 @@
 void FrameCaptionButton::SetImage(CaptionButtonIcon icon,
                                   Animate animate,
                                   const gfx::VectorIcon& icon_definition) {
-  gfx::ImageSkia new_icon_image = gfx::CreateVectorIcon(
-      icon_definition, GetButtonColor(color_mode_, background_color_));
+  gfx::ImageSkia new_icon_image =
+      gfx::CreateVectorIcon(icon_definition, GetButtonColor(background_color_));
 
   // The early return is dependent on |animate| because callers use SetImage()
   // with ANIMATE_NO to progress the crossfade animation to the end.
@@ -190,11 +197,6 @@
   UpdateInkDropBaseColor();
 }
 
-void FrameCaptionButton::SetColorMode(ColorMode color_mode) {
-  color_mode_ = color_mode;
-  UpdateInkDropBaseColor();
-}
-
 void FrameCaptionButton::PaintButtonContents(gfx::Canvas* canvas) {
   constexpr SkAlpha kHighlightVisibleOpacity = 0x14;
   SkAlpha highlight_alpha = SK_AlphaTRANSPARENT;
@@ -287,7 +289,7 @@
   // glyph color.
   // TODO(pkasting): It would likely be better to make the button glyph always
   // be an alpha-blended version of GetColorWithMaxContrast(background_color_).
-  const SkColor button_color = GetButtonColor(color_mode_, background_color_);
+  const SkColor button_color = GetButtonColor(background_color_);
   set_ink_drop_base_color(
       GetColorWithMaxContrast(GetColorWithMaxContrast(button_color)));
 }
diff --git a/ui/views/window/frame_caption_button.h b/ui/views/window/frame_caption_button.h
index 81b8748..5c9cb224 100644
--- a/ui/views/window/frame_caption_button.h
+++ b/ui/views/window/frame_caption_button.h
@@ -26,11 +26,6 @@
  public:
   enum Animate { ANIMATE_YES, ANIMATE_NO };
 
-  enum class ColorMode {
-    kDefault,  // Most windows.
-    kThemed,   // Windows that have been themed by PWA manifest.
-  };
-
   static const char kViewClassName[];
 
   FrameCaptionButton(views::ButtonListener* listener,
@@ -38,9 +33,8 @@
                      int hit_test_type);
   ~FrameCaptionButton() override;
 
-  // Gets the color to use for a frame caption button while a theme color is
-  // set.
-  static SkColor GetButtonColor(ColorMode color_mode, SkColor background_color);
+  // Gets the color to use for a frame caption button.
+  static SkColor GetButtonColor(SkColor background_color);
 
   // Gets the alpha ratio for the colors of inactive frame caption buttons.
   static float GetInactiveButtonColorAlphaRatio();
@@ -69,7 +63,6 @@
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
 
   void SetBackgroundColor(SkColor background_color);
-  void SetColorMode(ColorMode color_mode);
 
   void set_paint_as_active(bool paint_as_active) {
     paint_as_active_ = paint_as_active;
@@ -111,9 +104,6 @@
   // The current background color.
   SkColor background_color_;
 
-  // The algorithm to determine button colors.
-  ColorMode color_mode_;
-
   // Whether the button should be painted as active.
   bool paint_as_active_;
 
diff --git a/ui/views/window/frame_caption_button_unittest.cc b/ui/views/window/frame_caption_button_unittest.cc
index ad7d2f9..211c4b88 100644
--- a/ui/views/window/frame_caption_button_unittest.cc
+++ b/ui/views/window/frame_caption_button_unittest.cc
@@ -22,8 +22,8 @@
 
 TEST(FrameCaptionButtonTest, ThemedColorContrast) {
   for (SkColor background_color : kBackgroundColors) {
-    SkColor button_color = views::FrameCaptionButton::GetButtonColor(
-        views::FrameCaptionButton::ColorMode::kThemed, background_color);
+    SkColor button_color =
+        views::FrameCaptionButton::GetButtonColor(background_color);
     EXPECT_GE(color_utils::GetContrastRatio(button_color, background_color), 3);
   }
 }
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.h b/ui/views_bridge_mac/bridged_native_widget_impl.h
index 68afafaa..3f21e108 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.h
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.h
@@ -200,6 +200,7 @@
   void CreateWindow(
       views_bridge_mac::mojom::CreateWindowParamsPtr params) override;
   void SetParent(uint64_t parent_id) override;
+  void ShowEmojiPanel() override;
   void InitWindow(views_bridge_mac::mojom::BridgedNativeWidgetInitParamsPtr
                       params) override;
   void InitCompositorView() override;
diff --git a/ui/views_bridge_mac/bridged_native_widget_impl.mm b/ui/views_bridge_mac/bridged_native_widget_impl.mm
index 83a49da0..3190bf9 100644
--- a/ui/views_bridge_mac/bridged_native_widget_impl.mm
+++ b/ui/views_bridge_mac/bridged_native_widget_impl.mm
@@ -21,6 +21,7 @@
 #import "ui/base/cocoa/constrained_window/constrained_window_animation.h"
 #include "ui/base/cocoa/remote_accessibility_api.h"
 #import "ui/base/cocoa/window_size_constants.h"
+#include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/layout.h"
 #include "ui/base/ui_base_switches.h"
@@ -384,6 +385,10 @@
     [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove];
 }
 
+void BridgedNativeWidgetImpl::ShowEmojiPanel() {
+  ui::ShowEmojiPanel();
+}
+
 void BridgedNativeWidgetImpl::CreateWindow(
     views_bridge_mac::mojom::CreateWindowParamsPtr params) {
   SetWindow(CreateNSWindow(params.get()));
diff --git a/ui/views_bridge_mac/mojo/bridged_native_widget.mojom b/ui/views_bridge_mac/mojo/bridged_native_widget.mojom
index 8d1e4e43..637cf66 100644
--- a/ui/views_bridge_mac/mojo/bridged_native_widget.mojom
+++ b/ui/views_bridge_mac/mojo/bridged_native_widget.mojom
@@ -88,6 +88,9 @@
   // this BridgedNativeWidget.
   SetParent(uint64 parent_id);
 
+  // Show the emoji panel for the NSWindow's process.
+  ShowEmojiPanel();
+
   // Initialize the window's style.
   InitWindow(BridgedNativeWidgetInitParams params);
 
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index f4e00640..1424d432 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -197,7 +197,7 @@
       }
     </style>
 
-    <div id="root" on-contextmenu="onContextMenu_" on-tap="focusInput_">
+    <div id="root" on-contextmenu="onContextMenu_" on-tap="onRootTap_">
       <div id="pinInputDiv">
         <cr-input id="pinInput" type="password" value="{{value}}"
             is-input-rtl$="[[isInputRtl_(value)]]"
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
index 4778670e4..5e5647b 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
@@ -185,7 +185,7 @@
    * @param {number=} opt_selectionStart
    * @param {number=} opt_selectionEnd
    */
-  focus: function(opt_selectionStart, opt_selectionEnd) {
+  focusInput: function(opt_selectionStart, opt_selectionEnd) {
     setTimeout(function() {
       this.passwordElement_().focus();
       this.selectionStart_ = opt_selectionStart || 0;
@@ -197,11 +197,11 @@
    * Transfers focus to the input. Called when a non button element on the
    * PIN button area is clicked to prevent focus from leaving the input.
    */
-  focusInput_: function() {
+  onRootTap_: function() {
     // Focus the input and place the selected region to its exact previous
     // location, as this function will not be called by something that will also
     // modify the input value.
-    this.focus(this.selectionStart_, this.selectionEnd_);
+    this.focusInput(this.selectionStart_, this.selectionEnd_);
   },
 
   /** @private */
@@ -233,7 +233,7 @@
     // button is tabbed into, it should keep focus, so users can use tab and
     // spacebar/return to enter their PIN.
     if (!event.target.receivedFocusFromKeyboard) {
-      this.focus(selectionStart + 1, selectionStart + 1);
+      this.focusInput(selectionStart + 1, selectionStart + 1);
     }
     event.stopImmediatePropagation();
   },
@@ -266,7 +266,7 @@
     // selected region of the input element. If it is just a caret, remove the
     // character in front of the caret.
     let selectionStart = this.selectionStart_;
-    let selectionEnd = this.selectionEnd_;
+    const selectionEnd = this.selectionEnd_;
     if (selectionStart == selectionEnd && selectionStart) {
       selectionStart--;
     }
@@ -312,7 +312,7 @@
     }.bind(this), INITIAL_BACKSPACE_DELAY_MS);
 
     if (!event.target.receivedFocusFromKeyboard) {
-      this.focus(this.selectionStart_, this.selectionEnd_);
+      this.focusInput(this.selectionStart_, this.selectionEnd_);
     }
     event.stopImmediatePropagation();
   },
@@ -344,11 +344,11 @@
 
     // Since on-down gives the input element focus, the input element will
     // already have focus when on-up is called. This will actually bring up the
-    // virtual keyboard, even if focus() is wrapped in a setTimeout. Blur the
-    // input element first to workaround this.
+    // virtual keyboard, even if focusInput() is wrapped in a setTimeout. Blur
+    // the input element first to workaround this.
     this.blur();
     if (!event.target.receivedFocusFromKeyboard) {
-      this.focus(this.selectionStart_, this.selectionEnd_);
+      this.focusInput(this.selectionStart_, this.selectionEnd_);
     }
     event.stopImmediatePropagation();
   },
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
index a223f95..a782c7c 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
@@ -147,7 +147,7 @@
   },
 
   focus: function() {
-    this.$.pinKeyboard.focus();
+    this.$.pinKeyboard.focusInput();
   },
 
   /** @override */
@@ -284,7 +284,8 @@
 
   /**
    * @param {!CustomEvent} e Custom event containing the new pin.
-   * @private */
+   * @private
+   */
   onPinChange_: function(e) {
     const newPin = /** @type {{pin: string}} */ (e.detail).pin;
     if (!this.isConfirmStep) {
@@ -335,7 +336,7 @@
       this.isConfirmStep = true;
       this.onPinChange_(new CustomEvent(
           'pin-change', {detail: {pin: this.pinKeyboardValue_}}));
-      this.$.pinKeyboard.focus();
+      this.$.pinKeyboard.focusInput();
       this.writeUma(LockScreenProgress.ENTER_PIN);
       return;
     }
@@ -345,7 +346,7 @@
       this.showProblem_(MessageType.MISMATCH, ProblemType.ERROR);
       this.enableSubmit = false;
       // Focus the PIN keyboard and highlight the entire PIN.
-      this.$.pinKeyboard.focus(0, this.pinKeyboardValue_.length + 1);
+      this.$.pinKeyboard.focusInput(0, this.pinKeyboardValue_.length + 1);
       return;
     }
 
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
index 48a08a6d..086b8bc 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
@@ -50,6 +50,10 @@
 const DROPDOWN_ITEM_CLASS = 'dropdown-item';
 
 (function() {
+
+/** @const {number} */
+const AFTER_END_OFFSET = 10;
+
 /**
  * Returns the point to start along the X or Y axis given a start and end
  * point to anchor to, the length of the target and the direction to anchor
@@ -244,7 +248,7 @@
     if (e.key == 'Enter') {
       // If a menu item has focus, don't change focus or close menu on 'Enter'.
       const options = this.querySelectorAll('.dropdown-item');
-      let focusedIndex =
+      const focusedIndex =
           Array.prototype.indexOf.call(options, getDeepActiveElement());
       if (focusedIndex != -1) {
         return;
@@ -359,11 +363,22 @@
     this.anchorElement_.scrollIntoViewIfNeeded();
 
     const rect = this.anchorElement_.getBoundingClientRect();
+
+    let height = rect.height;
+    if (opt_config &&
+        opt_config.anchorAlignmentY == AnchorAlignment.AFTER_END) {
+      // When an action menu is positioned after the end of an element, the
+      // action menu can appear too far away from the anchor element, typically
+      // because anchors tend to have padding. So we offset the height a bit
+      // so the menu shows up slightly closer to the content of anchor.
+      height -= AFTER_END_OFFSET;
+    }
+
     this.showAtPosition(/** @type {ShowAtPositionConfig} */ (Object.assign(
         {
           top: rect.top,
           left: rect.left,
-          height: rect.height,
+          height: height,
           width: rect.width,
           // Default to anchoring towards the left.
           anchorAlignmentX: AnchorAlignment.BEFORE_END,
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index 603efbb..e4d7affc 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -29,11 +29,34 @@
         outline: none;
         user-select: none;
 
-        --cr-checkbox-size: 16px;
+        /* Sizes. */
         --cr-checkbox-border-size: 2px;
+        --cr-checkbox-size: 16px;
         --cr-checkbox-ripple-size: 40px;
+
+        /* Derived sizes (offsets). */
         --cr-checkbox-ripple-offset: calc(var(--cr-checkbox-size)/2 -
             var(--cr-checkbox-ripple-size)/2 - var(--cr-checkbox-border-size));
+
+        /* Light mode colors. */
+        --cr-checkbox-checked-box-color: var(--google-blue-600);
+        --cr-checkbox-checked-ripple-opacity: .2;
+        --cr-checkbox-mark-color: white;
+        --cr-checkbox-ripple-checked-color: var(--google-blue-600);
+        --cr-checkbox-ripple-unchecked-color: var(--google-grey-600);
+        --cr-checkbox-unchecked-box-color: var(--google-grey-refresh-700);
+        --cr-checkbox-unchecked-ripple-opacity: .15;
+      }
+
+      :host-context([dark]) {
+        /* Dark mode colors. */
+        --cr-checkbox-checked-box-color: var(--google-blue-300);
+        --cr-checkbox-checked-ripple-opacity: .4;
+        --cr-checkbox-mark-color: var(--google-grey-900);
+        --cr-checkbox-ripple-checked-color: var(--google-blue-300);
+        --cr-checkbox-ripple-unchecked-color: var(--google-grey-refresh-700);
+        --cr-checkbox-unchecked-box-color: var(--google-grey-refresh-500);
+        --cr-checkbox-unchecked-ripple-opacity: .4;
       }
 
       :host([disabled]) {
@@ -45,7 +68,7 @@
       #checkbox {
         background: none;
         border: var(--cr-checkbox-border-size) solid
-            var(--cr-checkbox-unchecked-box-color, var(--google-grey-refresh-700));
+            var(--cr-checkbox-unchecked-box-color);
         border-radius: 2px;
         box-sizing: border-box;
         cursor: pointer;
@@ -61,7 +84,7 @@
       }
 
       #checkmark {
-        border-color: var(--cr-checkbox-mark-color, white);
+        border-color: var(--cr-checkbox-mark-color);
         border-style: solid;
         border-width: 0 2px 2px 0;
         content: '';
@@ -77,10 +100,8 @@
       }
 
       :host([checked]) #checkbox {
-        background: var(--cr-checkbox-checked-box-color,
-            var(--google-blue-600));
-        border-color: var(--cr-checkbox-checked-box-color,
-            var(--google-blue-600));
+        background: var(--cr-checkbox-checked-box-color);
+        border-color: var(--cr-checkbox-checked-box-color);
       }
 
       :host([checked]) #checkmark {
@@ -90,8 +111,9 @@
       }
 
       paper-ripple {
-        --paper-ripple-opacity: var(--cr-checkbox-ripple-opacity, 0.15);
-        color: var(--cr-checkbox-ripple-unchecked-color, var(--google-grey-600));
+        --paper-ripple-opacity: var(--cr-checkbox-ripple-opacity,
+            var(--cr-checkbox-unchecked-ripple-opacity));
+        color: var(--cr-checkbox-ripple-unchecked-color);
         height: var(--cr-checkbox-ripple-size);
         left: var(--cr-checkbox-ripple-offset);
         pointer-events: none;
@@ -101,8 +123,9 @@
       }
 
       :host([checked]) paper-ripple {
-        --paper-ripple-opacity: var(--cr-checkbox-ripple-opacity, 0.2);
-        color: var(--cr-checkbox-ripple-checked-color, var(--google-blue-600));
+        --paper-ripple-opacity: var(--cr-checkbox-ripple-opacity,
+            var(--cr-checkbox-checked-ripple-opacity));
+        color: var(--cr-checkbox-ripple-checked-color);
       }
 
       :host-context([dir=rtl]) paper-ripple {
diff --git a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index eae9c1c2..bef7d7c 100644
--- a/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -23,6 +23,11 @@
         width: var(--drawer-width);
       }
 
+      :host-context([dark]) dialog {
+        background: var(--google-grey-900)
+            linear-gradient(rgba(255, 255, 255, .04), rgba(255, 255, 255, .04));
+      }
+
       :host dialog,
       #container {
         height: 100%;
@@ -61,6 +66,7 @@
       .drawer-header {
         align-items: center;
         border-bottom: var(--cr-separator-line);
+        color: var(--cr-drawer-heading-color);
         display: flex;
         font-size: 123.08%;  /* go to 16px from 13px */
         min-height: 56px;
@@ -68,6 +74,10 @@
         padding-inline-start: 24px;
       }
 
+      :host-context([dark]) .drawer-header {
+        --cr-separator-color: rgba(255, 255, 255, .1);
+      }
+
       :host ::slotted(.drawer-content) {
         height: calc(100% - 56px);
         overflow: auto;
diff --git a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html
index 209dc71..171c6f5 100644
--- a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html
+++ b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html
@@ -2,18 +2,20 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html">
 
 <dom-module id="cr-searchable-drop-down">
   <template>
     <style>
-      cr-input {
+      :host(:not([error-message-allowed])) cr-input {
         --cr-input-error-display: none;
       }
 
       iron-dropdown,
       cr-input {
-        width: var(--cr-searchable-drop-down-width, 270px);
+        width: var(--cr-searchable-drop-down-width,
+                   var(--cr-default-input-max-width));
       }
 
       iron-dropdown {
@@ -50,7 +52,9 @@
       explicitly used. -->
     <cr-input label="[[label]]" on-click="onClick_" value="[[value]]"
         on-input="onInput_" id="search" autofocus="[[autofocus]]"
-        placeholder="[[placeholder]]">
+        placeholder="[[placeholder]]"
+        error-message="[[getErrorMessage_(errorMessage, errorMessageAllowed)]]"
+        invalid="[[shouldShowErrorMessage_(errorMessage, errorMessageAllowed)]]">
     </cr-input>
     <iron-dropdown horizontal-align="left" vertical-align="top"
         vertical-offset="52">
diff --git a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
index 9aff43d..4432178 100644
--- a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
+++ b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js
@@ -20,6 +20,22 @@
       reflectToAttribute: true,
     },
 
+    /**
+     * Whether space should be left below the text field to display an error
+     * message. Must be true for |errorMessage| to be displayed.
+     */
+    errorMessageAllowed: {
+      type: Boolean,
+      value: false,
+      reflectToAttribute: true,
+    },
+
+    /**
+     * When |errorMessage| is set, the text field is highlighted red and
+     * |errorMessage| is displayed beneath it.
+     */
+    errorMessage: String,
+
     placeholder: String,
 
     /** @type {!Array<string>} */
@@ -78,4 +94,28 @@
       return item.toLowerCase().includes(searchTerm.toLowerCase());
     };
   },
+
+  /**
+   * @param {string} errorMessage
+   * @param {boolean} errorMessageAllowed
+   * @return {boolean}
+   * @private
+   */
+  shouldShowErrorMessage_: function(errorMessage, errorMessageAllowed) {
+    return !!this.getErrorMessage_(errorMessage, errorMessageAllowed);
+  },
+
+  /**
+   * @param {string} errorMessage
+   * @param {boolean} errorMessageAllowed
+   * @return {string}
+   * @private
+   */
+  getErrorMessage_: function(errorMessage, errorMessageAllowed) {
+    if (!errorMessageAllowed) {
+      return '';
+    }
+    return errorMessage;
+  },
+
 });
\ No newline at end of file
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index 81b95f0..cdaeda4 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -21,6 +21,7 @@
 
       :host-context([dark]) {
         border-bottom: 1px solid var(--cr-separator-color);
+        color: var(--cr-secondary-text-color);
       }
 
       h1 {
@@ -33,6 +34,10 @@
         padding-inline-end: 12px;
       }
 
+      :host-context([dark]) :-webkit-any(h1, #menuButtonContainer) {
+        color: var(--cr-primary-text-color);
+      }
+
       #leftContent {
         /* margin-start here must match margin-end on #rightContent. */
         margin-inline-start: 12px;
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html
index abf1be28..3bbfba6 100644
--- a/ui/webui/resources/cr_elements/paper_button_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -25,8 +25,8 @@
         --flat-text-color-action: white;
 
         --hover-bg-action: rgba(var(--google-blue-600-rgb), .9);
-        --hover-bg-color: rgba(var(--google-blue-500-rgb), .04)
-        --hover-border-color: var(--google-blue-100);
+        --hover-bg-color: rgba(var(--google-blue-500-rgb), .04);
+        --hover-border-color: var(--google-blue-refresh-100);
         --hover-shadow-action-rgb: var(--google-blue-500-rgb);
 
         /* Blue-ish color used either as a background or as a text color,
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index 4d1e4f05..84c85d1 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -20,6 +20,8 @@
     --google-red-600: #D93025;
 
     /* -refresh differentiate from polymer's color.html. */
+    --google-blue-refresh-100: #d2e3fc;
+    --google-blue-refresh-300: #8AB4F8;
     --google-green-refresh-700: #188038;
     --google-grey-refresh-100: #F1F3F4;
     --google-grey-refresh-300: #DADCE0;
@@ -65,7 +67,7 @@
     /* TODO(dbeam): form-field-label, {section,title}-text, & toggle colors. */
 
     --cr-form-field-label-color: var(--dark-secondary-color);
-    --cr-link-color: var(--google-blue-300);
+    --cr-link-color: var(--google-blue-refresh-300);
     --cr-menu-background-color: var(--google-grey-900);
     --cr-menu-background-focus-color: rgba(var(--google-grey-800-rgb), .6);
     --cr-menu-background-sheen: rgba(255, 255, 255, .06);  /* Only dark mode. */
@@ -87,6 +89,9 @@
     /* Spacing between policy (controlledBy) indicator and control. */
     --cr-controlled-by-spacing: 24px;
 
+    /* Default max-width for input fields */
+    --cr-default-input-max-width: 264px;
+
     /* The inner icon is 20px in size. The button has 8px * 2 padding. */
     --cr-icon-ripple-size: 36px;
     --cr-icon-ripple-padding: 8px;
diff --git a/ui/wm/core/base_focus_rules.cc b/ui/wm/core/base_focus_rules.cc
index eb446ee..6c17e45 100644
--- a/ui/wm/core/base_focus_rules.cc
+++ b/ui/wm/core/base_focus_rules.cc
@@ -29,14 +29,14 @@
 BaseFocusRules::~BaseFocusRules() = default;
 
 bool BaseFocusRules::IsWindowConsideredVisibleForActivation(
-    aura::Window* window) const {
+    const aura::Window* window) const {
   return window->IsVisible();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // BaseFocusRules, FocusRules implementation:
 
-bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const {
+bool BaseFocusRules::IsToplevelWindow(const aura::Window* window) const {
   // The window must in a valid hierarchy.
   if (!window->GetRootWindow())
     return false;
@@ -46,7 +46,7 @@
   return SupportsChildActivation(window->parent());
 }
 
-bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
+bool BaseFocusRules::CanActivateWindow(const aura::Window* window) const {
   // It is possible to activate a NULL window, it is equivalent to clearing
   // activation.
   if (!window)
@@ -76,7 +76,7 @@
   return !GetModalTransient(window);
 }
 
-bool BaseFocusRules::CanFocusWindow(aura::Window* window,
+bool BaseFocusRules::CanFocusWindow(const aura::Window* window,
                                     const ui::Event* event) const {
   // It is possible to focus a NULL window, it is equivalent to clearing focus.
   if (!window)
@@ -84,15 +84,16 @@
 
   // The focused window is always inside the active window, so windows that
   // aren't activatable can't contain the focused window.
-  aura::Window* activatable = GetActivatableWindow(window);
+  const aura::Window* activatable = GetActivatableWindow(window);
   if (!activatable || !activatable->Contains(window))
     return false;
   return window->CanFocus();
 }
 
-aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const {
-  aura::Window* parent = window->parent();
-  aura::Window* child = window;
+const aura::Window* BaseFocusRules::GetToplevelWindow(
+    const aura::Window* window) const {
+  const aura::Window* parent = window->parent();
+  const aura::Window* child = window;
   while (parent) {
     if (IsToplevelWindow(child))
       return child;
@@ -104,35 +105,8 @@
 }
 
 aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
-  aura::Window* parent = window->parent();
-  aura::Window* child = window;
-  while (parent) {
-    if (CanActivateWindow(child))
-      return child;
-
-    // CanActivateWindow() above will return false if |child| is blocked by a
-    // modal transient. In this case the modal is or contains the activatable
-    // window. We recurse because the modal may itself be blocked by a modal
-    // transient.
-    aura::Window* modal_transient = GetModalTransient(child);
-    if (modal_transient)
-      return GetActivatableWindow(modal_transient);
-
-    if (wm::GetTransientParent(child)) {
-      // To avoid infinite recursion, if |child| has a transient parent
-      // whose own modal transient is |child| itself, just return |child|.
-      aura::Window* parent_modal_transient =
-          GetModalTransient(wm::GetTransientParent(child));
-      if (parent_modal_transient == child)
-        return child;
-
-      return GetActivatableWindow(wm::GetTransientParent(child));
-    }
-
-    parent = parent->parent();
-    child = child->parent();
-  }
-  return nullptr;
+  return const_cast<aura::Window*>(
+      GetActivatableWindow(const_cast<const aura::Window*>(window)));
 }
 
 aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
@@ -188,4 +162,37 @@
   return nullptr;
 }
 
+const aura::Window* BaseFocusRules::GetActivatableWindow(
+    const aura::Window* window) const {
+  const aura::Window* parent = window->parent();
+  const aura::Window* child = window;
+  while (parent) {
+    if (CanActivateWindow(child))
+      return child;
+
+    // CanActivateWindow() above will return false if |child| is blocked by a
+    // modal transient. In this case the modal is or contains the activatable
+    // window. We recurse because the modal may itself be blocked by a modal
+    // transient.
+    const aura::Window* modal_transient = GetModalTransient(child);
+    if (modal_transient)
+      return GetActivatableWindow(modal_transient);
+
+    if (wm::GetTransientParent(child)) {
+      // To avoid infinite recursion, if |child| has a transient parent
+      // whose own modal transient is |child| itself, just return |child|.
+      const aura::Window* parent_modal_transient =
+          GetModalTransient(wm::GetTransientParent(child));
+      if (parent_modal_transient == child)
+        return child;
+
+      return GetActivatableWindow(wm::GetTransientParent(child));
+    }
+
+    parent = parent->parent();
+    child = child->parent();
+  }
+  return nullptr;
+}
+
 }  // namespace wm
diff --git a/ui/wm/core/base_focus_rules.h b/ui/wm/core/base_focus_rules.h
index 6adf8794..52caf7f2 100644
--- a/ui/wm/core/base_focus_rules.h
+++ b/ui/wm/core/base_focus_rules.h
@@ -19,23 +19,30 @@
   ~BaseFocusRules() override;
 
   // Returns true if the children of |window| can be activated.
-  virtual bool SupportsChildActivation(aura::Window* window) const = 0;
+  virtual bool SupportsChildActivation(const aura::Window* window) const = 0;
 
   // Returns true if |window| is considered visible for activation purposes.
   virtual bool IsWindowConsideredVisibleForActivation(
-      aura::Window* window) const;
+      const aura::Window* window) const;
 
   // Overridden from FocusRules:
-  bool IsToplevelWindow(aura::Window* window) const override;
-  bool CanActivateWindow(aura::Window* window) const override;
-  bool CanFocusWindow(aura::Window* window,
+  bool IsToplevelWindow(const aura::Window* window) const override;
+  bool CanActivateWindow(const aura::Window* window) const override;
+  bool CanFocusWindow(const aura::Window* window,
                       const ui::Event* event) const override;
-  aura::Window* GetToplevelWindow(aura::Window* window) const override;
+  const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const override;
   aura::Window* GetActivatableWindow(aura::Window* window) const override;
   aura::Window* GetFocusableWindow(aura::Window* window) const override;
   aura::Window* GetNextActivatableWindow(aura::Window* ignore) const override;
 
  private:
+  aura::Window* GetToplevelWindow(aura::Window* window) const {
+    return const_cast<aura::Window*>(
+        GetToplevelWindow(const_cast<const aura::Window*>(window)));
+  }
+  const aura::Window* GetActivatableWindow(const aura::Window* window) const;
+
   DISALLOW_COPY_AND_ASSIGN(BaseFocusRules);
 };
 
diff --git a/ui/wm/core/default_activation_client.cc b/ui/wm/core/default_activation_client.cc
index ee8ac40d..4f7615bf 100644
--- a/ui/wm/core/default_activation_client.cc
+++ b/ui/wm/core/default_activation_client.cc
@@ -110,15 +110,17 @@
 }
 
 aura::Window* DefaultActivationClient::GetActivatableWindow(
-    aura::Window* window) {
+    aura::Window* window) const {
   return nullptr;
 }
 
-aura::Window* DefaultActivationClient::GetToplevelWindow(aura::Window* window) {
+const aura::Window* DefaultActivationClient::GetToplevelWindow(
+    const aura::Window* window) const {
   return nullptr;
 }
 
-bool DefaultActivationClient::CanActivateWindow(aura::Window* window) const {
+bool DefaultActivationClient::CanActivateWindow(
+    const aura::Window* window) const {
   return true;
 }
 
diff --git a/ui/wm/core/default_activation_client.h b/ui/wm/core/default_activation_client.h
index b7ff7bc..ff7817e 100644
--- a/ui/wm/core/default_activation_client.h
+++ b/ui/wm/core/default_activation_client.h
@@ -35,9 +35,10 @@
   void ActivateWindow(aura::Window* window) override;
   void DeactivateWindow(aura::Window* window) override;
   const aura::Window* GetActiveWindow() const override;
-  aura::Window* GetActivatableWindow(aura::Window* window) override;
-  aura::Window* GetToplevelWindow(aura::Window* window) override;
-  bool CanActivateWindow(aura::Window* window) const override;
+  aura::Window* GetActivatableWindow(aura::Window* window) const override;
+  const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const override;
+  bool CanActivateWindow(const aura::Window* window) const override;
 
   // Overridden from WindowObserver:
   void OnWindowDestroyed(aura::Window* window) override;
diff --git a/ui/wm/core/focus_controller.cc b/ui/wm/core/focus_controller.cc
index 0514c37a..5ae0413 100644
--- a/ui/wm/core/focus_controller.cc
+++ b/ui/wm/core/focus_controller.cc
@@ -67,15 +67,17 @@
   return active_window_;
 }
 
-aura::Window* FocusController::GetActivatableWindow(aura::Window* window) {
+aura::Window* FocusController::GetActivatableWindow(
+    aura::Window* window) const {
   return rules_->GetActivatableWindow(window);
 }
 
-aura::Window* FocusController::GetToplevelWindow(aura::Window* window) {
+const aura::Window* FocusController::GetToplevelWindow(
+    const aura::Window* window) const {
   return rules_->GetToplevelWindow(window);
 }
 
-bool FocusController::CanActivateWindow(aura::Window* window) const {
+bool FocusController::CanActivateWindow(const aura::Window* window) const {
   return rules_->CanActivateWindow(window);
 }
 
diff --git a/ui/wm/core/focus_controller.h b/ui/wm/core/focus_controller.h
index 7649a30..e88ac12 100644
--- a/ui/wm/core/focus_controller.h
+++ b/ui/wm/core/focus_controller.h
@@ -56,9 +56,10 @@
   void ActivateWindow(aura::Window* window) override;
   void DeactivateWindow(aura::Window* window) override;
   const aura::Window* GetActiveWindow() const override;
-  aura::Window* GetActivatableWindow(aura::Window* window) override;
-  aura::Window* GetToplevelWindow(aura::Window* window) override;
-  bool CanActivateWindow(aura::Window* window) const override;
+  aura::Window* GetActivatableWindow(aura::Window* window) const override;
+  const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const override;
+  bool CanActivateWindow(const aura::Window* window) const override;
 
   // Overridden from aura::client::FocusClient:
   void AddObserver(aura::client::FocusChangeObserver* observer) override;
diff --git a/ui/wm/core/focus_controller_unittest.cc b/ui/wm/core/focus_controller_unittest.cc
index aa233278..ee49966 100644
--- a/ui/wm/core/focus_controller_unittest.cc
+++ b/ui/wm/core/focus_controller_unittest.cc
@@ -411,18 +411,18 @@
   }
 
   // Overridden from BaseFocusRules:
-  bool SupportsChildActivation(aura::Window* window) const override {
+  bool SupportsChildActivation(const aura::Window* window) const override {
     // In FocusControllerTests, only the RootWindow has activatable children.
     return window->GetRootWindow() == window;
   }
-  bool CanActivateWindow(aura::Window* window) const override {
+  bool CanActivateWindow(const aura::Window* window) const override {
     // Restricting focus to a non-activatable child window means the activatable
     // parent outside the focus restriction is activatable.
     bool can_activate =
         CanFocusOrActivate(window) || window->Contains(focus_restriction_);
     return can_activate ? BaseFocusRules::CanActivateWindow(window) : false;
   }
-  bool CanFocusWindow(aura::Window* window,
+  bool CanFocusWindow(const aura::Window* window,
                       const ui::Event* event) const override {
     return CanFocusOrActivate(window)
                ? BaseFocusRules::CanFocusWindow(window, event)
@@ -444,7 +444,7 @@
   }
 
  private:
-  bool CanFocusOrActivate(aura::Window* window) const {
+  bool CanFocusOrActivate(const aura::Window* window) const {
     return !focus_restriction_ || focus_restriction_->Contains(window);
   }
 
diff --git a/ui/wm/core/focus_rules.h b/ui/wm/core/focus_rules.h
index 8a29541..44644c9 100644
--- a/ui/wm/core/focus_rules.h
+++ b/ui/wm/core/focus_rules.h
@@ -27,13 +27,13 @@
   // is considered toplevel is determined by a similar set of rules that
   // govern activation and focus. Not all toplevel windows are activatable,
   // call CanActivateWindow() to determine if a window can be activated.
-  virtual bool IsToplevelWindow(aura::Window* window) const = 0;
+  virtual bool IsToplevelWindow(const aura::Window* window) const = 0;
   // Returns true if |window| can be activated or focused.
-  virtual bool CanActivateWindow(aura::Window* window) const = 0;
+  virtual bool CanActivateWindow(const aura::Window* window) const = 0;
   // For CanFocusWindow(), NULL window is supported, because NULL is a valid
   // focusable window (in the case of clearing focus).
   // If |event| is non-null it is the event triggering the focus change.
-  virtual bool CanFocusWindow(aura::Window* window,
+  virtual bool CanFocusWindow(const aura::Window* window,
                               const ui::Event* event) const = 0;
 
   // Returns the toplevel window containing |window|. Not all toplevel windows
@@ -41,7 +41,9 @@
   // activatable window, which might be in a different hierarchy.
   // Will return NULL if |window| is not contained by a window considered to be
   // a toplevel window.
-  virtual aura::Window* GetToplevelWindow(aura::Window* window) const = 0;
+  virtual const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const = 0;
+
   // Returns the activatable or focusable window given an attempt to activate or
   // focus |window|. Some possible scenarios (not intended to be exhaustive):
   // - |window| is a child of a non-focusable window and so focus must be set
diff --git a/ui/wm/core/window_modality_controller.cc b/ui/wm/core/window_modality_controller.cc
index 44fbc4f..c3503e6 100644
--- a/ui/wm/core/window_modality_controller.cc
+++ b/ui/wm/core/window_modality_controller.cc
@@ -25,27 +25,28 @@
 namespace wm {
 namespace {
 
-bool HasAncestor(aura::Window* window, aura::Window* ancestor) {
+bool HasAncestor(const aura::Window* window, const aura::Window* ancestor) {
   return ancestor && ancestor->Contains(window);
 }
 
-bool TransientChildIsWindowModal(aura::Window* window) {
+bool TransientChildIsWindowModal(const aura::Window* window) {
   return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW;
 }
 
-bool TransientChildIsSystemModal(aura::Window* window) {
+bool TransientChildIsSystemModal(const aura::Window* window) {
   return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM;
 }
 
-bool TransientChildIsChildModal(aura::Window* window) {
+bool TransientChildIsChildModal(const aura::Window* window) {
   return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_CHILD;
 }
 
-aura::Window* GetModalParent(aura::Window* window) {
+aura::Window* GetModalParent(const aura::Window* window) {
   return window->GetProperty(aura::client::kChildModalParentKey);
 }
 
-bool IsModalTransientChild(aura::Window* transient, aura::Window* original) {
+bool IsModalTransientChild(const aura::Window* transient,
+                           const aura::Window* original) {
   return transient->IsVisible() &&
          (TransientChildIsWindowModal(transient) ||
           TransientChildIsSystemModal(transient) ||
@@ -53,14 +54,15 @@
            HasAncestor(original, GetModalParent(transient))));
 }
 
-aura::Window* GetModalTransientChild(aura::Window* activatable,
-                                     aura::Window* original) {
-  for (aura::Window* transient : GetTransientChildren(activatable)) {
+const aura::Window* GetModalTransientChild(const aura::Window* activatable,
+                                           const aura::Window* original) {
+  for (const aura::Window* transient : GetTransientChildren(activatable)) {
     if (IsModalTransientChild(transient, original)) {
       if (GetTransientChildren(transient).empty())
         return transient;
 
-      aura::Window* modal_child = GetModalTransientChild(transient, original);
+      const aura::Window* modal_child =
+          GetModalTransientChild(transient, original);
       return modal_child ? modal_child : transient;
     }
   }
@@ -74,11 +76,16 @@
 }
 
 aura::Window* GetModalTransient(aura::Window* window) {
+  return const_cast<aura::Window*>(
+      GetModalTransient(const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* GetModalTransient(const aura::Window* window) {
   if (!window)
     return nullptr;
 
   // We always want to check for the transient child of the toplevel window.
-  aura::Window* toplevel = GetToplevelWindow(window);
+  const aura::Window* toplevel = GetToplevelWindow(window);
   if (!toplevel)
     return nullptr;
 
diff --git a/ui/wm/core/window_modality_controller.h b/ui/wm/core/window_modality_controller.h
index e9349e5..8a9f929 100644
--- a/ui/wm/core/window_modality_controller.h
+++ b/ui/wm/core/window_modality_controller.h
@@ -31,6 +31,8 @@
 // Returns the modal transient child of |window|, or NULL if |window| does not
 // have any modal transient children.
 WM_CORE_EXPORT aura::Window* GetModalTransient(aura::Window* window);
+WM_CORE_EXPORT const aura::Window* GetModalTransient(
+    const aura::Window* window);
 
 // WindowModalityController is an event filter that consumes events sent to
 // windows that are the transient parents of window-modal windows. This filter
diff --git a/ui/wm/core/window_util.cc b/ui/wm/core/window_util.cc
index 17136c5..4fa23c03 100644
--- a/ui/wm/core/window_util.cc
+++ b/ui/wm/core/window_util.cc
@@ -77,11 +77,11 @@
   return client && client->GetActiveWindow() == window;
 }
 
-bool CanActivateWindow(aura::Window* window) {
+bool CanActivateWindow(const aura::Window* window) {
   DCHECK(window);
   if (!window->GetRootWindow())
     return false;
-  ActivationClient* client = GetActivationClient(window->GetRootWindow());
+  const ActivationClient* client = GetActivationClient(window->GetRootWindow());
   return client && client->CanActivateWindow(window);
 }
 
@@ -118,7 +118,7 @@
   }
 }
 
-bool WindowStateIs(aura::Window* window, ui::WindowShowState state) {
+bool WindowStateIs(const aura::Window* window, ui::WindowShowState state) {
   return window->GetProperty(aura::client::kShowStateKey) == state;
 }
 
@@ -140,7 +140,12 @@
 }
 
 aura::Window* GetToplevelWindow(aura::Window* window) {
-  ActivationClient* client = GetActivationClient(window->GetRootWindow());
+  return const_cast<aura::Window*>(
+      GetToplevelWindow(const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* GetToplevelWindow(const aura::Window* window) {
+  const ActivationClient* client = GetActivationClient(window->GetRootWindow());
   return client ? client->GetToplevelWindow(window) : NULL;
 }
 
diff --git a/ui/wm/core/window_util.h b/ui/wm/core/window_util.h
index fd47b650..1fa69d60 100644
--- a/ui/wm/core/window_util.h
+++ b/ui/wm/core/window_util.h
@@ -29,11 +29,11 @@
 WM_CORE_EXPORT void ActivateWindow(aura::Window* window);
 WM_CORE_EXPORT void DeactivateWindow(aura::Window* window);
 WM_CORE_EXPORT bool IsActiveWindow(const aura::Window* window);
-WM_CORE_EXPORT bool CanActivateWindow(aura::Window* window);
+WM_CORE_EXPORT bool CanActivateWindow(const aura::Window* window);
 WM_CORE_EXPORT void SetWindowFullscreen(aura::Window* window, bool fullscreen);
 
 // Returns true if |window|'s show state is |state|.
-WM_CORE_EXPORT bool WindowStateIs(aura::Window* window,
+WM_CORE_EXPORT bool WindowStateIs(const aura::Window* window,
                                   ui::WindowShowState state);
 
 // Sets the window state to |state|.
@@ -50,6 +50,8 @@
 // Retrieves the toplevel window for |window|. The ActivationClient makes this
 // determination.
 WM_CORE_EXPORT aura::Window* GetToplevelWindow(aura::Window* window);
+WM_CORE_EXPORT const aura::Window* GetToplevelWindow(
+    const aura::Window* window);
 
 // Returns the existing Layer for |root| (and all its descendants) and creates
 // a new layer for |root| and all its descendants. This is intended for
diff --git a/ui/wm/public/activation_client.h b/ui/wm/public/activation_client.h
index a2b5ee7..c5315ce 100644
--- a/ui/wm/public/activation_client.h
+++ b/ui/wm/public/activation_client.h
@@ -40,14 +40,15 @@
   // GetToplevelWindow() below), as the toplevel window may not be activatable
   // (for example it may be blocked by a modal transient, or some other
   // condition).
-  virtual aura::Window* GetActivatableWindow(aura::Window* window) = 0;
+  virtual aura::Window* GetActivatableWindow(aura::Window* window) const = 0;
 
   // Retrieves the toplevel window for |window|, or NULL if there is none.
-  virtual aura::Window* GetToplevelWindow(aura::Window* window) = 0;
+  virtual const aura::Window* GetToplevelWindow(
+      const aura::Window* window) const = 0;
 
   // Returns true if |window| can be activated, false otherwise. If |window| has
   // a modal child it can not be activated.
-  virtual bool CanActivateWindow(aura::Window* window) const = 0;
+  virtual bool CanActivateWindow(const aura::Window* window) const = 0;
 
  protected:
   virtual ~ActivationClient() {}
diff --git a/ui/wm/public/activation_delegate.cc b/ui/wm/public/activation_delegate.cc
index 79d4833..f6ebe79 100644
--- a/ui/wm/public/activation_delegate.cc
+++ b/ui/wm/public/activation_delegate.cc
@@ -18,7 +18,7 @@
   window->SetProperty(kActivationDelegateKey, delegate);
 }
 
-ActivationDelegate* GetActivationDelegate(aura::Window* window) {
+ActivationDelegate* GetActivationDelegate(const aura::Window* window) {
   return window->GetProperty(kActivationDelegateKey);
 }
 
diff --git a/ui/wm/public/activation_delegate.h b/ui/wm/public/activation_delegate.h
index 9fc83f4..9d6bb34 100644
--- a/ui/wm/public/activation_delegate.h
+++ b/ui/wm/public/activation_delegate.h
@@ -28,7 +28,7 @@
 WM_PUBLIC_EXPORT void SetActivationDelegate(aura::Window* window,
                                             ActivationDelegate* delegate);
 WM_PUBLIC_EXPORT ActivationDelegate* GetActivationDelegate(
-    aura::Window* window);
+    const aura::Window* window);
 
 }  // namespace wm
 
diff --git a/url/BUILD.gn b/url/BUILD.gn
index e75c0f2..e7a43342 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -167,6 +167,20 @@
   }
 }
 
+test("url_perftests") {
+  sources = [
+    "run_all_perftests.cc",
+    "url_parse_perftest.cc",
+  ]
+
+  deps = [
+    ":url",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
+
 fuzzer_test("gurl_fuzzer") {
   sources = [
     "gurl_fuzzer.cc",
diff --git a/url/run_all_perftests.cc b/url/run_all_perftests.cc
new file mode 100644
index 0000000..4fe3675
--- /dev/null
+++ b/url/run_all_perftests.cc
@@ -0,0 +1,14 @@
+// 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/perf_test_suite.h"
+
+int main(int argc, char** argv) {
+  base::PerfTestSuite test_suite(argc, argv);
+  return base::LaunchUnitTestsSerially(
+      argc, argv,
+      base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/url/url_parse_perftest.cc b/url/url_parse_perftest.cc
new file mode 100644
index 0000000..6f972cd
--- /dev/null
+++ b/url/url_parse_perftest.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_piece.h"
+#include "base/test/perf_time_logger.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon.h"
+#include "url/url_canon_stdstring.h"
+
+namespace {
+
+TEST(URLParse, FullURL) {
+  constexpr base::StringPiece kUrl =
+      "http://me:pass@host/foo/bar.html;param?query=yes#ref";
+
+  url::Parsed parsed;
+  base::PerfTimeLogger timer("Full_URL_Parse_AMillion");
+
+  for (int i = 0; i < 1000000; i++)
+    url::ParseStandardURL(kUrl.data(), kUrl.size(), &parsed);
+  timer.Done();
+}
+
+constexpr base::StringPiece kTypicalUrl1 =
+    "http://www.google.com/"
+    "search?q=url+parsing&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:"
+    "official&client=firefox-a";
+
+constexpr base::StringPiece kTypicalUrl2 =
+    "http://www.amazon.com/Stephen-King-Thrillers-Horror-People/dp/0766012336/"
+    "ref=sr_1_2/133-4144931-4505264?ie=UTF8&s=books&qid=2144880915&sr=8-2";
+
+constexpr base::StringPiece kTypicalUrl3 =
+    "http://store.apple.com/1-800-MY-APPLE/WebObjects/AppleStore.woa/wa/"
+    "RSLID?nnmm=browse&mco=578E9744&node=home/desktop/mac_pro";
+
+TEST(URLParse, TypicalURLParse) {
+  url::Parsed parsed1;
+  url::Parsed parsed2;
+  url::Parsed parsed3;
+
+  // Do this 1/3 of a million times since we do 3 different URLs.
+  base::PerfTimeLogger parse_timer("Typical_URL_Parse_AMillion");
+  for (int i = 0; i < 333333; i++) {
+    url::ParseStandardURL(kTypicalUrl1.data(), kTypicalUrl1.size(), &parsed1);
+    url::ParseStandardURL(kTypicalUrl2.data(), kTypicalUrl2.size(), &parsed2);
+    url::ParseStandardURL(kTypicalUrl3.data(), kTypicalUrl3.size(), &parsed3);
+  }
+  parse_timer.Done();
+}
+
+// Includes both parsing and canonicalization with no mallocs.
+TEST(URLParse, TypicalURLParseCanon) {
+  url::Parsed parsed1;
+  url::Parsed parsed2;
+  url::Parsed parsed3;
+
+  base::PerfTimeLogger canon_timer("Typical_Parse_Canon_AMillion");
+  url::Parsed out_parsed;
+  url::RawCanonOutput<1024> output;
+  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
+    url::ParseStandardURL(kTypicalUrl1.data(), kTypicalUrl1.size(), &parsed1);
+    output.set_length(0);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl1.data(), kTypicalUrl1.size(), parsed1,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output,
+        &out_parsed);
+
+    url::ParseStandardURL(kTypicalUrl2.data(), kTypicalUrl2.size(), &parsed2);
+    output.set_length(0);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl2.data(), kTypicalUrl2.size(), parsed2,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output,
+        &out_parsed);
+
+    url::ParseStandardURL(kTypicalUrl3.data(), kTypicalUrl3.size(), &parsed3);
+    output.set_length(0);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl3.data(), kTypicalUrl3.size(), parsed3,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output,
+        &out_parsed);
+  }
+  canon_timer.Done();
+}
+
+// Includes both parsing and canonicalization, and mallocs for the output.
+TEST(URLParse, TypicalURLParseCanonStdString) {
+  url::Parsed parsed1;
+  url::Parsed parsed2;
+  url::Parsed parsed3;
+
+  base::PerfTimeLogger canon_timer("Typical_Parse_Canon_AMillion");
+  url::Parsed out_parsed;
+  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
+    url::ParseStandardURL(kTypicalUrl1.data(), kTypicalUrl1.size(), &parsed1);
+    std::string out1;
+    url::StdStringCanonOutput output1(&out1);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl1.data(), kTypicalUrl1.size(), parsed1,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output1,
+        &out_parsed);
+
+    url::ParseStandardURL(kTypicalUrl2.data(), kTypicalUrl2.size(), &parsed2);
+    std::string out2;
+    url::StdStringCanonOutput output2(&out2);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl2.data(), kTypicalUrl2.size(), parsed2,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output2,
+        &out_parsed);
+
+    url::ParseStandardURL(kTypicalUrl3.data(), kTypicalUrl3.size(), &parsed3);
+    std::string out3;
+    url::StdStringCanonOutput output3(&out3);
+    url::CanonicalizeStandardURL(
+        kTypicalUrl3.data(), kTypicalUrl3.size(), parsed3,
+        url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, nullptr, &output3,
+        &out_parsed);
+  }
+  canon_timer.Done();
+}
+
+TEST(URLParse, GURL) {
+  base::PerfTimeLogger gurl_timer("Typical_GURL_AMillion");
+  for (int i = 0; i < 333333; i++) {  // divide by 3 so we get 1M
+    GURL gurl1(kTypicalUrl1);
+    GURL gurl2(kTypicalUrl2);
+    GURL gurl3(kTypicalUrl3);
+  }
+  gurl_timer.Done();
+}
+
+}  // namespace
diff --git a/webrunner/BUILD.gn b/webrunner/BUILD.gn
index adb6bd2..8f973d0 100644
--- a/webrunner/BUILD.gn
+++ b/webrunner/BUILD.gn
@@ -92,10 +92,17 @@
 
 source_set("castrunner_common") {
   sources = [
+    "app/cast/bindings/cast_channel.cc",
+    "app/cast/bindings/cast_channel.h",
     "app/cast/cast_runner.cc",
     "app/cast/cast_runner.h",
   ]
+  data = [
+    "app/cast/bindings/cast_channel.js",
+  ]
   deps = [
+    ":mem_buffer_common",
+    ":named_message_port_connector",
     ":web_fidl",
     "//base",
     "//url",
@@ -104,6 +111,7 @@
     ":cast_fidl",
     ":webrunner_common",
   ]
+  configs += [ ":webrunner_implementation" ]
 }
 
 executable("castrunner_exe") {
@@ -136,7 +144,7 @@
     ":app_config_manager_test_support",
     ":castrunner_common",
     ":castrunner_test_support",
-    ":test_common",
+    ":test_support",
     ":test_support",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
@@ -152,7 +160,7 @@
     ":app_config_manager_test_support",
     ":castrunner_common",
     ":castrunner_test_support",
-    ":test_common",
+    ":test_support",
     ":test_support",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
@@ -174,14 +182,38 @@
     "common/named_message_port_connector.js",
   ]
   deps = [
-    ":common",
+    ":mem_buffer_common",
     ":web_fidl",
     "//base",
     "//third_party/fuchsia-sdk/sdk:mem",
   ]
+  configs += [ ":webrunner_implementation" ]
 }
 
-source_set("common") {
+test("castrunner_browsertests") {
+  sources = [
+    "app/cast/bindings/cast_channel_browsertest.cc",
+  ]
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  data = [
+    "app/cast/test/data",
+  ]
+  deps = [
+    ":browsertest_common",
+    ":castrunner_common",
+    ":mem_buffer_common",
+    ":named_message_port_connector",
+    ":test_support",
+    ":web_fidl",
+    "//base/test:test_support",
+    "//content/public/browser",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//ui/ozone",
+  ]
+}
+
+source_set("mem_buffer_common") {
   sources = [
     "common/mem_buffer_util.cc",
     "common/mem_buffer_util.h",
@@ -242,12 +274,19 @@
 source_set("test_support") {
   testonly = true
   sources = [
+    # TODO(kmarshall): Move common/test/* to test/.
+    "common/test/test_common.cc",
+    "common/test/test_common.h",
     "test/fake_context.cc",
     "test/fake_context.h",
     "test/promise.h",
   ]
   deps = [
+    ":mem_buffer_common",
+    ":web_fidl",
     "//base",
+    "//base/test:test_config",
+    "//content/test:test_support",
   ]
   public_deps = [
     ":web_fidl",
@@ -291,7 +330,7 @@
 
 component("service_lib") {
   deps = [
-    ":common",
+    ":mem_buffer_common",
     ":mojom",
     ":service_pak",
     "//base",
@@ -402,21 +441,6 @@
   output = "$root_out_dir/webrunner.pak"
 }
 
-source_set("test_common") {
-  testonly = true
-  sources = [
-    "common/test/test_common.cc",
-    "common/test/test_common.h",
-  ]
-  deps = [
-    ":common",
-    ":web_fidl",
-    "//base",
-    "//base/test:test_config",
-    "//content/test:test_support",
-  ]
-}
-
 source_set("browsertest_common") {
   testonly = true
   sources = [
@@ -447,14 +471,14 @@
     "common/test/data",
   ]
   data_deps = [
-    ":common",
+    ":named_message_port_connector",
   ]
   deps = [
     ":browsertest_common",
-    ":common",
+    ":mem_buffer_common",
     ":named_message_port_connector",
     ":service_lib",
-    ":test_common",
+    ":test_support",
     ":test_support",
     ":web_fidl",
     "//base/test:test_support",
@@ -486,7 +510,7 @@
     "app/web/webrunner_smoke_test.cc",
   ]
   deps = [
-    ":test_common",
+    ":test_support",
     "//base",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
@@ -531,6 +555,11 @@
 
   sources = [
     "fidl/cast/application_config.fidl",
+    "fidl/cast/cast_channel.fidl",
+  ]
+
+  public_deps = [
+    ":web_fidl",
   ]
 }
 
diff --git a/webrunner/app/cast/bindings/cast_channel.cc b/webrunner/app/cast/bindings/cast_channel.cc
new file mode 100644
index 0000000..e5a6af1
--- /dev/null
+++ b/webrunner/app/cast/bindings/cast_channel.cc
@@ -0,0 +1,122 @@
+// 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 "webrunner/app/cast/bindings/cast_channel.h"
+
+#include <lib/fit/function.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "webrunner/common/mem_buffer_util.h"
+#include "webrunner/common/named_message_port_connector.h"
+#include "webrunner/fidl/chromium/web/cpp/fidl.h"
+
+namespace castrunner {
+
+// Unique identifier of the Cast Channel message port, used by the JavaScript
+// API to connect to the port.
+const char kMessagePortName[] = "cast.__platform__.channel";
+
+CastChannelImpl::CastChannelImpl(
+    chromium::web::Frame* frame,
+    webrunner::NamedMessagePortConnector* connector)
+    : frame_(frame), connector_(connector) {
+  DCHECK(connector_);
+  DCHECK(frame_);
+
+  connector->Register(
+      kMessagePortName,
+      base::BindRepeating(&CastChannelImpl::OnMasterPortReceived,
+                          base::Unretained(this)),
+      frame_);
+
+  // Load the cast.__platform__.channel bundle from the package assets, into a
+  // mem::Buffer.
+  base::FilePath assets_path;
+  CHECK(base::PathService::Get(base::DIR_ASSETS, &assets_path));
+  fuchsia::mem::Buffer bindings_buf = webrunner::MemBufferFromFile(base::File(
+      assets_path.AppendASCII("webrunner/app/cast/bindings/cast_channel.js"),
+      base::File::FLAG_OPEN | base::File::FLAG_READ));
+  CHECK(bindings_buf.vmo);
+
+  // Inject the cast.__platform__.channel API to all origins.
+  std::vector<std::string> origins = {"*"};
+
+  // Configure the bundle to be re-injected every time the |frame_| content is
+  // loaded.
+  frame_->ExecuteJavaScript(
+      std::move(origins), std::move(bindings_buf),
+      chromium::web::ExecuteMode::ON_PAGE_LOAD,
+      [](bool success) { CHECK(success) << "Couldn't insert bindings."; });
+}
+
+CastChannelImpl::~CastChannelImpl() {
+  connector_->Unregister(frame_, kMessagePortName);
+}
+
+void CastChannelImpl::OnMasterPortError() {
+  master_port_.Unbind();
+}
+
+void CastChannelImpl::Connect(ConnectCallback callback) {
+  // If there is already a bound Cast Channel ready, then return it.
+  if (pending_channel_) {
+    callback(std::move(pending_channel_));
+    return;
+  }
+
+  pending_connect_cb_ = std::move(callback);
+
+  if (master_port_) {
+    master_port_->ReceiveMessage(
+        fit::bind_member(this, &CastChannelImpl::OnCastChannelMessageReceived));
+  }
+
+  // If there is no master port available at this time, then defer invocation of
+  // |pending_connect_cb_| until the master port has been received.
+}
+
+void CastChannelImpl::OnMasterPortReceived(chromium::web::MessagePortPtr port) {
+  DCHECK(port);
+
+  master_port_ = std::move(port);
+  master_port_.set_error_handler([this](zx_status_t status) {
+    ZX_LOG_IF(WARNING, status != ZX_ERR_PEER_CLOSED, status)
+        << "Cast Channel master port disconnected.";
+    OnMasterPortError();
+  });
+
+  if (pending_connect_cb_) {
+    // Resolve the in-flight Connect call.
+    Connect(std::move(pending_connect_cb_));
+  }
+}
+
+void CastChannelImpl::OnCastChannelMessageReceived(
+    chromium::web::WebMessage message) {
+  if (!message.incoming_transfer ||
+      !message.incoming_transfer->is_message_port()) {
+    LOG(WARNING) << "Received a CastChannel without a message port.";
+    OnMasterPortError();
+    return;
+  }
+
+  // Fulfill an outstanding Connect() operation, if there is one.
+  if (pending_connect_cb_) {
+    pending_connect_cb_(std::move(message.incoming_transfer->message_port()));
+    pending_connect_cb_ = {};
+    return;
+  }
+
+  pending_channel_ = std::move(message.incoming_transfer->message_port());
+}
+
+}  // namespace castrunner
diff --git a/webrunner/app/cast/bindings/cast_channel.h b/webrunner/app/cast/bindings/cast_channel.h
new file mode 100644
index 0000000..04bbd0f
--- /dev/null
+++ b/webrunner/app/cast/bindings/cast_channel.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef WEBRUNNER_APP_CAST_BINDINGS_CAST_CHANNEL_H_
+#define WEBRUNNER_APP_CAST_BINDINGS_CAST_CHANNEL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "webrunner/common/webrunner_export.h"
+#include "webrunner/fidl/chromium/cast/cpp/fidl.h"
+#include "webrunner/fidl/chromium/web/cpp/fidl.h"
+
+namespace webrunner {
+class NamedMessagePortConnector;
+}
+
+namespace castrunner {
+
+// Handles the injection of cast.__platform__.channel bindings into pages'
+// scripting context, and establishes a bidirectional message pipe over
+// which the two communicate.
+class WEBRUNNER_EXPORT CastChannelImpl : public chromium::cast::CastChannel {
+ public:
+  // Attaches CastChannel bindings and port to a |frame|.
+  // |frame|: The frame to be provided with a CastChannel.
+  // |connector|: The NamedMessagePortConnector to use for establishing
+  // transport.
+  // Both |frame| and |connector| must outlive |this|.
+  CastChannelImpl(chromium::web::Frame* frame,
+                  webrunner::NamedMessagePortConnector* connector);
+  ~CastChannelImpl() override;
+
+  // chromium::cast::CastChannel implementation.
+  void Connect(ConnectCallback callback) override;
+
+ private:
+  // Receives a port used for receiving new Cast Channel ports.
+  void OnMasterPortReceived(chromium::web::MessagePortPtr port);
+
+  // Receives a message containing a newly opened Cast Channel from
+  // |master_port_|.
+  void OnCastChannelMessageReceived(chromium::web::WebMessage message);
+
+  // Handles error conditions on |master_port_|.
+  void OnMasterPortError();
+
+  chromium::web::Frame* const frame_;
+  webrunner::NamedMessagePortConnector* const connector_;
+
+  // A long-lived port, used to receive new Cast Channel ports when they are
+  // opened. Should be automatically  populated by the
+  // NamedMessagePortConnector whenever |frame| loads a new page.
+  chromium::web::MessagePortPtr master_port_;
+
+  fuchsia::mem::Buffer bindings_script_;
+  ConnectCallback pending_connect_cb_;
+
+  // A Cast Channel received from the webpage, waiting to be handled via
+  // ListenForChannel().
+  fidl::InterfaceHandle<chromium::web::MessagePort> pending_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastChannelImpl);
+};
+
+}  // namespace castrunner
+
+#endif  // WEBRUNNER_APP_CAST_BINDINGS_CAST_CHANNEL_H_
diff --git a/webrunner/app/cast/bindings/cast_channel.js b/webrunner/app/cast/bindings/cast_channel.js
new file mode 100644
index 0000000..07ea745
--- /dev/null
+++ b/webrunner/app/cast/bindings/cast_channel.js
@@ -0,0 +1,74 @@
+// 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.
+
+// Implementation of the cast.__platform__.channel API which uses MessagePort
+// IPC to communicate with an actual Cast Channel implementation provided by
+// the content embedder. There is at most one channel which may be opened (able
+// to send & receive messages) or closed.
+cast.__platform__.channel = new class {
+  constructor() {
+    this.master_port_ = cast.__platform__.connector.bind(
+        'cast.__platform__.channel',
+        function(ignored) {
+          // |master_port_| is send-only, so ignore all incoming messages.
+        });
+  }
+
+  // Signals to the peer that the Cast Channel is opened.
+  // |openHandler|: A callback function which is invoked on channel open with
+  //                a boolean indicating success.
+  // |messageHandler|: Invoked when a message arrives from the peer.
+  open(openHandler, messageHandler) {
+    if (this.current_port_) {
+      console.error('open() called on an open Cast Channel.');
+      openHandler(false);
+      return;
+    }
+
+    if (!messageHandler) {
+      console.error('Null messageHandler passed to open().');
+      openHandler(false);
+      return;
+    }
+
+    // Create the MessageChannel for Cast Channel and distribute its ports.
+    var channel = new MessageChannel();
+    this.master_port_.sendMessage('', [channel.port1]);
+
+    this.current_port_ = channel.port2;
+    this.current_port_.onmessage = function(message) {
+      messageHandler(message.data);
+    };
+    this.current_port_.onerror = function() {
+      console.error('Cast Channel was closed unexpectedly by peer.');
+      return;
+    };
+
+    openHandler(true);
+  }
+
+  // Closes the Cast Channel.
+  close(closeHandler) {
+    if (this.current_port_) {
+      this.current_port_.close();
+      this.current_port_ = null;
+    }
+  }
+
+  // Sends a message to the Cast Channel's peer.
+  send(message) {
+    if (!this.current_port_) {
+      console.error('send() called on a closed Cast Channel.');
+      return;
+    }
+
+    this.current_port_.postMessage(message);
+  }
+
+  // Used to send newly opened Cast Channel ports to C++.
+  master_port_ = null;
+
+  // The current opened Cast Channel.
+  current_port_ = null;
+};
diff --git a/webrunner/app/cast/bindings/cast_channel_browsertest.cc b/webrunner/app/cast/bindings/cast_channel_browsertest.cc
new file mode 100644
index 0000000..eec926a
--- /dev/null
+++ b/webrunner/app/cast/bindings/cast_channel_browsertest.cc
@@ -0,0 +1,222 @@
+// Copyright 2018 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 <lib/fidl/cpp/binding.h>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_restrictions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/url_constants.h"
+#include "webrunner/app/cast/bindings/cast_channel.h"
+#include "webrunner/common/mem_buffer_util.h"
+#include "webrunner/common/named_message_port_connector.h"
+#include "webrunner/common/test/test_common.h"
+#include "webrunner/common/test/webrunner_browser_test.h"
+#include "webrunner/test/promise.h"
+
+namespace castrunner {
+
+// Use a shorter name for NavigationEvent, because it is
+// referenced frequently in this file.
+using NavigationDetails = chromium::web::NavigationEvent;
+
+class CastChannelImplTest : public webrunner::WebRunnerBrowserTest,
+                            public chromium::web::NavigationEventObserver {
+ public:
+  CastChannelImplTest() : run_timeout_(TestTimeouts::action_timeout()) {
+    set_test_server_root(base::FilePath("webrunner/app/cast/test/data"));
+  }
+
+  ~CastChannelImplTest() override = default;
+
+ protected:
+  void SetUpOnMainThread() override {
+    webrunner::WebRunnerBrowserTest::SetUpOnMainThread();
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    frame_ = WebRunnerBrowserTest::CreateFrame(this);
+    connector_ = std::make_unique<webrunner::NamedMessagePortConnector>();
+  }
+
+  void OnNavigationStateChanged(
+      chromium::web::NavigationEvent change,
+      OnNavigationStateChangedCallback callback) override {
+    connector_->NotifyPageLoad(frame_.get());
+    if (navigate_run_loop_)
+      navigate_run_loop_->Quit();
+    callback();
+  }
+
+  void CheckLoadUrl(const std::string& url,
+                    chromium::web::NavigationController* controller) {
+    navigate_run_loop_ = std::make_unique<base::RunLoop>();
+    controller->LoadUrl(url, nullptr);
+    navigate_run_loop_->Run();
+    navigate_run_loop_.reset();
+  }
+
+  std::unique_ptr<base::RunLoop> navigate_run_loop_;
+  chromium::web::FramePtr frame_;
+  std::unique_ptr<webrunner::NamedMessagePortConnector> connector_;
+
+ private:
+  const base::RunLoop::ScopedRunTimeoutForTest run_timeout_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastChannelImplTest);
+};
+
+IN_PROC_BROWSER_TEST_F(CastChannelImplTest, CastChannelBuffered) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL test_url(embedded_test_server()->GetURL("/cast_channel.html"));
+
+  frame_->SetJavaScriptLogLevel(chromium::web::LogLevel::INFO);
+  chromium::web::NavigationControllerPtr controller;
+  frame_->GetNavigationController(controller.NewRequest());
+
+  testing::InSequence seq;
+  CastChannelImpl cast_channel_instance(frame_.get(), connector_.get());
+  fidl::Binding<chromium::cast::CastChannel> cast_channel_binding(
+      &cast_channel_instance);
+  chromium::cast::CastChannelPtr cast_channel =
+      cast_channel_binding.NewBinding().Bind();
+
+  // Verify that CastChannelImpl can properly handle message, connect,
+  // disconnect, and MessagePort disconnection events.
+  CheckLoadUrl(test_url.spec(), controller.get());
+
+  chromium::web::MessagePortPtr channel;
+
+  // Expect channel open.
+  {
+    base::RunLoop run_loop;
+    webrunner::Promise<fidl::InterfaceHandle<chromium::web::MessagePort>>
+        channel_promise(run_loop.QuitClosure());
+    cast_channel->Connect(
+        webrunner::ConvertToFitFunction(channel_promise.GetReceiveCallback()));
+    run_loop.Run();
+
+    channel = channel_promise->Bind();
+  }
+
+  // Send a message to the channel.
+  auto expected_list = {"this", "is", "a", "test"};
+  for (const std::string& expected : expected_list) {
+    base::RunLoop run_loop;
+    webrunner::Promise<chromium::web::WebMessage> message(
+        run_loop.QuitClosure());
+    channel->ReceiveMessage(
+        webrunner::ConvertToFitFunction(message.GetReceiveCallback()));
+    run_loop.Run();
+
+    EXPECT_EQ(webrunner::StringFromMemBufferOrDie(message->data), expected);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(CastChannelImplTest, CastChannelReconnect) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL test_url(embedded_test_server()->GetURL("/cast_channel_reconnect.html"));
+  GURL empty_url(embedded_test_server()->GetURL("/defaultresponse"));
+
+  frame_->SetJavaScriptLogLevel(chromium::web::LogLevel::INFO);
+  chromium::web::NavigationControllerPtr controller;
+  frame_->GetNavigationController(controller.NewRequest());
+
+  testing::InSequence seq;
+  CastChannelImpl cast_channel_instance(frame_.get(), connector_.get());
+  fidl::Binding<chromium::cast::CastChannel> cast_channel_binding(
+      &cast_channel_instance);
+  chromium::cast::CastChannelPtr cast_channel =
+      cast_channel_binding.NewBinding().Bind();
+
+  // Verify that CastChannelImpl can properly handle message, connect,
+  // disconnect, and MessagePort disconnection events.
+  // Also verify that the cast channel is used across inter-page navigations.
+  for (int i = 0; i < 5; ++i) {
+    CheckLoadUrl(test_url.spec(), controller.get());
+
+    chromium::web::MessagePortPtr channel;
+
+    // Expect channel open.
+    {
+      base::RunLoop run_loop;
+      webrunner::Promise<fidl::InterfaceHandle<chromium::web::MessagePort>>
+          channel_promise(run_loop.QuitClosure());
+      cast_channel->Connect(webrunner::ConvertToFitFunction(
+          channel_promise.GetReceiveCallback()));
+      run_loop.Run();
+
+      channel = channel_promise->Bind();
+    }
+
+    // Expect channel close.
+    {
+      base::RunLoop run_loop;
+      channel.set_error_handler([&run_loop](zx_status_t) { run_loop.Quit(); });
+      run_loop.Run();
+    }
+
+    // Expect channel re-open.
+    {
+      base::RunLoop run_loop;
+      webrunner::Promise<fidl::InterfaceHandle<chromium::web::MessagePort>>
+          channel_promise(run_loop.QuitClosure());
+      cast_channel->Connect(webrunner::ConvertToFitFunction(
+          channel_promise.GetReceiveCallback()));
+      run_loop.Run();
+
+      channel = channel_promise->Bind();
+    }
+
+    // Read "reconnected" from the channel.
+    {
+      base::RunLoop run_loop;
+      webrunner::Promise<chromium::web::WebMessage> message(
+          run_loop.QuitClosure());
+      channel->ReceiveMessage(
+          webrunner::ConvertToFitFunction(message.GetReceiveCallback()));
+      run_loop.Run();
+
+      EXPECT_EQ(webrunner::StringFromMemBufferOrDie(message->data),
+                "reconnected");
+    }
+
+    // Send a message to the channel.
+    {
+      chromium::web::WebMessage message;
+      message.data = webrunner::MemBufferFromString("hello");
+
+      base::RunLoop run_loop;
+      webrunner::Promise<bool> post_result(run_loop.QuitClosure());
+      channel->PostMessage(
+          std::move(message),
+          webrunner::ConvertToFitFunction(post_result.GetReceiveCallback()));
+      run_loop.Run();
+      EXPECT_EQ(true, *post_result);
+    }
+
+    // Get a message from the channel.
+    {
+      base::RunLoop run_loop;
+      webrunner::Promise<chromium::web::WebMessage> message(
+          run_loop.QuitClosure());
+      channel->ReceiveMessage(
+          webrunner::ConvertToFitFunction(message.GetReceiveCallback()));
+      run_loop.Run();
+
+      EXPECT_EQ(webrunner::StringFromMemBufferOrDie(message->data),
+                "ack hello");
+    }
+
+    // Navigate away.
+    CheckLoadUrl(empty_url.spec(), controller.get());
+  }
+}
+
+}  // namespace castrunner
diff --git a/webrunner/app/cast/test/data/cast_channel.html b/webrunner/app/cast/test/data/cast_channel.html
new file mode 100644
index 0000000..ed9bd66
--- /dev/null
+++ b/webrunner/app/cast/test/data/cast_channel.html
@@ -0,0 +1,17 @@
+<html>
+  <head><title>castChannel</title></head>
+  <body>
+    <script>
+      var reopened = false;
+
+      function openHandler() {
+        cast.__platform__.channel.send('this');
+        cast.__platform__.channel.send('is');
+        cast.__platform__.channel.send('a');
+        cast.__platform__.channel.send('test');
+      }
+
+      cast.__platform__.channel.open(openHandler, function(ignored) {});
+    </script>
+  </body>
+</html>
diff --git a/webrunner/app/cast/test/data/cast_channel_reconnect.html b/webrunner/app/cast/test/data/cast_channel_reconnect.html
new file mode 100644
index 0000000..936b2db
--- /dev/null
+++ b/webrunner/app/cast/test/data/cast_channel_reconnect.html
@@ -0,0 +1,25 @@
+<html>
+  <head><title>castChannel</title></head>
+  <body>
+    <script>
+      var reopened = false;
+
+      // Executes state transitions: open, close, open, send, receive, send.
+      function openHandler() {
+        if (!reopened) {
+          reopened = true;
+          cast.__platform__.channel.close(function() {});
+          cast.__platform__.channel.open(openHandler, messageHandler);
+        } else {
+          cast.__platform__.channel.send('reconnected');
+        }
+      }
+
+      function messageHandler(data) {
+        cast.__platform__.channel.send('ack ' + data);
+      }
+
+      cast.__platform__.channel.open(openHandler, messageHandler);
+    </script>
+  </body>
+</html>
diff --git a/webrunner/browser/frame_impl.cc b/webrunner/browser/frame_impl.cc
index 6d54da4..2e26b4d 100644
--- a/webrunner/browser/frame_impl.cc
+++ b/webrunner/browser/frame_impl.cc
@@ -117,13 +117,13 @@
   ~FrameFocusRules() override = default;
 
   // wm::BaseFocusRules implementation.
-  bool SupportsChildActivation(aura::Window*) const override;
+  bool SupportsChildActivation(const aura::Window*) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FrameFocusRules);
 };
 
-bool FrameFocusRules::SupportsChildActivation(aura::Window*) const {
+bool FrameFocusRules::SupportsChildActivation(const aura::Window*) const {
   // TODO(crbug.com/878439): Return a result based on window properties such as
   // visibility.
   return true;
diff --git a/webrunner/browser/webrunner_browser_main.cc b/webrunner/browser/webrunner_browser_main.cc
index 95b8df0c..f4e183a1 100644
--- a/webrunner/browser/webrunner_browser_main.cc
+++ b/webrunner/browser/webrunner_browser_main.cc
@@ -13,8 +13,8 @@
 namespace webrunner {
 
 int WebRunnerBrowserMain(const content::MainFunctionParams& parameters) {
-  std::unique_ptr<content::BrowserMainRunner> main_runner(
-      content::BrowserMainRunner::Create());
+  std::unique_ptr<content::BrowserMainRunner> main_runner =
+      content::BrowserMainRunner::Create();
   int exit_code = main_runner->Initialize(parameters);
   if (exit_code >= 0)
     return exit_code;
diff --git a/webrunner/common/named_message_port_connector.js b/webrunner/common/named_message_port_connector.js
index 5c0beab..691c8e3 100644
--- a/webrunner/common/named_message_port_connector.js
+++ b/webrunner/common/named_message_port_connector.js
@@ -20,13 +20,14 @@
     this.id_ = id;
   }
 
-  // Sends a message to the port, or buffers the message for later delivery if
-  // no port is available yet.
-  sendMessage(message) {
-    if (this.port_)
-      this.port_.postMessage(message);
-    else
-      this.buffer_.push(message);
+  // Sends a message and optionally a list of Transferables to the Port,
+  // or buffers the message for later delivery if |this| is unconnected.
+  sendMessage(data, transferables) {
+    if (this.port_) {
+      this.port_.postMessage(data, transferables);
+    } else {
+      this.buffer_.push({'data': data, 'transferables': transferables});
+    }
   }
 
   // Receives a MessagePort from cast.__platform__.connector.
@@ -40,7 +41,8 @@
 
     // Flush and destroy the pre-connect buffer.
     for (var i = 0; i < this.buffer_.length; ++i) {
-      this.port_.postMessage(this.buffer_[i]);
+      this.port_.postMessage(this.buffer_[i].data,
+          this.buffer_[i].transferables);
     }
     this.buffer = null;
   }
diff --git a/webrunner/fidl/cast/cast_channel.fidl b/webrunner/fidl/cast/cast_channel.fidl
new file mode 100644
index 0000000..2b6331a6
--- /dev/null
+++ b/webrunner/fidl/cast/cast_channel.fidl
@@ -0,0 +1,17 @@
+// 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.
+
+library chromium.cast;
+
+using chromium.web;
+
+[Discoverable]
+interface CastChannel {
+  // Handles Cast Channel open calls from the webpage.
+  // The new Cast Channel's message port is returned asynchronously once
+  // opened by the webpage. The port is disconnected when the peer's Cast
+  // Channel is closed.
+  1: Connect() -> (chromium.web.MessagePort channel);
+};
+
diff --git a/webrunner/renderer/on_load_script_injector.cc b/webrunner/renderer/on_load_script_injector.cc
index f2d7201..1a5abe9 100644
--- a/webrunner/renderer/on_load_script_injector.cc
+++ b/webrunner/renderer/on_load_script_injector.cc
@@ -8,7 +8,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/auto_reset.h"
 #include "base/memory/shared_memory_handle.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/renderer/render_frame.h"
@@ -30,15 +29,16 @@
   bindings_.AddBinding(this, std::move(request));
 }
 
-void OnLoadScriptInjector::DidClearWindowObject() {
-  // The script may cause the window to be cleared (e.g. by producing a page
-  // load event), so the guard is used to prevent reentrancy and potential
-  // infinite loops.
-  if (is_handling_clear_window_object_)
+void OnLoadScriptInjector::DidCommitProvisionalLoad(
+    bool is_same_document_navigation,
+    ui::PageTransition transition) {
+  // Ignore pushState or document fragment navigation.
+  if (is_same_document_navigation)
     return;
 
-  base::AutoReset<bool> clear_window_reset(&is_handling_clear_window_object_,
-                                           true);
+  // Don't inject anything for subframes.
+  if (!render_frame()->IsMainFrame())
+    return;
 
   for (mojo::ScopedSharedBufferHandle& script : on_load_scripts_) {
     DCHECK_EQ(script->GetSize() % 2, 0u);  // Crude check to see this is UTF-16.
diff --git a/webrunner/renderer/on_load_script_injector.h b/webrunner/renderer/on_load_script_injector.h
index 362c8d9..7208a37 100644
--- a/webrunner/renderer/on_load_script_injector.h
+++ b/webrunner/renderer/on_load_script_injector.h
@@ -31,14 +31,14 @@
 
   // RenderFrameObserver override:
   void OnDestruct() override;
-  void DidClearWindowObject() override;
+  void DidCommitProvisionalLoad(bool is_same_document_navigation,
+                                ui::PageTransition transition) override;
 
  private:
   // Called by OnDestruct(), when the RenderFrame is destroyed.
   ~OnLoadScriptInjector() override;
 
   std::vector<mojo::ScopedSharedBufferHandle> on_load_scripts_;
-  bool is_handling_clear_window_object_ = false;
   mojo::AssociatedBindingSet<mojom::OnLoadScriptInjector> bindings_;
   base::WeakPtrFactory<OnLoadScriptInjector> weak_ptr_factory_;