diff --git a/.gn b/.gn
index 3a7b224f..70856f6 100644
--- a/.gn
+++ b/.gn
@@ -386,6 +386,7 @@
   # "//third_party/crashpad/*", 20ish errors
   "//third_party/crc32c/*",
   "//third_party/cros_system_api/*",
+  "//third_party/custom_tabs_client/*",
   "//third_party/cython/*",
   "//third_party/d3/*",
   "//third_party/dawn/*",
diff --git a/DEPS b/DEPS
index 01dac9e..c4b4011 100644
--- a/DEPS
+++ b/DEPS
@@ -145,7 +145,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '593290ed75fd3fbf060d94021925cdf76c62c38e',
+  'skia_revision': 'b2c5a94b1c7a40a223c198573aa9bd83998945e4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -157,7 +157,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': '483ee3fa2315298aba6cbaf6f62d515f7214c0f7',
+  'angle_revision': '93560ef51dc1e79ddeb2d6dcbb8f33eb83ade1b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -224,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.
-  'feed_revision': '4f87f10f5d5a589dbfd10815222d5d37fe267f06',
+  'feed_revision': 'f4372718f4cabd57efeab02f5726ed147f29768f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -280,11 +280,11 @@
   # 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': '1cd83ffbdd5d5f870f57398768555d3d97068177',
+  'dawn_revision': '421584173cae282ee496291b1cb08eebcad71be7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'b71a08181bcb67b75f9b4f44fa92237816ade534',
+  'quiche_revision': 'f35ea98e9c0aae429b91fac10a2cc40fff84c938',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -664,11 +664,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/android_sdk/androidx_browser/src': {
-      'url': Var('chromium_git') + '/external/gob/android/platform/frameworks/support/browser.git' + '@' + 'aeeea8bd0a6703bc4a148e9bcd6998553def74ab',
-      'condition': 'checkout_android',
-  },
-
   'src/third_party/android_sdk/public': {
       'packages': [
           {
@@ -814,7 +809,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6454258063cd52c8667a82668a537cb52de4a161',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '863bf72e6d5da911279b064e499e3d5d64685e59',
       'condition': 'checkout_linux',
   },
 
@@ -839,7 +834,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'aa2db565b32b0993a834348932c69424993e3f06',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a110bf60c043d93a23c105215f000b88a2825c49',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1212,7 +1207,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '887948c03303365c7466f047e93e634bd1ca6ae2',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0a8f3c09ad39006cd3066668e255edd47ea2f80b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1380,7 +1375,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b6685420c42a6b0903e2d4e9e5a270261d30a52d',
+    Var('webrtc_git') + '/src.git' + '@' + '3cc2f70bc33c3bfba6732d896e8c25a861d9b8b7',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1421,7 +1416,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@97c6c359d43f7b1c926208166ae4a4029d8b3bd8',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1e32e832b77e6b9fa3137d360cf011096bbd6af8',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index fad37d5..82166d3 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -30,6 +30,12 @@
   }
 }
 
+generate_jni("common_jni_headers") {
+  sources = [
+    "java/src/org/chromium/android_webview/common/AwResource.java",
+  ]
+}
+
 generate_jni("native_jni") {
   sources = [
     "apk/java/src/com/android/webview/chromium/WebViewApkApplication.java",
@@ -869,24 +875,25 @@
     "java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java",
     "java/src/org/chromium/android_webview/permission/AwPermissionRequest.java",
     "java/src/org/chromium/android_webview/policy/AwPolicyProvider.java",
+    "java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java",
     "java/src/org/chromium/android_webview/ui/util/UnuploadedFilesStateLoader.java",
     "java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java",
     "java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java",
   ]
   deps = [
-    ":android_webview_commandline_java",
-    ":android_webview_crash_info_java",
-    ":android_webview_platform_services_java",
     ":android_webview_services_java",
-    ":android_webview_variations_utils_java",
-    ":resources",
-    ":strings_grd",
-    ":system_webview_manifest",
+    ":common_commandline_java",
+    ":common_crash_info_java",
 
     # This includes AwResource, which may be called via JNI. We're including it
     # here because there's currently no good way to enforce that it gets included
     # when it's depended on via JNI.
-    "//android_webview/common:common_java",
+    ":common_java",
+    ":common_platform_services_java",
+    ":common_variations_utils_java",
+    ":resources",
+    ":strings_grd",
+    ":system_webview_manifest",
     "//base:base_java",
     "//components/autofill/android:autofill_java",
     "//components/autofill/android:provider_java",
@@ -930,8 +937,8 @@
   min_sdk_version = 21
 }
 
-android_library("android_webview_variations_utils_java") {
-  java_files = [ "java/src/org/chromium/android_webview/VariationsUtils.java" ]
+android_library("common_variations_utils_java") {
+  java_files = [ "java/src/org/chromium/android_webview/common/variations/VariationsUtils.java" ]
   deps = [
     "//android_webview/proto:aw_variations_seed_proto_java",
     "//base:base_java",
@@ -1019,24 +1026,48 @@
   jar_excluded_patterns = [ "*/PlatformServiceBridgeImpl.class" ]
 }
 
+android_library("common_platform_services_java") {
+  java_files = [
+    "java/src/org/chromium/android_webview/common/PlatformServiceBridge.java",
+    "java/src/org/chromium/android_webview/common/PlatformServiceBridgeImpl.java",
+  ]
+
+  deps = [
+    "//base:base_java",
+    "//third_party/android_deps:com_android_support_support_annotations_java",
+  ]
+
+  # The appropriate .class file will be loaded via a dependency to a library
+  # like :platform_service_bridge_upstream_implementation_java below. We only include the
+  # .java file because other files in the target depend on it.
+  jar_excluded_patterns = [ "*/PlatformServiceBridgeImpl.class" ]
+}
+
 # This target compiles the implementation of PlatformServiceBridge for AOSP targets.
 android_library("platform_service_bridge_upstream_implementation_java") {
-  java_files =
-      [ "java/src/org/chromium/android_webview/PlatformServiceBridgeImpl.java" ]
+  java_files = [ "java/src/org/chromium/android_webview/common/PlatformServiceBridgeImpl.java" ]
   deps = [
-    ":android_webview_platform_services_java",
+    ":common_platform_services_java",
   ]
 }
 
-android_library("android_webview_crash_info_java") {
+android_library("common_crash_info_java") {
   java_files =
-      [ "java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java" ]
+      [ "java/src/org/chromium/android_webview/common/crash/CrashInfo.java" ]
 
   deps = [
     "//base:base_java",
   ]
 }
 
+android_library("common_java") {
+  java_files =
+      [ "java/src/org/chromium/android_webview/common/AwResource.java" ]
+  deps = [
+    "//base:base_java",
+  ]
+}
+
 android_library("android_webview_commandline_java") {
   java_files = [
     "java/src/org/chromium/android_webview/command_line/CommandLineUtil.java",
@@ -1047,6 +1078,15 @@
   ]
 }
 
+android_library("common_commandline_java") {
+  java_files =
+      [ "java/src/org/chromium/android_webview/common/CommandLineUtil.java" ]
+
+  deps = [
+    "//base:base_java",
+  ]
+}
+
 # Keep WebView's services separate from other WebView code to keep their deps clean
 # (and make them easy to move).
 android_library("android_webview_services_java") {
@@ -1059,10 +1099,10 @@
     "java/src/org/chromium/android_webview/services/VariationsSeedServer.java",
   ]
   deps = [
-    ":android_webview_commandline_java",
-    ":android_webview_crash_info_java",
-    ":android_webview_platform_services_java",
-    ":android_webview_variations_utils_java",
+    ":common_commandline_java",
+    ":common_crash_info_java",
+    ":common_platform_services_java",
+    ":common_variations_utils_java",
     ":system_webview_manifest",
     "//android_webview/apk:apk_java",
     "//base:base_java",
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index a130678..2daa630 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -31,8 +31,8 @@
     "java/src/com/android/webview/chromium/WebViewApkApplication.java",
   ]
   deps = [
-    "//android_webview:android_webview_commandline_java",
     "//android_webview:android_webview_locale_config_java",
+    "//android_webview:common_commandline_java",
     "//base:base_java",
     "//components/embedder_support/android:application_java",
     "//ui/android:ui_java",
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
index f6cd74f..da9af528 100644
--- a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
+++ b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
@@ -8,7 +8,7 @@
 import android.content.Context;
 
 import org.chromium.android_webview.AwLocaleConfig;
-import org.chromium.android_webview.command_line.CommandLineUtil;
+import org.chromium.android_webview.common.CommandLineUtil;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.PathUtils;
 import org.chromium.base.annotations.JNINamespace;
diff --git a/android_webview/common/BUILD.gn b/android_webview/common/BUILD.gn
index 1619d3c..d44a0772 100644
--- a/android_webview/common/BUILD.gn
+++ b/android_webview/common/BUILD.gn
@@ -5,12 +5,6 @@
 import("//build/config/android/rules.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
-generate_jni("native_jni") {
-  sources = [
-    "../java/src/org/chromium/android_webview/common/AwResource.java",
-  ]
-}
-
 mojom("common_mojom") {
   sources = [
     "js_java_interaction/interfaces.mojom",
@@ -52,7 +46,7 @@
     "url_constants.h",
   ]
   deps = [
-    ":native_jni",
+    "//android_webview:common_jni_headers",
     "//base",
     "//components/cdm/common",
     "//components/crash/content/app",
@@ -73,11 +67,3 @@
     "//url",
   ]
 }
-
-android_library("common_java") {
-  java_files =
-      [ "../java/src/org/chromium/android_webview/common/AwResource.java" ]
-  deps = [
-    "//base:base_java",
-  ]
-}
diff --git a/android_webview/common/DEPS b/android_webview/common/DEPS
index d5b65ca7..686085e 100644
--- a/android_webview/common/DEPS
+++ b/android_webview/common/DEPS
@@ -1,7 +1,7 @@
 include_rules = [
   "-android_webview",
   "+android_webview/common",
-  "+android_webview/native_jni",
+  "+android_webview/common_jni_headers",
   "+components/cdm/common",
   "+components/printing/common",
   "+media/base/android",
diff --git a/android_webview/common/aw_resource.cc b/android_webview/common/aw_resource.cc
index 6033ec1..e94fca77 100644
--- a/android_webview/common/aw_resource.cc
+++ b/android_webview/common/aw_resource.cc
@@ -4,7 +4,7 @@
 
 #include "android_webview/common/aw_resource.h"
 
-#include "android_webview/common/native_jni/AwResource_jni.h"
+#include "android_webview/common_jni_headers/AwResource_jni.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
diff --git a/android_webview/docs/commandline-flags.md b/android_webview/docs/commandline-flags.md
index a325aac..24039c9 100644
--- a/android_webview/docs/commandline-flags.md
+++ b/android_webview/docs/commandline-flags.md
@@ -103,4 +103,4 @@
 
 ## Implementation
 
-See [CommandLineUtil.java](https://cs.chromium.org/chromium/src/android_webview/java/src/org/chromium/android_webview/command_line/CommandLineUtil.java).
+See [CommandLineUtil.java](https://cs.chromium.org/chromium/src/android_webview/java/src/org/chromium/android_webview/common/CommandLineUtil.java).
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index 1b1ad80..d4882c7 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -9,12 +9,11 @@
 
 android_library("glue") {
   deps = [
-    "//android_webview:android_webview_commandline_java",
     "//android_webview:android_webview_java",
     "//android_webview:android_webview_locale_config_java",
-    "//android_webview:android_webview_platform_services_java",
+    "//android_webview:common_commandline_java",
+    "//android_webview:common_java",
     "//android_webview:system_webview_manifest",
-    "//android_webview/common:common_java",
     "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
     "//android_webview/support_library/callback:callback_java",
     "//base:base_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index bbeee392..cb2adb7 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -37,7 +37,7 @@
 import org.chromium.android_webview.AwSwitches;
 import org.chromium.android_webview.ResourcesContextWrapperFactory;
 import org.chromium.android_webview.WebViewChromiumRunQueue;
-import org.chromium.android_webview.command_line.CommandLineUtil;
+import org.chromium.android_webview.common.CommandLineUtil;
 import org.chromium.base.BuildInfo;
 import org.chromium.base.BundleUtils;
 import org.chromium.base.CommandLine;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
index 50fe5e5..16b430b2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index f7578aa3..f23e734 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -14,7 +14,8 @@
 import android.os.RemoteException;
 import android.os.StrictMode;
 
-import org.chromium.android_webview.command_line.CommandLineUtil;
+import org.chromium.android_webview.common.CommandLineUtil;
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.android_webview.policy.AwPolicyProvider;
 import org.chromium.android_webview.services.CrashReceiverService;
 import org.chromium.android_webview.services.ICrashReceiverService;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index 1d56b491b..58fc51c2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.net.Uri;
 
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwMetricsLogUploader.java b/android_webview/java/src/org/chromium/android_webview/AwMetricsLogUploader.java
index e5e7690..43122da 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwMetricsLogUploader.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwMetricsLogUploader.java
@@ -4,6 +4,7 @@
 
 package org.chromium.android_webview;
 
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
index bb1f9d3..b3ef9d2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java
@@ -10,6 +10,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.base.Callback;
 import org.chromium.base.CommandLine;
 import org.chromium.base.Log;
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
index 904a2604..c84ee42 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/VariationsSeedLoader.java
@@ -14,6 +14,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.services.IVariationsSeedServer;
 import org.chromium.android_webview.services.VariationsSeedServer;
 import org.chromium.base.ContextUtils;
diff --git a/android_webview/java/src/org/chromium/android_webview/common/CommandLineUtil.java b/android_webview/java/src/org/chromium/android_webview/common/CommandLineUtil.java
new file mode 100644
index 0000000..4aef44f1
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/common/CommandLineUtil.java
@@ -0,0 +1,58 @@
+// 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.
+
+package org.chromium.android_webview.common;
+
+import android.os.StrictMode;
+
+import org.chromium.base.BuildInfo;
+import org.chromium.base.CommandLine;
+
+/**
+ * Utility class for WebView's CommandLine - this is compiled into a separate target that can be
+ * reached from WebView's separate minidump-uploading Services.
+ */
+public class CommandLineUtil {
+    protected CommandLineUtil() {}
+
+    public static final String WEBVIEW_COMMAND_LINE_FILE = "/data/local/tmp/webview-command-line";
+
+    // same switch as kEnableCrashReporterForTesting in //base/base_switches.h
+    public static final String CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH =
+            "enable-crash-reporter-for-testing";
+
+    private static final String COMMAND_LINE_UTIL_INTERNAL =
+            "org.chromium.android_webview.command_line.CommandLineUtilInternal";
+
+    private static CommandLineUtil sInstance;
+
+    private static CommandLineUtil getInstance() {
+        if (sInstance != null) return sInstance;
+        try {
+            sInstance = (CommandLineUtil) Class.forName(COMMAND_LINE_UTIL_INTERNAL).newInstance();
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                | IllegalArgumentException e) {
+            sInstance = new CommandLineUtil();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Initialize the CommandLine for WebView - this should be initialized on the same thread where
+     * we subsequently access CommandLine.
+     */
+    public static void initCommandLine() {
+        if (BuildInfo.isDebugAndroid()) {
+            // Suppress the StrictMode violation as this codepath is only hit on debuggable builds.
+            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            CommandLine.initFromFile(CommandLineUtil.WEBVIEW_COMMAND_LINE_FILE);
+            StrictMode.setThreadPolicy(oldPolicy);
+        } else {
+            CommandLine.init(null);
+        }
+        getInstance().initCommandLineInternal(CommandLine.getInstance());
+    }
+
+    protected void initCommandLineInternal(CommandLine commandLine) {}
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
new file mode 100644
index 0000000..b3195bb
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridge.java
@@ -0,0 +1,93 @@
+// 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.
+
+package org.chromium.android_webview.common;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.annotation.NonNull;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
+
+/**
+ * This class manages platform-specific services. (i.e. Google Services) The platform
+ * should extend this class and use this base class to fetch their specialized version.
+ */
+public abstract class PlatformServiceBridge {
+    private static final String TAG = "PlatformServiceBrid-";
+
+    private static PlatformServiceBridge sInstance;
+    private static final Object sInstanceLock = new Object();
+
+    private static HandlerThread sHandlerThread;
+    private static Handler sHandler;
+    private static final Object sHandlerLock = new Object();
+
+    protected PlatformServiceBridge() {}
+
+    @SuppressWarnings("unused")
+    public static PlatformServiceBridge getInstance() {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                // Load an instance of PlatformServiceBridgeImpl. Because this can change
+                // depending on the GN configuration, this may not be the PlatformServiceBridgeImpl
+                // defined upstream.
+                sInstance = new PlatformServiceBridgeImpl();
+            }
+            return sInstance;
+        }
+    }
+
+    // Provide a mocked PlatformServiceBridge for testing.
+    public static void injectInstance(PlatformServiceBridge testBridge) {
+        // Although reference assignments are atomic, we still wouldn't want to assign it in the
+        // middle of getInstance().
+        synchronized (sInstanceLock) {
+            sInstance = testBridge;
+        }
+    }
+
+    // Return a handler appropriate for executing blocking Platform Service tasks.
+    public static Handler getHandler() {
+        synchronized (sHandlerLock) {
+            if (sHandler == null) {
+                sHandlerThread = new HandlerThread("PlatformServiceBridgeHandlerThread");
+                sHandlerThread.start();
+                sHandler = new Handler(sHandlerThread.getLooper());
+            }
+        }
+        return sHandler;
+    }
+
+    // Can WebView use Google Play Services (a.k.a. GMS)?
+    public boolean canUseGms() {
+        return false;
+    }
+
+    // Overriding implementations may call "callback" asynchronously, on any thread.
+    public void querySafeBrowsingUserConsent(@NonNull final Callback<Boolean> callback) {
+        // User opt-in preference depends on a SafetyNet API.
+    }
+
+    // Overriding implementations may call "callback" asynchronously. For simplicity (and not
+    // because of any technical limitation) we require that "queryMetricsSetting" and "callback"
+    // both get called on WebView's UI thread.
+    public void queryMetricsSetting(Callback<Boolean> callback) {
+        ThreadUtils.assertOnUiThread();
+        callback.onResult(false);
+    }
+
+    public void setSafeBrowsingHandler() {
+        // We don't have this specialized service.
+    }
+
+    public void warmUpSafeBrowsing(Context context, @NonNull final Callback<Boolean> callback) {
+        callback.onResult(false);
+    }
+
+    // Takes an uncompressed, serialized UMA proto and logs it via a platform-specific mechanism.
+    public void logMetrics(byte[] data) {}
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridgeImpl.java b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridgeImpl.java
new file mode 100644
index 0000000..c5c6824
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/common/PlatformServiceBridgeImpl.java
@@ -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.
+
+package org.chromium.android_webview.common;
+
+/**
+ * Instantiable version of {@link PlatformServiceBridge}, don't add anything to this class!
+ * Downstream targets may provide a different implementation. In GN, we specify that
+ * {@link PlatformServiceBridge} is compiled separately from its implementation; other
+ * projects may specify a different PlatformServiceBridgeImpl via GN.
+ */
+public class PlatformServiceBridgeImpl extends PlatformServiceBridge {}
diff --git a/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java
new file mode 100644
index 0000000..24ef232
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java
@@ -0,0 +1,154 @@
+// 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.android_webview.common.crash;
+
+import android.support.annotation.NonNull;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.InvalidObjectException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class that bundles various information about a crash.
+ */
+public class CrashInfo {
+    /**
+     * Crash file report/minidump upload status.
+     */
+    public static enum UploadState {
+        SKIPPED,
+        PENDING,
+        PENDING_USER_REQUESTED,
+        UPLOADED,
+    }
+
+    /**
+     * Upload state for the crash.
+     */
+    public UploadState uploadState;
+
+    /**
+     * ID for locally stored data that may or may not be uploaded.
+     */
+    @NonNull
+    public String localId;
+    /**
+     * The time the data was captured. This is useful if the data is stored locally when
+     * captured and uploaded at a later time.
+     */
+    public long captureTime = -1;
+    /**
+     * The application package name where a crash happened.
+     */
+    public String packageName;
+    /**
+     * List of variation/experiment keys activated when the crash happened.
+     */
+    public List<String> variations;
+
+    /**
+     * ID the crash collecting servers responded with after the crash report is uploaded.
+     * Only valid when |uploadState| == Uploaded.
+     */
+    public String uploadId;
+    /**
+     * The time when the crash report is uploaded.
+     * Only valid when |uploadState| == Uploaded.
+     */
+    public long uploadTime = -1;
+
+    private static final String CRASH_LOCAL_ID_KEY = "crash-local-id";
+    private static final String CRASH_CAPTURE_TIME_KEY = "crash-capture-time";
+    private static final String CRASH_PACKAGE_NAME_KEY = "app-package-name";
+    private static final String CRASH_VARIATIONS_KEY = "variations";
+    private static final String CRASH_UPLOAD_ID_KEY = "crash-upload-id";
+    private static final String CRASH_UPLOAD_TIME_KEY = "crash-upload-time";
+
+    /**
+     * Create a {@code CrashInfo} object with a non-null localId.
+     * This Enforces that localId can never be null, hence eliminate the need for null check.
+     * localId should always has a non-null value because it used to join crash info from
+     * different sources together.
+     */
+    public CrashInfo(@NonNull String localId) {
+        this.localId = localId;
+    }
+
+    /**
+     * Serialize {@code CrashInfo} object into a JSON object string.
+     *
+     * @return serialized string for the object.
+     */
+    public String serializeToJson() {
+        try {
+            JSONObject jsonObj = new JSONObject();
+            jsonObj.put(CRASH_LOCAL_ID_KEY, localId);
+            if (captureTime != -1) {
+                jsonObj.put(CRASH_CAPTURE_TIME_KEY, captureTime);
+            }
+            if (packageName != null) {
+                jsonObj.put(CRASH_PACKAGE_NAME_KEY, packageName);
+            }
+            if (variations != null && !variations.isEmpty()) {
+                jsonObj.put(CRASH_VARIATIONS_KEY, new JSONArray(variations));
+            }
+            if (uploadId != null) {
+                jsonObj.put(CRASH_UPLOAD_ID_KEY, uploadId);
+            }
+            if (uploadTime != -1) {
+                jsonObj.put(CRASH_UPLOAD_TIME_KEY, uploadTime);
+            }
+            return jsonObj.toString();
+        } catch (JSONException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Load {@code CrashInfo} from a JSON string.
+     *
+     * @param jsonString JSON string to load {@code CrashInfo} from.
+     * @return {@code CrashInfo} loaded from the serialized JSON object string.
+     * @throws JSONException if it's a malformatted JSON string.
+     * @throws InvalidObjectException if the JSON Object doesn't have "crash-local-id" field.
+     */
+    public static CrashInfo readFromJsonString(String jsonString)
+            throws JSONException, InvalidObjectException {
+        JSONObject jsonObj = new JSONObject(jsonString);
+        if (!jsonObj.has(CRASH_LOCAL_ID_KEY)) {
+            throw new InvalidObjectException(
+                    "JSON Object doesn't have the field " + CRASH_LOCAL_ID_KEY);
+        }
+        CrashInfo crashInfo = new CrashInfo(jsonObj.getString(CRASH_LOCAL_ID_KEY));
+
+        if (jsonObj.has(CRASH_CAPTURE_TIME_KEY)) {
+            crashInfo.captureTime = jsonObj.getLong(CRASH_CAPTURE_TIME_KEY);
+        }
+        if (jsonObj.has(CRASH_PACKAGE_NAME_KEY)) {
+            crashInfo.packageName = jsonObj.getString(CRASH_PACKAGE_NAME_KEY);
+        }
+        if (jsonObj.has(CRASH_VARIATIONS_KEY)) {
+            JSONArray variationsJSONArr = jsonObj.getJSONArray(CRASH_VARIATIONS_KEY);
+            if (variationsJSONArr != null) {
+                crashInfo.variations = new ArrayList<>();
+                for (int i = 0; i < variationsJSONArr.length(); i++) {
+                    crashInfo.variations.add(variationsJSONArr.getString(i));
+                }
+            }
+        }
+        if (jsonObj.has(CRASH_UPLOAD_ID_KEY)) {
+            crashInfo.uploadId = jsonObj.getString(CRASH_UPLOAD_ID_KEY);
+        }
+        if (jsonObj.has(CRASH_UPLOAD_TIME_KEY)) {
+            crashInfo.uploadTime = jsonObj.getLong(CRASH_UPLOAD_TIME_KEY);
+        }
+
+        return crashInfo;
+    }
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java b/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java
similarity index 85%
rename from android_webview/java/src/org/chromium/android_webview/VariationsUtils.java
rename to android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.java
index 74f2c932..a6e3c80 100644
--- a/android_webview/java/src/org/chromium/android_webview/VariationsUtils.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/variations/VariationsUtils.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.android_webview;
+package org.chromium.android_webview.common.variations;
 
 import android.support.annotation.Nullable;
 
@@ -57,8 +57,8 @@
         File oldSeedFile = getSeedFile();
         File newSeedFile = getNewSeedFile();
         if (!newSeedFile.renameTo(oldSeedFile)) {
-            Log.e(TAG, "Failed to replace old seed " + oldSeedFile +
-                    " with new seed " + newSeedFile);
+            Log.e(TAG,
+                    "Failed to replace old seed " + oldSeedFile + " with new seed " + newSeedFile);
         }
     }
 
@@ -103,11 +103,9 @@
                 return null;
             }
 
-            if (!proto.hasSignature()        ||
-                !proto.hasCountry()          ||
-                !proto.hasDate()             ||
-                !proto.hasIsGzipCompressed() ||
-                !proto.hasSeedData()) return null;
+            if (!proto.hasSignature() || !proto.hasCountry() || !proto.hasDate()
+                    || !proto.hasIsGzipCompressed() || !proto.hasSeedData())
+                return null;
 
             SeedInfo info = new SeedInfo();
             info.signature = proto.getSignature();
@@ -136,12 +134,12 @@
     public static boolean writeSeed(FileOutputStream out, SeedInfo info) {
         try {
             AwVariationsSeed proto = AwVariationsSeed.newBuilder()
-                .setSignature(info.signature)
-                .setCountry(info.country)
-                .setDate(info.date)
-                .setIsGzipCompressed(info.isGzipCompressed)
-                .setSeedData(ByteString.copyFrom(info.seedData))
-                .build();
+                                             .setSignature(info.signature)
+                                             .setCountry(info.country)
+                                             .setDate(info.date)
+                                             .setIsGzipCompressed(info.isGzipCompressed)
+                                             .setSeedData(ByteString.copyFrom(info.seedData))
+                                             .build();
             proto.writeTo(out);
             return true;
         } catch (IOException e) {
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java
index 64e8fcb..f941627 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java
@@ -7,8 +7,8 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 
-import org.chromium.android_webview.PlatformServiceBridge;
-import org.chromium.android_webview.command_line.CommandLineUtil;
+import org.chromium.android_webview.common.CommandLineUtil;
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
diff --git a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
index a7f076a..b64c830 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/AwVariationsSeedFetcher.java
@@ -13,7 +13,7 @@
 import android.content.Context;
 import android.os.Build;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.compat.ApiHelperForN;
diff --git a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
index b2c5483c..dbfb98802 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/CrashReceiverService.java
@@ -12,7 +12,7 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
-import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
+import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
diff --git a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
index 24ea238..c427cdb 100644
--- a/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
+++ b/android_webview/java/src/org/chromium/android_webview/services/VariationsSeedHolder.java
@@ -9,7 +9,7 @@
 import android.os.ParcelFileDescriptor;
 import android.support.annotation.VisibleForTesting;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.base.Log;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
 
diff --git a/android_webview/java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java b/android_webview/java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java
index 800209b9..480fef1 100644
--- a/android_webview/java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/ui/util/CrashInfoLoader.java
@@ -1,16 +1,11 @@
 // 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.android_webview.ui.util;
 
-import android.support.annotation.NonNull;
+import org.chromium.android_webview.common.crash.CrashInfo;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.InvalidObjectException;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -18,145 +13,6 @@
  */
 public abstract class CrashInfoLoader {
     /**
-     * Crash file report/minidump upload status.
-     */
-    public static enum UploadState {
-        SKIPPED,
-        PENDING,
-        PENDING_USER_REQUESTED,
-        UPLOADED,
-    }
-
-    /**
-     * A class that bundles various information about a crash.
-     */
-    public static class CrashInfo {
-        /**
-         * Upload state for the crash.
-         */
-        public UploadState uploadState;
-
-        /**
-         * ID for locally stored data that may or may not be uploaded.
-         */
-        @NonNull
-        public String localId;
-        /**
-         * The time the data was captured. This is useful if the data is stored locally when
-         * captured and uploaded at a later time.
-         */
-        public long captureTime = -1;
-        /**
-         * The application package name where a crash happened.
-         */
-        public String packageName;
-        /**
-         * List of variation/experiment keys activated when the crash happened.
-         */
-        public List<String> variations;
-
-        /**
-         * ID the crash collecting servers responded with after the crash report is uploaded.
-         * Only valid when |uploadState| == Uploaded.
-         */
-        public String uploadId;
-        /**
-         * The time when the crash report is uploaded.
-         * Only valid when |uploadState| == Uploaded.
-         */
-        public long uploadTime = -1;
-
-        private static final String CRASH_LOCAL_ID_KEY = "crash-local-id";
-        private static final String CRASH_CAPTURE_TIME_KEY = "crash-capture-time";
-        private static final String CRASH_PACKAGE_NAME_KEY = "app-package-name";
-        private static final String CRASH_VARIATIONS_KEY = "variations";
-        private static final String CRASH_UPLOAD_ID_KEY = "crash-upload-id";
-        private static final String CRASH_UPLOAD_TIME_KEY = "crash-upload-time";
-
-        /**
-         * Create a {@code CrashInfo} object with a non-null localId.
-         * This Enforces that localId can never be null, hence eliminate the need for null check.
-         * localId should always has a non-null value because it used to join crash info from
-         * different sources together.
-         */
-        public CrashInfo(@NonNull String localId) {
-            this.localId = localId;
-        }
-
-        /**
-         * Serialize {@code CrashInfo} object into a JSON object string.
-         *
-         * @return serialized string for the object.
-         */
-        public String serializeToJson() {
-            try {
-                JSONObject jsonObj = new JSONObject();
-                jsonObj.put(CRASH_LOCAL_ID_KEY, localId);
-                if (captureTime != -1) {
-                    jsonObj.put(CRASH_CAPTURE_TIME_KEY, captureTime);
-                }
-                if (packageName != null) {
-                    jsonObj.put(CRASH_PACKAGE_NAME_KEY, packageName);
-                }
-                if (variations != null && !variations.isEmpty()) {
-                    jsonObj.put(CRASH_VARIATIONS_KEY, new JSONArray(variations));
-                }
-                if (uploadId != null) {
-                    jsonObj.put(CRASH_UPLOAD_ID_KEY, uploadId);
-                }
-                if (uploadTime != -1) {
-                    jsonObj.put(CRASH_UPLOAD_TIME_KEY, uploadTime);
-                }
-                return jsonObj.toString();
-            } catch (JSONException e) {
-                return null;
-            }
-        }
-
-        /**
-         * Load {@code CrashInfo} from a JSON string.
-         *
-         * @param jsonString JSON string to load {@code CrashInfo} from.
-         * @return {@code CrashInfo} loaded from the serialized JSON object string.
-         * @throws JSONException if it's a malformatted JSON string.
-         * @throws InvalidObjectException if the JSON Object doesn't have "crash-local-id" field.
-         */
-        public static CrashInfo readFromJsonString(String jsonString)
-                throws JSONException, InvalidObjectException {
-            JSONObject jsonObj = new JSONObject(jsonString);
-            if (!jsonObj.has(CRASH_LOCAL_ID_KEY)) {
-                throw new InvalidObjectException(
-                        "JSON Object doesn't have the field " + CRASH_LOCAL_ID_KEY);
-            }
-            CrashInfo crashInfo = new CrashInfo(jsonObj.getString(CRASH_LOCAL_ID_KEY));
-
-            if (jsonObj.has(CRASH_CAPTURE_TIME_KEY)) {
-                crashInfo.captureTime = jsonObj.getLong(CRASH_CAPTURE_TIME_KEY);
-            }
-            if (jsonObj.has(CRASH_PACKAGE_NAME_KEY)) {
-                crashInfo.packageName = jsonObj.getString(CRASH_PACKAGE_NAME_KEY);
-            }
-            if (jsonObj.has(CRASH_VARIATIONS_KEY)) {
-                JSONArray variationsJSONArr = jsonObj.getJSONArray(CRASH_VARIATIONS_KEY);
-                if (variationsJSONArr != null) {
-                    crashInfo.variations = new ArrayList<>();
-                    for (int i = 0; i < variationsJSONArr.length(); i++) {
-                        crashInfo.variations.add(variationsJSONArr.getString(i));
-                    }
-                }
-            }
-            if (jsonObj.has(CRASH_UPLOAD_ID_KEY)) {
-                crashInfo.uploadId = jsonObj.getString(CRASH_UPLOAD_ID_KEY);
-            }
-            if (jsonObj.has(CRASH_UPLOAD_TIME_KEY)) {
-                crashInfo.uploadTime = jsonObj.getLong(CRASH_UPLOAD_TIME_KEY);
-            }
-
-            return crashInfo;
-        }
-    }
-
-    /**
      * Loads all crashes info from source.
      *
      * @return list of crashes info.
diff --git a/android_webview/java/src/org/chromium/android_webview/ui/util/UnuploadedFilesStateLoader.java b/android_webview/java/src/org/chromium/android_webview/ui/util/UnuploadedFilesStateLoader.java
index 4f4dc3d7..69816a01 100644
--- a/android_webview/java/src/org/chromium/android_webview/ui/util/UnuploadedFilesStateLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/ui/util/UnuploadedFilesStateLoader.java
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 package org.chromium.android_webview.ui.util;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
+import org.chromium.android_webview.common.crash.CrashInfo.UploadState;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 
 import java.io.File;
diff --git a/android_webview/java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java b/android_webview/java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java
index 54095c5..e9ac867 100644
--- a/android_webview/java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java
+++ b/android_webview/java/src/org/chromium/android_webview/ui/util/UploadedCrashesInfoLoader.java
@@ -1,8 +1,11 @@
 // 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.android_webview.ui.util;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
@@ -62,7 +65,7 @@
         }
 
         CrashInfo info = new CrashInfo(components[2]);
-        info.uploadState = UploadState.UPLOADED;
+        info.uploadState = CrashInfo.UploadState.UPLOADED;
         try {
             info.uploadTime = Long.parseLong(components[0]);
         } catch (NumberFormatException e) {
diff --git a/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java b/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java
index 3bac1d8e..2f0f6714 100644
--- a/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java
+++ b/android_webview/java/src/org/chromium/android_webview/ui/util/WebViewCrashLogParser.java
@@ -1,10 +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.
+
 package org.chromium.android_webview.ui.util;
 
 import org.json.JSONException;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.base.Log;
 
 import java.io.File;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
index d0798836e..762dffd 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetcherTest.java
@@ -21,7 +21,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.services.AwVariationsSeedFetcher;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.base.ContextUtils;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
index 931dbac..becc222 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java
@@ -15,7 +15,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.services.VariationsSeedHolder;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.base.test.util.CallbackHelper;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
index 4733a80..2c2634f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java
@@ -18,7 +18,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.VariationsSeedLoader;
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.test.services.MockVariationsSeedServer;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.base.ContextUtils;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsUtilsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/common/variations/VariationsUtilsTest.java
similarity index 76%
rename from android_webview/javatests/src/org/chromium/android_webview/test/VariationsUtilsTest.java
rename to android_webview/javatests/src/org/chromium/android_webview/test/common/variations/VariationsUtilsTest.java
index f6089de..47182ab2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VariationsUtilsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/common/variations/VariationsUtilsTest.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.android_webview.test;
+package org.chromium.android_webview.test.common.variations;
 
 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS;
 
@@ -14,8 +14,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.android_webview.proto.AwVariationsSeedOuterClass.AwVariationsSeed;
+import org.chromium.android_webview.test.AwJUnit4ClassRunner;
+import org.chromium.android_webview.test.OnlyRunIn;
 import org.chromium.android_webview.test.util.VariationsTestUtils;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
 
@@ -57,10 +59,10 @@
                 stream = new FileOutputStream(file);
                 SeedInfo info = VariationsTestUtils.createMockSeed();
                 AwVariationsSeed proto = AwVariationsSeed.newBuilder()
-                    .setSignature(info.signature)
-                    .setCountry(info.country)
-                    .setDate(info.date)
-                    .build();
+                                                 .setSignature(info.signature)
+                                                 .setCountry(info.country)
+                                                 .setDate(info.date)
+                                                 .build();
                 proto.writeTo(stream);
 
                 Assert.assertNull("Seed with missing fields should've failed to load.",
@@ -80,12 +82,12 @@
         // Create a complete, serialized seed.
         SeedInfo info = VariationsTestUtils.createMockSeed();
         AwVariationsSeed proto = AwVariationsSeed.newBuilder()
-            .setSignature(info.signature)
-            .setCountry(info.country)
-            .setDate(info.date)
-            .setIsGzipCompressed(info.isGzipCompressed)
-            .setSeedData(ByteString.copyFrom(info.seedData))
-            .build();
+                                         .setSignature(info.signature)
+                                         .setCountry(info.country)
+                                         .setDate(info.date)
+                                         .setIsGzipCompressed(info.isGzipCompressed)
+                                         .setSeedData(ByteString.copyFrom(info.seedData))
+                                         .build();
         byte[] protoBytes = proto.toByteArray();
 
         // Sanity check: protoBytes is at least as long as the seedData field.
@@ -106,8 +108,9 @@
                 }
 
                 // Reading each truncated seed should fail.
-                Assert.assertNull("Seed truncated from " + protoBytes.length + " to " + offset +
-                        " bytes should've failed to load.", VariationsUtils.readSeedFile(file));
+                Assert.assertNull("Seed truncated from " + protoBytes.length + " to " + offset
+                                + " bytes should've failed to load.",
+                        VariationsUtils.readSeedFile(file));
             } finally {
                 if (file != null) file.delete();
             }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java
index 19bff37..4027c6c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java
@@ -16,10 +16,10 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.android_webview.services.CrashReceiverService;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.android_webview.test.OnlyRunIn;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
 
 import java.io.File;
 import java.io.FileInputStream;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java
index 5944242..1a2c7c5c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java
@@ -14,7 +14,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.android_webview.PlatformServiceBridge;
+import org.chromium.android_webview.common.PlatformServiceBridge;
 import org.chromium.android_webview.services.AwMinidumpUploaderDelegate;
 import org.chromium.android_webview.services.CrashReceiverService;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UnuploadedFilesStateLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UnuploadedFilesStateLoaderTest.java
index ac62392f9..33e0bf22 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UnuploadedFilesStateLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UnuploadedFilesStateLoaderTest.java
@@ -15,10 +15,10 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
+import org.chromium.android_webview.common.crash.CrashInfo.UploadState;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.android_webview.test.OnlyRunIn;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.UploadState;
 import org.chromium.android_webview.ui.util.UnuploadedFilesStateLoader;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UploadedCrashesInfoLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UploadedCrashesInfoLoaderTest.java
index c7b45c4..36b7d47 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UploadedCrashesInfoLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/UploadedCrashesInfoLoaderTest.java
@@ -14,10 +14,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
+import org.chromium.android_webview.common.crash.CrashInfo.UploadState;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.android_webview.test.OnlyRunIn;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.UploadState;
 import org.chromium.android_webview.ui.util.UploadedCrashesInfoLoader;
 
 import java.io.BufferedWriter;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java
index 08fc773..0a3e673e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ui/util/WebViewCrashLogParserTest.java
@@ -15,8 +15,8 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 
+import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
-import org.chromium.android_webview.ui.util.CrashInfoLoader.CrashInfo;
 import org.chromium.android_webview.ui.util.WebViewCrashLogParser;
 
 import java.io.File;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/VariationsTestUtils.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/VariationsTestUtils.java
index 3e57094..538cdf5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/VariationsTestUtils.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/VariationsTestUtils.java
@@ -6,7 +6,7 @@
 
 import org.junit.Assert;
 
-import org.chromium.android_webview.VariationsUtils;
+import org.chromium.android_webview.common.variations.VariationsUtils;
 import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
 
 import java.io.File;
diff --git a/android_webview/support_library/BUILD.gn b/android_webview/support_library/BUILD.gn
index 48f7933..f09fece 100644
--- a/android_webview/support_library/BUILD.gn
+++ b/android_webview/support_library/BUILD.gn
@@ -26,7 +26,6 @@
   ]
 
   deps = [
-    "//android_webview:android_webview_commandline_java",
     "//android_webview:android_webview_java",
     "//android_webview/glue:glue",
     "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
diff --git a/android_webview/support_library/callback/BUILD.gn b/android_webview/support_library/callback/BUILD.gn
index 214eeb0..b4886e5 100644
--- a/android_webview/support_library/callback/BUILD.gn
+++ b/android_webview/support_library/callback/BUILD.gn
@@ -13,7 +13,6 @@
   ]
 
   deps = [
-    "//android_webview:android_webview_commandline_java",
     "//android_webview:android_webview_java",
     "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
     "//base:base_java",
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index b35379b..0bb992a 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -48,10 +48,10 @@
     ":android_webview_apk_resources",
     "//android_webview:android_webview_java",
     "//android_webview:android_webview_locale_config_java",
+    "//android_webview:common_java",
     "//android_webview:locale_pak_assets",
     "//android_webview:platform_service_bridge_upstream_implementation_java",
     "//android_webview/apk:apk_java",
-    "//android_webview/common:common_java",
     "//base:base_java",
     "//base:base_java_test_support",
     "//components/heap_profiling:heap_profiling_java_test_support",
@@ -156,7 +156,6 @@
   min_sdk_version = 21
   deps = [
     "//android_webview:android_webview_java",
-    "//android_webview:android_webview_platform_services_java",
     "//android_webview:android_webview_services_java",
     "//android_webview:aw_variations_seed_server_aidl",
     "//android_webview/test/embedded_test_server:aw_net_java_test_support",
@@ -270,13 +269,13 @@
     "../javatests/src/org/chromium/android_webview/test/UserAgentTest.java",
     "../javatests/src/org/chromium/android_webview/test/VariationsSeedHolderTest.java",
     "../javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java",
-    "../javatests/src/org/chromium/android_webview/test/VariationsUtilsTest.java",
     "../javatests/src/org/chromium/android_webview/test/VisualStateTest.java",
     "../javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java",
     "../javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java",
     "../javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java",
     "../javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java",
     "../javatests/src/org/chromium/android_webview/test/WebViewWebVrTest.java",
+    "../javatests/src/org/chromium/android_webview/test/common/variations/VariationsUtilsTest.java",
     "../javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java",
     "../javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java",
     "../javatests/src/org/chromium/android_webview/test/services/MockVariationsSeedServer.java",
diff --git a/ash/media/media_notification_controller_impl.cc b/ash/media/media_notification_controller_impl.cc
index 85afaee..1d2a61a6 100644
--- a/ash/media/media_notification_controller_impl.cc
+++ b/ash/media/media_notification_controller_impl.cc
@@ -137,7 +137,10 @@
     media_session::mojom::AudioFocusRequestStatePtr session) {
   const std::string id = session->request_id->ToString();
 
-  if (base::Contains(notifications_, id))
+  // If we have an existing unfrozen item then this is a duplicate call and
+  // we should ignore it.
+  auto it = notifications_.find(id);
+  if (it != notifications_.end() && !it->second.frozen())
     return;
 
   media_session::mojom::MediaControllerPtr controller;
@@ -149,16 +152,29 @@
         mojo::MakeRequest(&controller), *session->request_id);
   }
 
-  notifications_.emplace(
-      std::piecewise_construct, std::forward_as_tuple(id),
-      std::forward_as_tuple(
-          this, id, session->source_name.value_or(std::string()),
-          std::move(controller), std::move(session->session_info)));
+  if (it != notifications_.end()) {
+    // If the notification was previously frozen then we should reset the
+    // controller because the mojo pipe would have been reset.
+    it->second.SetController(std::move(controller),
+                             std::move(session->session_info));
+  } else {
+    notifications_.emplace(
+        std::piecewise_construct, std::forward_as_tuple(id),
+        std::forward_as_tuple(
+            this, id, session->source_name.value_or(std::string()),
+            std::move(controller), std::move(session->session_info)));
+  }
 }
 
 void MediaNotificationControllerImpl::OnFocusLost(
     media_session::mojom::AudioFocusRequestStatePtr session) {
-  notifications_.erase(session->request_id->ToString());
+  auto it = notifications_.find(session->request_id->ToString());
+  if (it == notifications_.end())
+    return;
+
+  // If we lost focus then we should freeze the notification as it may regain
+  // focus after a second or so.
+  it->second.Freeze();
 }
 
 void MediaNotificationControllerImpl::ShowNotification(const std::string& id) {
@@ -192,6 +208,15 @@
   message_center::MessageCenter::Get()->RemoveNotification(id, false);
 }
 
+void MediaNotificationControllerImpl::RemoveItem(const std::string& id) {
+  notifications_.erase(id);
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+MediaNotificationControllerImpl::GetTaskRunner() const {
+  return task_runner_for_testing_;
+}
+
 std::unique_ptr<MediaNotificationContainerImpl>
 MediaNotificationControllerImpl::CreateMediaNotification(
     const message_center::Notification& notification) {
@@ -205,6 +230,11 @@
                                                           std::move(item));
 }
 
+bool MediaNotificationControllerImpl::HasItemForTesting(
+    const std::string& id) const {
+  return base::Contains(notifications_, id);
+}
+
 void MediaNotificationControllerImpl::RecordConcurrentNotificationCount() {
   UMA_HISTOGRAM_EXACT_LINEAR(
       kCountHistogramName,
diff --git a/ash/media/media_notification_controller_impl.h b/ash/media/media_notification_controller_impl.h
index bc6db4c3..826d803b 100644
--- a/ash/media/media_notification_controller_impl.h
+++ b/ash/media/media_notification_controller_impl.h
@@ -57,6 +57,8 @@
   // media_message_center::MediaNotificationController:
   void ShowNotification(const std::string& id) override;
   void HideNotification(const std::string& id) override;
+  void RemoveItem(const std::string& id) override;
+  scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
 
   std::unique_ptr<MediaNotificationContainerImpl> CreateMediaNotification(
       const message_center::Notification& notification);
@@ -67,6 +69,12 @@
     return &it->second;
   }
 
+  bool HasItemForTesting(const std::string& id) const;
+  void set_task_runner_for_testing(
+      scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing) {
+    task_runner_for_testing_ = task_runner_for_testing;
+  }
+
  private:
   // Called when we display a new media notification. It will record the
   // concurrent number of media notifications displayed.
@@ -82,6 +90,9 @@
   std::map<const std::string, media_message_center::MediaNotificationItem>
       notifications_;
 
+  // Tick clock used for testing.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing_;
+
   std::unique_ptr<MediaNotificationBlocker> blocker_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerImpl);
diff --git a/ash/media/media_notification_controller_impl_unittest.cc b/ash/media/media_notification_controller_impl_unittest.cc
index 204a56bc..73ee7432 100644
--- a/ash/media/media_notification_controller_impl_unittest.cc
+++ b/ash/media/media_notification_controller_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "base/unguessable_token.h"
 #include "components/media_message_center/media_notification_item.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
@@ -46,7 +47,10 @@
 
 class MediaNotificationControllerImplTest : public AshTestBase {
  public:
-  MediaNotificationControllerImplTest() = default;
+  MediaNotificationControllerImplTest()
+      : task_runner_(new base::TestMockTimeTaskRunner(
+            base::TestMockTimeTaskRunner::Type::kStandalone)) {}
+
   ~MediaNotificationControllerImplTest() override = default;
 
   // AshTestBase
@@ -55,6 +59,9 @@
         features::kMediaSessionNotification);
 
     AshTestBase::SetUp();
+
+    Shell::Get()->media_notification_controller()->set_task_runner_for_testing(
+        task_runner_);
   }
 
   void ExpectNotificationCount(unsigned count) {
@@ -94,7 +101,13 @@
     Shell::Get()->session_controller()->SetSessionInfo(info);
   }
 
+  void SimulateFreezeTimerExpired() {
+    task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(2500));
+  }
+
  private:
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+
   base::test::ScopedFeatureList scoped_feature_list_;
 
   base::HistogramTester histogram_tester_;
@@ -128,6 +141,7 @@
 
   Shell::Get()->media_notification_controller()->OnFocusLost(
       GetRequestStateWithId(id));
+  SimulateFreezeTimerExpired();
 
   ExpectNotificationCount(0);
 }
@@ -164,6 +178,7 @@
 
   Shell::Get()->media_notification_controller()->OnFocusLost(
       GetRequestStateWithId(id1));
+  SimulateFreezeTimerExpired();
 
   ExpectNotificationCount(1);
   ExpectHistogramCountRecorded(1, 1);
@@ -475,4 +490,255 @@
   EXPECT_EQ(2u, message_center->GetVisibleNotifications().size());
 }
 
+// Test that when we lose focus we freeze the notification until the timer
+// is fired and then remove it.
+TEST_F(MediaNotificationControllerImplTest, OnFocusLostFreezeUntilTimerFired) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id.ToString())
+                   ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusLost(
+      GetRequestStateWithId(id));
+
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  SimulateFreezeTimerExpired();
+  ExpectNotificationCount(0);
+}
+
+// Test that when we lose focus we freeze the notification and then we see
+// the session resume we keep the notification and unfreeze it.
+TEST_F(MediaNotificationControllerImplTest, OnFocusLostFreezeAndResumeSameId) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id.ToString())
+                   ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusLost(
+      GetRequestStateWithId(id));
+
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  // The session comes back and is controllable so we should unfreeze the
+  // notification.
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id.ToString())
+                   ->frozen());
+}
+
+// Test that when we lose focus we freeze the notification and then we see
+// the session resume but it is missing metadata we hide the notification.
+TEST_F(MediaNotificationControllerImplTest,
+       OnFocusLostFreezeAndResumeSameId_MissingMetadata) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id.ToString())
+                   ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusLost(
+      GetRequestStateWithId(id));
+
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(media_session::MediaMetadata());
+
+  // The session has come back but the metadata is missing data so we should
+  // keep the notification frozen.
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  // After the timer has been fired we should hide the notification but still
+  // have the controller.
+  SimulateFreezeTimerExpired();
+  ExpectNotificationCount(0);
+  EXPECT_TRUE(Shell::Get()->media_notification_controller()->HasItemForTesting(
+      id.ToString()));
+}
+
+// Test that when we lose focus we freeze the notification and then we see
+// the session resume but it is not controllable we hide the notification.
+TEST_F(MediaNotificationControllerImplTest,
+       OnFocusLostFreezeAndResumeSameId_NotControllable) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id.ToString())
+                   ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusLost(
+      GetRequestStateWithId(id));
+
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  media_session::mojom::AudioFocusRequestStatePtr state =
+      GetRequestStateWithId(id);
+  state->session_info->is_controllable = false;
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      std::move(state));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  // The session has come back but the metadata is not controllable so we should
+  // keep the notification frozen.
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id.ToString())
+                  ->frozen());
+
+  // After the timer has been fired we should hide the notification but still
+  // have the controller.
+  SimulateFreezeTimerExpired();
+  ExpectNotificationCount(0);
+  EXPECT_TRUE(Shell::Get()->media_notification_controller()->HasItemForTesting(
+      id.ToString()));
+}
+
+// Test that when we lose focus we freeze the notification and we see a new
+// session then that does not unfreeze the first notification.
+TEST_F(MediaNotificationControllerImplTest,
+       OnFocusLostFreezeAndDoNotResumeNewId) {
+  base::UnguessableToken id1 = base::UnguessableToken::Create();
+  base::UnguessableToken id2 = base::UnguessableToken::Create();
+
+  ExpectNotificationCount(0);
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id1));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id1.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(1);
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id1.ToString())
+                   ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusLost(
+      GetRequestStateWithId(id1));
+
+  ExpectNotificationCount(1);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id1.ToString())
+                  ->frozen());
+
+  Shell::Get()->media_notification_controller()->OnFocusGained(
+      GetRequestStateWithId(id2));
+
+  Shell::Get()
+      ->media_notification_controller()
+      ->GetItem(id2.ToString())
+      ->MediaSessionMetadataChanged(BuildMediaMetadata());
+
+  ExpectNotificationCount(2);
+  EXPECT_TRUE(Shell::Get()
+                  ->media_notification_controller()
+                  ->GetItem(id1.ToString())
+                  ->frozen());
+  EXPECT_FALSE(Shell::Get()
+                   ->media_notification_controller()
+                   ->GetItem(id2.ToString())
+                   ->frozen());
+
+  SimulateFreezeTimerExpired();
+  ExpectNotificationCount(1);
+
+  EXPECT_FALSE(Shell::Get()->media_notification_controller()->HasItemForTesting(
+      id1.ToString()));
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 7c413350..e153863c 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -786,8 +786,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // ShelfLayoutManager, private:
 
-ShelfLayoutManager::TargetBounds::TargetBounds()
-    : shelf_opacity(0.0f), status_opacity(0.0f) {}
+ShelfLayoutManager::TargetBounds::TargetBounds() : opacity(0.0f) {}
 
 ShelfLayoutManager::TargetBounds::~TargetBounds() = default;
 
@@ -912,15 +911,15 @@
     return;
 
   hide_animation_observer_.reset();
-  if (GetLayer(shelf_widget_)->opacity() != target_bounds.shelf_opacity) {
-    if (target_bounds.shelf_opacity == 0) {
+  if (GetLayer(shelf_widget_)->opacity() != target_bounds.opacity) {
+    if (target_bounds.opacity == 0) {
       // On hide, set the opacity after the animation completes.
       hide_animation_observer_ =
           std::make_unique<HideAnimationObserver>(GetLayer(shelf_widget_));
     } else {
       // On show, set the opacity before the animation begins to ensure the blur
       // is shown while the shelf moves.
-      GetLayer(shelf_widget_)->SetOpacity(target_bounds.shelf_opacity);
+      GetLayer(shelf_widget_)->SetOpacity(target_bounds.opacity);
     }
   }
 
@@ -966,18 +965,15 @@
                               &shelf_bounds);
     shelf_widget_->SetBounds(shelf_bounds);
 
-    GetLayer(nav_widget)->SetOpacity(target_bounds.nav_opacity);
-    GetLayer(status_widget)->SetOpacity(target_bounds.status_opacity);
+    GetLayer(nav_widget)->SetOpacity(target_bounds.opacity);
+    GetLayer(status_widget)->SetOpacity(target_bounds.opacity);
 
     // Having a window which is visible but does not have an opacity is an
     // illegal state. We therefore hide the shelf here if required.
-    if (!target_bounds.status_opacity)
-      status_widget->Hide();
-
-    if (state_.IsActiveSessionState() && target_bounds.nav_opacity)
-      nav_widget->ShowInactive();
-    else
+    if (!target_bounds.opacity) {
       nav_widget->Hide();
+      status_widget->Hide();
+    }
 
     // Setting visibility during an animation causes the visibility property to
     // animate. Override the animation settings to immediately set the
@@ -1030,8 +1026,11 @@
 
   // Setting visibility during an animation causes the visibility property to
   // animate. Set the visibility property without an animation.
-  if (target_bounds.status_opacity)
+  if (target_bounds.opacity) {
+    if (state_.IsActiveSessionState())
+      nav_widget->ShowInactive();
     status_widget->Show();
+  }
 }
 
 bool ShelfLayoutManager::IsDraggingWindowFromTopOrCaptionArea() const {
@@ -1118,14 +1117,7 @@
   target_bounds->nav_bounds_in_shelf =
       gfx::Rect(nav_origin, shelf_widget_->navigation_widget()->GetIdealSize());
 
-  target_bounds->shelf_opacity = ComputeTargetOpacity(state);
-  if (state.IsShelfAutoHidden() && drag_status_ != kDragInProgress) {
-    target_bounds->nav_opacity = 0.0f;
-    target_bounds->status_opacity = 0.0f;
-  } else {
-    target_bounds->nav_opacity = target_bounds->shelf_opacity;
-    target_bounds->status_opacity = target_bounds->shelf_opacity;
-  }
+  target_bounds->opacity = ComputeTargetOpacity(state);
 
   if (drag_status_ == kDragInProgress)
     UpdateTargetBoundsForGesture(target_bounds);
@@ -1731,12 +1723,12 @@
 }
 
 float ShelfLayoutManager::GetAppListBackgroundOpacityOnShelfOpacity() {
-  float shelf_opacity = shelf_widget_->GetBackgroundAlphaValue(
-                            shelf_background_type_before_drag_) /
-                        static_cast<float>(ShelfBackgroundAnimator::kMaxAlpha);
+  float opacity = shelf_widget_->GetBackgroundAlphaValue(
+                      shelf_background_type_before_drag_) /
+                  static_cast<float>(ShelfBackgroundAnimator::kMaxAlpha);
   const int shelf_size = ShelfConstants::shelf_size();
   if (launcher_above_shelf_bottom_amount_ < shelf_size)
-    return shelf_opacity;
+    return opacity;
   float launcher_above_shelf_amount =
       std::max(0.f, launcher_above_shelf_bottom_amount_ - shelf_size);
   float coefficient =
@@ -1747,8 +1739,7 @@
       is_background_blur_enabled_
           ? app_list::AppListView::kAppListOpacityWithBlur
           : app_list::AppListView::kAppListOpacity;
-  return app_list_view_opacity * coefficient +
-         (1 - coefficient) * shelf_opacity;
+  return app_list_view_opacity * coefficient + (1 - coefficient) * opacity;
 }
 
 bool ShelfLayoutManager::IsSwipingCorrectDirection() {
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 7f0c60c..850f10cf 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -230,9 +230,7 @@
     TargetBounds();
     ~TargetBounds();
 
-    float shelf_opacity;
-    float nav_opacity;
-    float status_opacity;
+    float opacity;
 
     gfx::Rect shelf_bounds;            // Bounds of the shelf within the screen
     gfx::Rect shelf_bounds_in_shelf;   // Bounds of the shelf minus status area
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index b70f579..5f05565 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1283,7 +1283,7 @@
   }
   // |rightmost_window_right| may have been modified by an earlier scroll.
   // |scroll_offset_| is added to adjust for that.
-  rightmost_window_right += scroll_offset_;
+  rightmost_window_right -= scroll_offset_;
   scroll_offset_min_ = total_bounds.right() - rightmost_window_right;
 }
 
@@ -1450,9 +1450,10 @@
       rects.push_back(gfx::RectF());
       continue;
     }
-    const int x =
-        width * (window_position / 2) + total_bounds.x() + scroll_offset_;
-    const int y = height * (window_position % 2) + total_bounds.y();
+    const int x = width * (window_position / kTabletLayoutRow) +
+                  total_bounds.x() + scroll_offset_;
+    const int y =
+        height * (window_position % kTabletLayoutRow) + total_bounds.y();
     const gfx::RectF bounds(x, y, width, height);
     rects.push_back(bounds);
     ++window_position;
diff --git a/ash/wm/overview/overview_grid_pre_event_handler.cc b/ash/wm/overview/overview_grid_pre_event_handler.cc
index b80710f..8a025d6e 100644
--- a/ash/wm/overview/overview_grid_pre_event_handler.cc
+++ b/ash/wm/overview/overview_grid_pre_event_handler.cc
@@ -18,20 +18,27 @@
 namespace {
 
 WallpaperView* GetWallpaperViewForRoot(const aura::Window* root_window) {
-  return RootWindowController::ForWindow(root_window)
-      ->wallpaper_widget_controller()
-      ->wallpaper_view();
+  auto* wallpaper_widget_controller =
+      RootWindowController::ForWindow(root_window)
+          ->wallpaper_widget_controller();
+  if (!wallpaper_widget_controller)
+    return nullptr;
+  return wallpaper_widget_controller->wallpaper_view();
 }
 
 }  // namespace
 
 OverviewGridPreEventHandler::OverviewGridPreEventHandler(OverviewGrid* grid)
     : grid_(grid) {
-  GetWallpaperViewForRoot(grid_->root_window())->AddPreTargetHandler(this);
+  auto* wallpaper_view = GetWallpaperViewForRoot(grid_->root_window());
+  if (wallpaper_view)
+    wallpaper_view->AddPreTargetHandler(this);
 }
 
 OverviewGridPreEventHandler::~OverviewGridPreEventHandler() {
-  GetWallpaperViewForRoot(grid_->root_window())->RemovePreTargetHandler(this);
+  auto* wallpaper_view = GetWallpaperViewForRoot(grid_->root_window());
+  if (wallpaper_view)
+    wallpaper_view->RemovePreTargetHandler(this);
 }
 
 void OverviewGridPreEventHandler::OnMouseEvent(ui::MouseEvent* event) {
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index a1b05cb4..96bea5a 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2812,6 +2812,11 @@
     EnterTabletMode();
   }
 
+  void GenerateScrollSequence(const gfx::Point& start, const gfx::Point& end) {
+    GetEventGenerator()->GestureScrollSequence(
+        start, end, base::TimeDelta::FromMilliseconds(10), 10);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -2894,6 +2899,73 @@
   EXPECT_LT(item6_bounds.y(), item7_bounds.y());
 }
 
+// Tests to see if windows are not shifted if all already available windows
+// fit on screen.
+TEST_F(OverviewSessionNewLayoutTest, CheckNoOverviewItemShift) {
+  std::vector<std::unique_ptr<aura::Window>> windows(4);
+  for (int i = 3; i >= 0; --i)
+    windows[i] = CreateTestWindow();
+
+  ToggleOverview();
+  ASSERT_TRUE(InOverviewSession());
+
+  OverviewItem* item0 = GetOverviewItemInGridWithWindow(0, windows[0].get());
+  const gfx::RectF before_shift_bounds = item0->target_bounds();
+
+  GenerateScrollSequence(gfx::Point(100, 50), gfx::Point(0, 50));
+  EXPECT_EQ(before_shift_bounds, item0->target_bounds());
+}
+
+// Tests to see if windows are shifted if at least one window is
+// partially/completely positioned offscreen.
+TEST_F(OverviewSessionNewLayoutTest, CheckOverviewItemShift) {
+  std::vector<std::unique_ptr<aura::Window>> windows(7);
+  for (int i = 6; i >= 0; --i)
+    windows[i] = CreateTestWindow();
+
+  ToggleOverview();
+  ASSERT_TRUE(InOverviewSession());
+
+  OverviewItem* item0 = GetOverviewItemInGridWithWindow(0, windows[0].get());
+  const gfx::RectF before_shift_bounds = item0->target_bounds();
+
+  GenerateScrollSequence(gfx::Point(100, 50), gfx::Point(0, 50));
+  EXPECT_LT(item0->target_bounds(), before_shift_bounds);
+}
+
+// Tests to see if windows remain in bounds after scrolling extremely far.
+TEST_F(OverviewSessionNewLayoutTest, CheckOverviewItemScrollingBounds) {
+  std::vector<std::unique_ptr<aura::Window>> windows(8);
+  for (int i = 7; i >= 0; --i)
+    windows[i] = CreateTestWindow();
+
+  ToggleOverview();
+  ASSERT_TRUE(InOverviewSession());
+
+  // Scroll an extreme amount to see if windows on the far left are still in
+  // bounds. First, align the left-most window (|windows|0||) to the left-hand
+  // bound and store the item's location. Then, scroll a far amount and check to
+  // see if the item moved at all.
+  OverviewItem* leftmost_window =
+      GetOverviewItemInGridWithWindow(0, windows[0].get());
+
+  GenerateScrollSequence(gfx::Point(0, 50), gfx::Point(5000, 50));
+  const gfx::RectF left_bounds = leftmost_window->target_bounds();
+  GenerateScrollSequence(gfx::Point(0, 50), gfx::Point(5000, 50));
+  EXPECT_EQ(left_bounds, leftmost_window->target_bounds());
+
+  // Scroll an extreme amount to see if windows on the far right are still in
+  // bounds. First, align the right-most window (|windows|7||) to the right-hand
+  // bound and store the item's location. Then, scroll a far amount and check to
+  // see if the item moved at all.
+  OverviewItem* rightmost_window =
+      GetOverviewItemInGridWithWindow(0, windows[7].get());
+  GenerateScrollSequence(gfx::Point(5000, 50), gfx::Point(0, 50));
+  const gfx::RectF right_bounds = rightmost_window->target_bounds();
+  GenerateScrollSequence(gfx::Point(5000, 50), gfx::Point(0, 50));
+  EXPECT_EQ(right_bounds, rightmost_window->target_bounds());
+}
+
 // Test the split view and overview functionalities in tablet mode.
 class SplitViewOverviewSessionTest : public OverviewSessionTest {
  public:
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 604fd0f4b..ea967ac 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1081,6 +1081,7 @@
     "win/iat_patch_function.h",
     "win/iunknown_impl.cc",
     "win/iunknown_impl.h",
+    "win/map.h",
     "win/message_window.cc",
     "win/message_window.h",
     "win/object_watcher.cc",
@@ -2814,6 +2815,7 @@
     "win/hstring_reference_unittest.cc",
     "win/i18n_unittest.cc",
     "win/iunknown_impl_unittest.cc",
+    "win/map_unittest.cc",
     "win/message_window_unittest.cc",
     "win/object_watcher_unittest.cc",
     "win/pe_image_unittest.cc",
diff --git a/base/win/map.h b/base/win/map.h
new file mode 100644
index 0000000..20ecc27
--- /dev/null
+++ b/base/win/map.h
@@ -0,0 +1,424 @@
+// 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 BASE_WIN_MAP_H_
+#define BASE_WIN_MAP_H_
+
+#include <windows.foundation.collections.h>
+#include <wrl/implements.h>
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "base/win/vector.h"
+#include "base/win/winrt_foundation_helpers.h"
+
+namespace base {
+namespace win {
+
+template <typename K, typename V>
+class Map;
+
+namespace internal {
+
+// Template tricks needed to dispatch to the correct implementation.
+// See base/win/winrt_foundation_helpers.h for explanation.
+
+template <typename K, typename V>
+using ComplexK =
+    typename ABI::Windows::Foundation::Collections::IMap<K, V>::K_complex;
+
+template <typename K, typename V>
+using ComplexV =
+    typename ABI::Windows::Foundation::Collections::IMap<K, V>::V_complex;
+
+template <typename K, typename V>
+using LogicalK = LogicalType<ComplexK<K, V>>;
+
+template <typename K, typename V>
+using LogicalV = LogicalType<ComplexV<K, V>>;
+
+template <typename K, typename V>
+using AbiK = AbiType<ComplexK<K, V>>;
+
+template <typename K, typename V>
+using AbiV = AbiType<ComplexV<K, V>>;
+
+template <typename K, typename V>
+using StorageK = StorageType<ComplexK<K, V>>;
+
+template <typename K, typename V>
+using StorageV = StorageType<ComplexV<K, V>>;
+
+template <typename K, typename V>
+class KeyValuePair : public Microsoft::WRL::RuntimeClass<
+                         Microsoft::WRL::RuntimeClassFlags<
+                             Microsoft::WRL::WinRtClassicComMix |
+                             Microsoft::WRL::InhibitRoOriginateError>,
+                         ABI::Windows::Foundation::Collections::
+                             IKeyValuePair<LogicalK<K, V>, LogicalV<K, V>>> {
+ public:
+  using AbiK = AbiK<K, V>;
+  using AbiV = AbiV<K, V>;
+  using StorageK = StorageK<K, V>;
+  using StorageV = StorageV<K, V>;
+
+  KeyValuePair(StorageK key, StorageV value)
+      : key_(std::move(key)), value_(std::move(value)) {}
+
+  // ABI::Windows::Foundation::Collections::IKeyValuePair:
+  IFACEMETHODIMP get_Key(AbiK* key) { return CopyTo(key_, key); }
+
+  IFACEMETHODIMP get_Value(AbiV* value) { return CopyTo(value_, value); }
+
+ private:
+  StorageK key_;
+  StorageV value_;
+};
+
+template <typename K>
+class MapChangedEventArgs
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<
+              Microsoft::WRL::WinRtClassicComMix |
+              Microsoft::WRL::InhibitRoOriginateError>,
+          ABI::Windows::Foundation::Collections::IMapChangedEventArgs<K>> {
+ public:
+  MapChangedEventArgs(
+      ABI::Windows::Foundation::Collections::CollectionChange change,
+      K key)
+      : change_(change), key_(std::move(key)) {}
+
+  ~MapChangedEventArgs() override = default;
+
+  // ABI::Windows::Foundation::Collections::IMapChangedEventArgs:
+  IFACEMETHODIMP get_CollectionChange(
+      ABI::Windows::Foundation::Collections::CollectionChange* value) override {
+    *value = change_;
+    return S_OK;
+  }
+
+  IFACEMETHODIMP get_Key(K* value) override {
+    *value = key_;
+    return S_OK;
+  }
+
+ private:
+  const ABI::Windows::Foundation::Collections::CollectionChange change_;
+  K key_;
+};
+
+}  // namespace internal
+
+// This file provides an implementation of Windows::Foundation::IMap. It
+// functions as a thin wrapper around an std::map, and dispatches
+// method calls to either the corresponding std::map API or
+// appropriate std algorithms. Furthermore, it notifies its observers whenever
+// its observable state changes, and is iterable. Please notice also that if the
+// map is modified while iterating over it, iterator methods will return
+// E_CHANGED_STATE. A base::win::Map can be constructed for any types <K,V>, and
+// is implicitly constructible from a std::map. In the case where K or V is a
+// pointer derived from IUnknown, the std::map needs to be of type
+// Microsoft::WRL::ComPtr<K> or Microsoft::WRL::ComPtr<V>. This enforces proper
+// reference counting and improves safety.
+template <typename K, typename V>
+class Map
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<
+              Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
+          ABI::Windows::Foundation::Collections::IMap<internal::LogicalK<K, V>,
+                                                      internal::LogicalV<K, V>>,
+          ABI::Windows::Foundation::Collections::IObservableMap<
+              internal::LogicalK<K, V>,
+              internal::LogicalV<K, V>>,
+          ABI::Windows::Foundation::Collections::IIterable<
+              ABI::Windows::Foundation::Collections::IKeyValuePair<
+                  internal::LogicalK<K, V>,
+                  internal::LogicalV<K, V>>*>> {
+ public:
+  using LogicalK = internal::LogicalK<K, V>;
+  using LogicalV = internal::LogicalV<K, V>;
+  using AbiK = internal::AbiK<K, V>;
+  using AbiV = internal::AbiV<K, V>;
+  using StorageK = internal::StorageK<K, V>;
+  using StorageV = internal::StorageV<K, V>;
+
+ private:
+  class MapView;
+
+  // Iterates over base::win::Map.
+  // Its methods return E_CHANGED_STATE is the map is modified.
+  // TODO(https://crbug.com/987533): Refactor MapIterator to leverage
+  // std::map::iterator.
+  class MapIterator
+      : public Microsoft::WRL::RuntimeClass<
+            Microsoft::WRL::RuntimeClassFlags<
+                Microsoft::WRL::WinRtClassicComMix |
+                Microsoft::WRL::InhibitRoOriginateError>,
+            ABI::Windows::Foundation::Collections::IIterator<
+                ABI::Windows::Foundation::Collections::IKeyValuePair<
+                    internal::LogicalK<K, V>,
+                    internal::LogicalV<K, V>>*>> {
+   public:
+    explicit MapIterator(Microsoft::WRL::ComPtr<MapView> view)
+        : view_(std::move(view)) {
+      DCHECK(view_->ValidState());
+      ConvertMapToVectorIterator();
+    }
+
+    // ABI::Windows::Foundation::Collections::IIterator:
+    IFACEMETHODIMP get_Current(
+        ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,
+                                                             LogicalV>**
+            current) override {
+      return view_->ValidState() ? iterator_->get_Current(current)
+                                 : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP get_HasCurrent(boolean* has_current) override {
+      return view_->ValidState() ? iterator_->get_HasCurrent(has_current)
+                                 : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP MoveNext(boolean* has_current) override {
+      return view_->ValidState() ? iterator_->MoveNext(has_current)
+                                 : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP GetMany(
+        unsigned capacity,
+        ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,
+                                                             LogicalV>** value,
+        unsigned* actual) override {
+      return view_->ValidState() ? iterator_->GetMany(capacity, value, actual)
+                                 : E_CHANGED_STATE;
+    }
+
+   private:
+    // Helper for iteration:
+    void ConvertMapToVectorIterator() {
+      // Create a vector that will hold Map's key-value pairs.
+      auto vector = Microsoft::WRL::Make<
+          Vector<ABI::Windows::Foundation::Collections::IKeyValuePair<
+              LogicalK, LogicalV>*>>();
+
+      // Fill the vector with container data.
+      for (const auto& pair : view_->get_map()) {
+        auto key_value_pair =
+            Microsoft::WRL::Make<internal::KeyValuePair<AbiK, AbiV>>(
+                pair.first, pair.second);
+        vector->Append(key_value_pair.Get());
+      }
+
+      // Return an iterator to that vector.
+      // Iterator is immutable (wraps an IVectorView) and Vector's lifecycle is
+      // ensured cause the view holds a reference to the vector, and iterator
+      // holds a reference to the view.
+      HRESULT hr = vector->First(&iterator_);
+      DCHECK(SUCCEEDED(hr));
+    }
+
+    Microsoft::WRL::ComPtr<MapView> view_;
+    Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator<
+        ABI::Windows::Foundation::Collections::IKeyValuePair<LogicalK,
+                                                             LogicalV>*>>
+        iterator_;
+  };
+
+  class MapView
+      : public Microsoft::WRL::RuntimeClass<
+            Microsoft::WRL::RuntimeClassFlags<
+                Microsoft::WRL::WinRtClassicComMix |
+                Microsoft::WRL::InhibitRoOriginateError>,
+            ABI::Windows::Foundation::Collections::
+                IMapView<internal::LogicalK<K, V>, internal::LogicalV<K, V>>,
+            ABI::Windows::Foundation::Collections::IIterable<
+                ABI::Windows::Foundation::Collections::IKeyValuePair<
+                    internal::LogicalK<K, V>,
+                    internal::LogicalV<K, V>>*>,
+            ABI::Windows::Foundation::Collections::MapChangedEventHandler<
+                internal::LogicalK<K, V>,
+                internal::LogicalV<K, V>>> {
+   public:
+    explicit MapView(Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map)
+        : map_(std::move(map)) {
+      map_->add_MapChanged(this, &map_changed_token_);
+    }
+
+    ~MapView() {
+      if (map_)
+        map_->remove_MapChanged(map_changed_token_);
+    }
+
+    // ABI::Windows::Foundation::Collections::IMapView:
+    IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override {
+      return map_ ? map_->Lookup(key, value) : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP get_Size(unsigned int* size) override {
+      return map_ ? map_->get_Size(size) : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP HasKey(AbiK key, boolean* found) override {
+      return map_ ? map_->HasKey(key, found) : E_CHANGED_STATE;
+    }
+
+    IFACEMETHODIMP Split(
+        ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>**
+            first_partition,
+        ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>**
+            second_partition) override {
+      NOTIMPLEMENTED();
+      return E_NOTIMPL;
+    }
+
+    // ABI::Windows::Foundation::Collections::IIterable:
+    IFACEMETHODIMP First(ABI::Windows::Foundation::Collections::IIterator<
+                         ABI::Windows::Foundation::Collections::
+                             IKeyValuePair<LogicalK, LogicalV>*>** first) {
+      return map_ ? map_->First(first) : E_CHANGED_STATE;
+    }
+
+    // ABI::Windows::Foundation::Collections::MapChangedEventHandler:
+    IFACEMETHODIMP Invoke(
+        ABI::Windows::Foundation::Collections::IObservableMap<LogicalK,
+                                                              LogicalV>* sender,
+        ABI::Windows::Foundation::Collections::IMapChangedEventArgs<LogicalK>*
+            e) {
+      DCHECK_EQ(map_.Get(), sender);
+      map_.Reset();
+      sender->remove_MapChanged(map_changed_token_);
+      return S_OK;
+    }
+
+    // Accessor used in MapIterator for iterating over Map's container.
+    // Will remain valid during the entire iteration.
+    const std::map<StorageK, StorageV, internal::Less>& get_map() {
+      DCHECK(map_);
+      return map_->map_;
+    }
+
+    bool ValidState() const { return map_; }
+
+   private:
+    Microsoft::WRL::ComPtr<Map<LogicalK, LogicalV>> map_;
+    EventRegistrationToken map_changed_token_;
+  };
+
+ public:
+  Map() = default;
+  explicit Map(const std::map<StorageK, StorageV, internal::Less>& map)
+      : map_(map) {}
+  explicit Map(std::map<StorageK, StorageV, internal::Less>&& map)
+      : map_(std::move(map)) {}
+
+  // ABI::Windows::Foundation::Collections::IMap:
+  IFACEMETHODIMP Lookup(AbiK key, AbiV* value) override {
+    auto it = map_.find(key);
+    if (it == map_.cend())
+      return E_BOUNDS;
+
+    return internal::CopyTo(it->second, value);
+  }
+
+  IFACEMETHODIMP get_Size(unsigned int* size) override {
+    *size = map_.size();
+    return S_OK;
+  }
+
+  IFACEMETHODIMP HasKey(AbiK key, boolean* found) override {
+    *found = Contains(map_, key);
+    return S_OK;
+  }
+
+  IFACEMETHODIMP GetView(
+      ABI::Windows::Foundation::Collections::IMapView<LogicalK, LogicalV>**
+          view) override {
+    return Microsoft::WRL::Make<MapView>(this).CopyTo(view);
+  }
+
+  IFACEMETHODIMP Insert(AbiK key, AbiV value, boolean* replaced) override {
+    *replaced = !InsertOrAssign(map_, key, std::move(value)).second;
+    NotifyMapChanged(*replaced ? ABI::Windows::Foundation::Collections::
+                                     CollectionChange_ItemChanged
+                               : ABI::Windows::Foundation::Collections::
+                                     CollectionChange_ItemInserted,
+                     key);
+    return S_OK;
+  }
+
+  IFACEMETHODIMP Remove(AbiK key) override {
+    if (!map_.erase(key))
+      return E_BOUNDS;
+
+    NotifyMapChanged(
+        ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved,
+        key);
+    return S_OK;
+  }
+
+  IFACEMETHODIMP Clear() override {
+    map_.clear();
+    NotifyMapChanged(
+        ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0);
+    return S_OK;
+  }
+
+  // ABI::Windows::Foundation::Collections::IObservableMap:
+  IFACEMETHODIMP add_MapChanged(
+      ABI::Windows::Foundation::Collections::MapChangedEventHandler<LogicalK,
+                                                                    LogicalV>*
+          handler,
+      EventRegistrationToken* token) override {
+    token->value = handler_id_++;
+    handlers_.emplace_hint(handlers_.end(), token->value, handler);
+    return S_OK;
+  }
+
+  IFACEMETHODIMP remove_MapChanged(EventRegistrationToken token) override {
+    return handlers_.erase(token.value) ? S_OK : E_BOUNDS;
+  }
+
+  // ABI::Windows::Foundation::Collections::IIterable:
+  IFACEMETHODIMP First(ABI::Windows::Foundation::Collections::IIterator<
+                       ABI::Windows::Foundation::Collections::
+                           IKeyValuePair<LogicalK, LogicalV>*>** first) {
+    return Microsoft::WRL::Make<MapIterator>(
+               Microsoft::WRL::Make<MapView>(this))
+        .CopyTo(first);
+  }
+
+ private:
+  ~Map() override {
+    // Handlers should not outlive the Map. Furthermore, they must ensure
+    // they are unregistered before the the handler is destroyed. This implies
+    // there should be no handlers left when the Map is destructed.
+    DCHECK(handlers_.empty());
+  }
+
+  void NotifyMapChanged(
+      ABI::Windows::Foundation::Collections::CollectionChange change,
+      AbiK key) {
+    auto args =
+        Microsoft::WRL::Make<internal::MapChangedEventArgs<AbiK>>(change, key);
+
+    // Invoking the handlers could result in mutations to the map, thus we make
+    // a copy beforehand.
+    auto handlers = handlers_;
+    for (auto& handler : handlers)
+      handler.second->Invoke(this, args.Get());
+  }
+
+  std::map<StorageK, StorageV, internal::Less> map_;
+  base::flat_map<int64_t,
+                 ABI::Windows::Foundation::Collections::
+                     MapChangedEventHandler<LogicalK, LogicalV>*>
+      handlers_;
+  int64_t handler_id_ = 0;
+};
+
+}  // namespace win
+}  // namespace base
+
+#endif  // BASE_WIN_MAP_H_
diff --git a/base/win/map_unittest.cc b/base/win/map_unittest.cc
new file mode 100644
index 0000000..096a55c
--- /dev/null
+++ b/base/win/map_unittest.cc
@@ -0,0 +1,548 @@
+// 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/win/map.h"
+
+#include <windows.foundation.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/core_winrt_util.h"
+#include "base/win/hstring_reference.h"
+#include "base/win/scoped_hstring.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ABI {
+namespace Windows {
+namespace Foundation {
+namespace Collections {
+
+// Add missing template specializations (since UWP doesn't provide them):
+
+// Map<int, double> specializations:
+template <>
+struct __declspec(uuid("34784dd6-b37b-4680-b391-899be4f755b6"))
+    IKeyValuePair<int, double> : IKeyValuePair_impl<int, double> {};
+
+template <>
+struct __declspec(uuid("c00bd9bd-cce5-46d6-9dc7-f03067e6d523"))
+    IMap<int, double> : IMap_impl<int, double> {};
+
+template <>
+struct __declspec(uuid("30e075af-9ba2-4562-9f10-a13a0e57ca5b"))
+    IMapView<int, double> : IMapView_impl<int, double> {};
+
+template <>
+struct __declspec(uuid("0a0e8ed6-7deb-4fd4-8033-38d270c69301"))
+    IObservableMap<int, double> : IObservableMap_impl<int, double> {};
+
+template <>
+struct __declspec(uuid("f41f9179-9c95-4755-af55-929a250fc0aa"))
+    IMapChangedEventArgs<int> : IMapChangedEventArgs_impl<int> {};
+
+template <>
+struct __declspec(uuid("79196029-07f6-47c6-9933-9ac3a04e7731"))
+    MapChangedEventHandler<int, double>
+    : MapChangedEventHandler_impl<int, double> {};
+
+template <>
+struct __declspec(uuid("bfd254c3-5ede-4f8f-9e48-3636347f6fe0"))
+    IIterable<IKeyValuePair<int, double>*>
+    : IIterable_impl<IKeyValuePair<int, double>*> {};
+
+template <>
+struct __declspec(uuid("6bb5c7ff-964e-469f-87d3-42daaea8e58d"))
+    IIterator<IKeyValuePair<int, double>*>
+    : IIterator_impl<IKeyValuePair<int, double>*> {};
+
+template <>
+struct __declspec(uuid("7d27014c-8df7-4977-bf98-b0c821f5f988"))
+    IVector<IKeyValuePair<int, double>*>
+    : IVector_impl<IKeyValuePair<int, double>*> {};
+
+template <>
+struct __declspec(uuid("d33b7a5c-9da6-4a6a-8b2e-e08cc0240d77"))
+    IVectorView<IKeyValuePair<int, double>*>
+    : IVectorView_impl<IKeyValuePair<int, double>*> {};
+
+template <>
+struct __declspec(uuid("e5b0d7f2-915d-4831-9a04-466fed63cfa0"))
+    VectorChangedEventHandler<IKeyValuePair<int, double>*>
+    : VectorChangedEventHandler_impl<IKeyValuePair<int, double>*> {};
+
+template <>
+struct __declspec(uuid("27c3ee04-457f-42dd-9556-8f7c4994d7af"))
+    IObservableVector<IKeyValuePair<int, double>*>
+    : IObservableVector_impl<IKeyValuePair<int, double>*> {};
+
+// Map<Uri*, Uri*> specializations:
+template <>
+struct __declspec(uuid("c03984bc-b800-43e4-a36e-3c8c4a34c005")) IMap<Uri*, Uri*>
+    : IMap_impl<Uri*, Uri*> {};
+
+template <>
+struct __declspec(uuid("93ec9c52-1b0b-4fd8-ab5a-f6ea32db0e35"))
+    IMapView<Uri*, Uri*> : IMapView_impl<Uri*, Uri*> {};
+
+template <>
+struct __declspec(uuid("9b711c83-5f01-4604-9e01-3d586b3f9cdd"))
+    IObservableMap<Uri*, Uri*> : IObservableMap_impl<Uri*, Uri*> {};
+
+template <>
+struct __declspec(uuid("f41f9179-9c95-4755-af55-929a250fc0aa"))
+    IMapChangedEventArgs<Uri*> : IMapChangedEventArgs_impl<Uri*> {};
+
+template <>
+struct __declspec(uuid("6d758124-f99a-47e7-ab74-7cff7359b206"))
+    MapChangedEventHandler<Uri*, Uri*>
+    : MapChangedEventHandler_impl<Uri*, Uri*> {};
+
+template <>
+struct __declspec(uuid("8b270b8a-d74b-459b-9933-81cb234d7c5e"))
+    IKeyValuePair<Uri*, Uri*> : IKeyValuePair_impl<Uri*, Uri*> {};
+
+template <>
+struct __declspec(uuid("6368bcea-dfbc-4847-ba50-9e217fc2d5c3"))
+    IIterable<IKeyValuePair<Uri*, Uri*>*>
+    : IIterable_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+template <>
+struct __declspec(uuid("7653cf9f-9d0b-46d3-882e-4c0afb209333"))
+    IIterator<IKeyValuePair<Uri*, Uri*>*>
+    : IIterator_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+template <>
+struct __declspec(uuid("98c3f5a7-237d-494b-ba89-4a49368d5491"))
+    IVector<IKeyValuePair<Uri*, Uri*>*>
+    : IVector_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+template <>
+struct __declspec(uuid("2cfc2617-7c88-4482-8158-97bf7cc458d7"))
+    IVectorView<IKeyValuePair<Uri*, Uri*>*>
+    : IVectorView_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+template <>
+struct __declspec(uuid("bb581e03-3ee7-4c01-8035-4f581c5e91f5"))
+    VectorChangedEventHandler<IKeyValuePair<Uri*, Uri*>*>
+    : VectorChangedEventHandler_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+template <>
+struct __declspec(uuid("fb0bd692-34c3-4242-a085-58ed71e8ea6b"))
+    IObservableVector<IKeyValuePair<Uri*, Uri*>*>
+    : IObservableVector_impl<IKeyValuePair<Uri*, Uri*>*> {};
+
+// Map<HSTRING*, IInspectable*> specializations:
+template <>
+struct __declspec(uuid("c6682be1-963c-4101-85aa-63db583eb0d5"))
+    IVector<IKeyValuePair<HSTRING, IInspectable*>*>
+    : IVector_impl<IKeyValuePair<HSTRING, IInspectable*>*> {};
+
+template <>
+struct __declspec(uuid("868e5342-49c8-478f-af0f-1691e1bbbb7c"))
+    IVectorView<IKeyValuePair<HSTRING, IInspectable*>*>
+    : IVectorView_impl<IKeyValuePair<HSTRING, IInspectable*>*> {};
+
+template <>
+struct __declspec(uuid("cd99b82f-a768-405f-9123-be509146fef8"))
+    VectorChangedEventHandler<IKeyValuePair<HSTRING, IInspectable*>*>
+    : VectorChangedEventHandler_impl<IKeyValuePair<HSTRING, IInspectable*>*> {};
+
+template <>
+struct __declspec(uuid("079e2180-0c7a-4508-85ff-7a5f2b29b92b"))
+    IObservableVector<IKeyValuePair<HSTRING, IInspectable*>*>
+    : IObservableVector_impl<IKeyValuePair<HSTRING, IInspectable*>*> {};
+
+}  // namespace Collections
+}  // namespace Foundation
+}  // namespace Windows
+}  // namespace ABI
+
+namespace base {
+namespace win {
+
+namespace {
+
+using ABI::Windows::Foundation::IPropertyValue;
+using ABI::Windows::Foundation::IPropertyValueStatics;
+using ABI::Windows::Foundation::Uri;
+using ABI::Windows::Foundation::Collections::CollectionChange;
+using ABI::Windows::Foundation::Collections::CollectionChange_ItemChanged;
+using ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted;
+using ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved;
+using ABI::Windows::Foundation::Collections::CollectionChange_Reset;
+using ABI::Windows::Foundation::Collections::IIterator;
+using ABI::Windows::Foundation::Collections::IKeyValuePair;
+using ABI::Windows::Foundation::Collections::IMapChangedEventArgs;
+using ABI::Windows::Foundation::Collections::IMapView;
+using ABI::Windows::Foundation::Collections::IObservableMap;
+using ABI::Windows::Foundation::Collections::MapChangedEventHandler;
+using Microsoft::WRL::ClassicCom;
+using Microsoft::WRL::ComPtr;
+using Microsoft::WRL::InhibitRoOriginateError;
+using Microsoft::WRL::Make;
+using Microsoft::WRL::RuntimeClass;
+using Microsoft::WRL::RuntimeClassFlags;
+
+const wchar_t kTestKey[] = L"Test key";
+const wchar_t kTestValue[] = L"Test value";
+
+const std::map<int, double, internal::Less> g_one{{1, 10.7}};
+const std::map<int, double, internal::Less> g_two{{1, 10.7}, {2, 20.3}};
+
+bool ResolveCoreWinRT() {
+  return base::win::ResolveCoreWinRTDelayload() &&
+         base::win::ScopedHString::ResolveCoreWinRTStringDelayload() &&
+         base::win::HStringReference::ResolveCoreWinRTStringDelayload();
+}
+
+HRESULT GetPropertyValueStaticsActivationFactory(
+    IPropertyValueStatics** statics) {
+  return base::win::GetActivationFactory<
+      IPropertyValueStatics, RuntimeClass_Windows_Foundation_PropertyValue>(
+      statics);
+}
+
+template <typename K, typename V>
+class FakeMapChangedEventHandler
+    : public RuntimeClass<
+          RuntimeClassFlags<ClassicCom | InhibitRoOriginateError>,
+          MapChangedEventHandler<K, V>> {
+ public:
+  explicit FakeMapChangedEventHandler(ComPtr<IObservableMap<K, V>> map)
+      : map_(std::move(map)) {
+    EXPECT_HRESULT_SUCCEEDED(map_->add_MapChanged(this, &token_));
+  }
+
+  ~FakeMapChangedEventHandler() {
+    EXPECT_HRESULT_SUCCEEDED(map_->remove_MapChanged(token_));
+  }
+
+  // MapChangedEventHandler:
+  IFACEMETHODIMP Invoke(IObservableMap<K, V>* sender,
+                        IMapChangedEventArgs<K>* e) {
+    sender_ = sender;
+    EXPECT_HRESULT_SUCCEEDED(e->get_CollectionChange(&change_));
+    EXPECT_HRESULT_SUCCEEDED(e->get_Key(&key_));
+    return S_OK;
+  }
+
+  IObservableMap<K, V>* sender() { return sender_; }
+  CollectionChange change() { return change_; }
+  K key() const { return key_; }
+
+ private:
+  ComPtr<IObservableMap<K, V>> map_;
+  EventRegistrationToken token_;
+  IObservableMap<K, V>* sender_ = nullptr;
+  CollectionChange change_ = CollectionChange_Reset;
+  K key_ = 0;
+};
+
+}  // namespace
+
+TEST(MapTest, Lookup_Empty) {
+  auto map = Make<Map<int, double>>();
+  double value;
+  HRESULT hr = map->Lookup(1, &value);
+  EXPECT_EQ(E_BOUNDS, hr);
+  hr = map->Lookup(2, &value);
+  EXPECT_EQ(E_BOUNDS, hr);
+}
+
+TEST(MapTest, Lookup_One) {
+  auto map = Make<Map<int, double>>(g_one);
+  double value;
+  HRESULT hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(10.7, value);
+  hr = map->Lookup(2, &value);
+  EXPECT_EQ(E_BOUNDS, hr);
+}
+
+TEST(MapTest, Lookup_Two) {
+  auto map = Make<Map<int, double>>(g_two);
+  double value;
+  HRESULT hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(10.7, value);
+  hr = map->Lookup(2, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(20.3, value);
+}
+
+TEST(MapTest, get_Size_Empty) {
+  auto map = Make<Map<int, double>>();
+  unsigned int size;
+  HRESULT hr = map->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(0u, size);
+}
+
+TEST(MapTest, get_Size_One) {
+  auto map = Make<Map<int, double>>(g_one);
+  unsigned int size;
+  HRESULT hr = map->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(1u, size);
+}
+
+TEST(MapTest, get_Size_Two) {
+  auto map = Make<Map<int, double>>(g_two);
+  unsigned int size;
+  HRESULT hr = map->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(2u, size);
+}
+
+TEST(MapTest, HasKey_Empty) {
+  auto map = Make<Map<int, double>>();
+  boolean found;
+  HRESULT hr = map->HasKey(1, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_FALSE(found);
+}
+
+TEST(MapTest, HasKey_One) {
+  auto map = Make<Map<int, double>>(g_one);
+  boolean found;
+  HRESULT hr = map->HasKey(1, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(found);
+  hr = map->HasKey(2, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_FALSE(found);
+}
+
+TEST(MapTest, HasKey_Two) {
+  auto map = Make<Map<int, double>>(g_two);
+  boolean found;
+  HRESULT hr = map->HasKey(1, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(found);
+  hr = map->HasKey(2, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(found);
+}
+
+TEST(MapTest, GetView) {
+  auto map = Make<Map<int, double>>(g_two);
+  ComPtr<IMapView<int, double>> view;
+  HRESULT hr = map->GetView(&view);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  double value;
+  hr = view->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(10.7, value);
+  hr = view->Lookup(2, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(20.3, value);
+
+  unsigned int size;
+  hr = view->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(2u, size);
+
+  boolean found;
+  hr = view->HasKey(1, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(found);
+  hr = view->HasKey(2, &found);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(found);
+
+  // The view is supposed to be a snapshot of the map when it's created.
+  // Further modifications to the map will invalidate the view.
+  boolean replaced;
+  hr = map->Insert(3, 11.2, &replaced);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_FALSE(replaced);
+
+  hr = view->Lookup(1, &value);
+  EXPECT_EQ(E_CHANGED_STATE, hr);
+
+  hr = view->get_Size(&size);
+  EXPECT_EQ(E_CHANGED_STATE, hr);
+
+  hr = view->HasKey(1, &found);
+  EXPECT_EQ(E_CHANGED_STATE, hr);
+}
+
+TEST(MapTest, Insert_Empty) {
+  auto map = Make<Map<int, double>>();
+  auto handler = Make<FakeMapChangedEventHandler<int, double>>(map.Get());
+  boolean replaced;
+  HRESULT hr = map->Insert(1, 11.2, &replaced);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_FALSE(replaced);
+  EXPECT_EQ(map.Get(), handler->sender());
+  EXPECT_EQ(CollectionChange_ItemInserted, handler->change());
+  EXPECT_EQ(1, handler->key());
+  double value;
+  hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(11.2, value);
+}
+
+TEST(MapTest, Insert_One) {
+  auto map = Make<Map<int, double>>(g_one);
+  auto handler = Make<FakeMapChangedEventHandler<int, double>>(map.Get());
+  double value;
+  HRESULT hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(10.7, value);
+  boolean replaced;
+  hr = map->Insert(1, 11.2, &replaced);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(replaced);
+  EXPECT_EQ(map.Get(), handler->sender());
+  EXPECT_EQ(CollectionChange_ItemChanged, handler->change());
+  EXPECT_EQ(1, handler->key());
+  hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(11.2, value);
+}
+
+TEST(MapTest, Remove_One) {
+  auto map = Make<Map<int, double>>(g_one);
+  auto handler = Make<FakeMapChangedEventHandler<int, double>>(map.Get());
+  double value;
+  HRESULT hr = map->Lookup(1, &value);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(10.7, value);
+  hr = map->Remove(1);
+  EXPECT_EQ(S_OK, hr);
+  EXPECT_EQ(map.Get(), handler->sender());
+  EXPECT_EQ(CollectionChange_ItemRemoved, handler->change());
+  EXPECT_EQ(1, handler->key());
+  hr = map->Lookup(1, &value);
+  EXPECT_EQ(E_BOUNDS, hr);
+}
+
+TEST(MapTest, Clear) {
+  auto map = Make<Map<int, double>>(g_one);
+  auto handler = Make<FakeMapChangedEventHandler<int, double>>(map.Get());
+  HRESULT hr = map->Clear();
+  EXPECT_EQ(map.Get(), handler->sender());
+  EXPECT_EQ(CollectionChange_Reset, handler->change());
+  EXPECT_EQ(0, handler->key());
+  unsigned int size;
+  hr = map->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(0u, size);
+}
+
+// Uri* is an AggregateType which ABI representation is IUriRuntimeClass*.
+TEST(MapTest, ConstructWithAggregateTypes) {
+  auto map = Make<Map<Uri*, Uri*>>();
+  unsigned size;
+  HRESULT hr = map->get_Size(&size);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(0u, size);
+}
+
+TEST(MapTest, First) {
+  auto map = Make<Map<int, double>>(g_two);
+  ComPtr<IIterator<IKeyValuePair<int, double>*>> iterator;
+
+  // Test iteration.
+  HRESULT hr = map->First(&iterator);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  boolean has_current;
+  hr = iterator->get_HasCurrent(&has_current);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(has_current);
+  ComPtr<IKeyValuePair<int, double>> current;
+  hr = iterator->get_Current(&current);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  int key;
+  hr = current->get_Key(&key);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(1, key);
+  double value;
+  hr = current->get_Value(&value);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(10.7, value);
+  hr = iterator->MoveNext(&has_current);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(has_current);
+  hr = iterator->get_Current(&current);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  hr = current->get_Key(&key);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(2, key);
+  hr = current->get_Value(&value);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_EQ(20.3, value);
+  hr = iterator->MoveNext(&has_current);
+  EXPECT_FALSE(SUCCEEDED(hr));
+  EXPECT_EQ(E_BOUNDS, hr);
+  EXPECT_FALSE(has_current);
+  hr = iterator->get_Current(&current);
+  EXPECT_FALSE(SUCCEEDED(hr));
+  EXPECT_EQ(E_BOUNDS, hr);
+
+  // Test invalidation.
+  hr = map->First(&iterator);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  hr = iterator->get_HasCurrent(&has_current);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_TRUE(has_current);
+  boolean replaced;
+  hr = map->Insert(3, 11.2, &replaced);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+  EXPECT_FALSE(replaced);
+  hr = iterator->get_HasCurrent(&has_current);
+  EXPECT_EQ(E_CHANGED_STATE, hr);
+  hr = iterator->MoveNext(&has_current);
+  EXPECT_EQ(E_CHANGED_STATE, hr);
+}
+
+TEST(MapTest, Properties) {
+  // This test case validates Map against Windows property key system,
+  // which is used to store WinRT device properties.
+  if (GetVersion() < Version::WIN8)
+    return;
+
+  ASSERT_TRUE(ResolveCoreWinRT());
+  ASSERT_HRESULT_SUCCEEDED(base::win::RoInitialize(RO_INIT_MULTITHREADED));
+
+  auto map = Make<Map<HSTRING, IInspectable*>>();
+
+  ComPtr<IPropertyValueStatics> property_value_statics;
+  HRESULT hr =
+      GetPropertyValueStaticsActivationFactory(&property_value_statics);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  base::win::HStringReference value_stringref_inserted(kTestValue);
+  ComPtr<IPropertyValue> value_inserted;
+  hr = property_value_statics->CreateString(value_stringref_inserted.Get(),
+                                            &value_inserted);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  base::win::HStringReference key_stringref_inserted(kTestKey);
+  boolean replaced;
+  hr = map->Insert(key_stringref_inserted.Get(), value_inserted.Get(),
+                   &replaced);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  base::win::HStringReference key_stringref_lookedup(kTestKey);
+  ComPtr<IInspectable> value_inspectable_lookedup;
+  hr = map->Lookup(key_stringref_lookedup.Get(), &value_inspectable_lookedup);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  ComPtr<IPropertyValue> value_lookedup;
+  hr = value_inspectable_lookedup.As(&value_lookedup);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  HSTRING value_string_lookedup;
+  hr = value_lookedup->GetString(&value_string_lookedup);
+  EXPECT_HRESULT_SUCCEEDED(hr);
+
+  auto value_stringref_lookedup = ScopedHString(value_string_lookedup);
+  EXPECT_EQ(kTestValue, value_stringref_lookedup.Get());
+}
+
+}  // namespace win
+}  // namespace base
diff --git a/build/android/gyp/native_libraries_template.py b/build/android/gyp/native_libraries_template.py
index 901fd86..52ba0872 100644
--- a/build/android/gyp/native_libraries_template.py
+++ b/build/android/gyp/native_libraries_template.py
@@ -24,7 +24,7 @@
     // by LibraryLoader.java.
     // TODO(cjhopman): This is public since it is referenced by NativeTestActivity.java
     // directly. The two ways of library loading should be refactored into one.
-    public static {MAYBE_FINAL}String[] LIBRARIES = {LIBRARIES};
+    public static {MAYBE_FINAL}String[] LIBRARIES = {{{LIBRARIES}}};
 
     // This is the expected version of the 'main' native library, which is the one that
     // implements the initial set of base JNI functions including
diff --git a/build/android/gyp/write_native_libraries_java.py b/build/android/gyp/write_native_libraries_java.py
index c7ad066..1bd542f 100755
--- a/build/android/gyp/write_native_libraries_java.py
+++ b/build/android/gyp/write_native_libraries_java.py
@@ -32,9 +32,6 @@
   parser.add_argument(
       '--native-libraries-list', help='File with list of native libraries.')
   parser.add_argument(
-      '--exclude-native-libraries',
-      help='List of native libraries to exclude from the output.')
-  parser.add_argument(
       '--version-number',
       default='""',
       help='Expected version of main library.')
@@ -57,27 +54,16 @@
           or not options.load_library_from_apk), (
               'Must set --enable-chromium-linker to load library from APK.')
 
-  lib_paths = []
-  exclude_native_libraries = []
-  if options.exclude_native_libraries:
-    exclude_native_libraries = options.exclude_native_libraries.split(',')
+  native_libraries_list = []
   if options.native_libraries_list:
     with open(options.native_libraries_list) as f:
-      for line in f:
-        line = line.strip()
-        assert line.endswith('.so')
-        if os.path.basename(line) in exclude_native_libraries:
-          continue
-        lib_paths.append(line)
-
-  def LibBasename(path):
-    filename = os.path.split(path)[1]
-    base = os.path.splitext(filename)[0]
-    return base[3:]  # remove lib prefix
-
-  # Convert to "base" library names: e.g. libfoo.so -> foo.
-  native_libraries_list = (
-      '{%s}' % ','.join(['"%s"' % LibBasename(s) for s in lib_paths]))
+      for path in f:
+        path = path.strip()
+        filename = os.path.split(path)[1]
+        assert filename.startswith('lib')
+        assert filename.endswith('.so')
+        # Remove lib prefix and .so suffix.
+        native_libraries_list.append('"%s"' % filename[3:-3])
 
   def bool_str(value):
     if value:
@@ -91,7 +77,7 @@
       'USE_LINKER': bool_str(options.enable_chromium_linker),
       'USE_LIBRARY_IN_ZIP_FILE': bool_str(options.load_library_from_apk),
       'ENABLE_LINKER_TESTS': bool_str(options.enable_chromium_linker_tests),
-      'LIBRARIES': native_libraries_list,
+      'LIBRARIES': ','.join(native_libraries_list),
       'VERSION_NUMBER': options.version_number,
       'CPU_FAMILY': options.cpu_family,
   }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 8b4192f0..363d0a5 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -728,8 +728,6 @@
   #   load_library_from_apk: (Optional) Whether libraries should be loaded from
   #     the APK without uncompressing.
   #   enable_chromium_linker_tests: (Optional) Whether to run tests.
-  #   dont_load_shared_libraries: (Optional) List of native libraries to exclude
-  #     from output.
   #   use_final_fields: True to use final fields. When false, all other
   #       variables must not be set.
   template("write_native_libraries_java") {
@@ -787,12 +785,6 @@
             invoker.enable_chromium_linker_tests) {
           args += [ "--enable-chromium-linker-tests" ]
         }
-
-        # TODO(tiborg): Remove once all usages are removed.
-        if (defined(invoker.dont_load_shared_libraries)) {
-          args += [ "--exclude-native-libraries=" +
-                    invoker.dont_load_shared_libraries ]
-        }
       }
     }
   }
@@ -2655,7 +2647,6 @@
       write_native_libraries_java("${_template_name}__native_libraries") {
         forward_variables_from(invoker,
                                [
-                                 "dont_load_shared_libraries",
                                  "enable_chromium_linker_tests",
                                ])
         deps = [
@@ -3365,7 +3356,6 @@
                                "dexlayout_profile",
                                "disable_r8_outlining",
                                "dist_ijar_path",
-                               "dont_load_shared_libraries",
                                "enable_chromium_linker_tests",
                                "enable_multidex",
                                "enable_native_mocks",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 99bd67e..abfd7312 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8905797561571658672
\ No newline at end of file
+8905772899826882240
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 017dfd3..d31bc514 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8905797565613988256
\ No newline at end of file
+8905773019186674272
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 9a52a23..b3145e8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -369,7 +369,6 @@
     "//third_party/android_deps:javax_inject_javax_inject_java",
     "//third_party/android_media:android_media_java",
     "//third_party/android_sdk:android_gcm_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
@@ -377,6 +376,7 @@
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
     "//third_party/cacheinvalidation:cacheinvalidation_proto_java",
     "//third_party/cct_dynamic_module:cct_dynamic_module_java",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/feed:feed_lib_proto_java",
     "//third_party/gif_player:gif_player_java",
     "//third_party/google_android_play_core:com_google_android_play_core_java",
@@ -710,10 +710,10 @@
     "//third_party/android_deps:com_android_support_mediarouter_v7_java",
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
     "//third_party/android_deps:com_android_support_support_annotations_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/hamcrest:hamcrest_java",
     "//ui/android:ui_java",
     "//url/mojom:url_mojom_gurl_java",
@@ -755,7 +755,7 @@
     "//third_party/android_deps:android_support_v4_java",
     "//third_party/android_media:android_media_resources",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/espresso:espresso_all_java",
     "//third_party/junit:junit",
   ]
@@ -862,13 +862,13 @@
     "//third_party/android_sdk:android_test_runner_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/blink/public/mojom:mojom_mhtml_load_result_java",
     "//third_party/blink/public/mojom:mojom_platform_java",
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
     "//third_party/cct_dynamic_module:cct_dynamic_module_java",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/espresso:espresso_all_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/jsr-305:jsr_305_javalib",
@@ -950,13 +950,13 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
-    "//third_party/android_deps:android_arch_lifecycle_common_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
-    "//third_party/android_deps:com_android_support_recyclerview_v7_java",
-    "//third_party/android_deps:com_android_support_support_annotations_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
+    "//third_party/android_deps:android_arch_lifecycle_common_java",
+    "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:com_android_support_recyclerview_v7_java",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
     "//ui/android:ui_java",
diff --git a/chrome/android/features/start_surface/internal/BUILD.gn b/chrome/android/features/start_surface/internal/BUILD.gn
index 0d9a901..9a3863d1 100644
--- a/chrome/android/features/start_surface/internal/BUILD.gn
+++ b/chrome/android/features/start_surface/internal/BUILD.gn
@@ -108,7 +108,7 @@
     deps += [
       "//chrome/android/public/profiles:java",
       "//content/public/android:content_java",
-      "//third_party/android_sdk/androidx_browser:androidx_browser_java",
+      "//third_party/custom_tabs_client:custom_tabs_support_java",
       "//third_party/feed:feed_lib_java",
       "//ui/android:ui_utils_java",
       "//ui/base/mojom:mojom_java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
index 8de5396b..9675009 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
@@ -8,6 +8,7 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
@@ -16,8 +17,6 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /** Implementation of the {@link NativePageNavigationDelegate} for the explore surface. */
 class ExploreSurfaceNavigationDelegate implements NativePageNavigationDelegate {
     private final Context mContext;
@@ -57,4 +56,4 @@
     public void setIncognito(boolean isIncognito) {
         mIsInCognito = isIncognito;
     }
-}
+}
\ No newline at end of file
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorageDirect.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorageDirect.java
new file mode 100644
index 0000000..0f65da1
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorageDirect.java
@@ -0,0 +1,55 @@
+// 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.feed;
+
+import com.google.android.libraries.feed.api.host.storage.CommitResult;
+import com.google.android.libraries.feed.api.host.storage.ContentMutation;
+import com.google.android.libraries.feed.api.host.storage.ContentStorage;
+import com.google.android.libraries.feed.api.host.storage.ContentStorageDirect;
+import com.google.android.libraries.feed.common.Result;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wrapper around {@link ContentStorage}, providing a synchronous implementation.
+ */
+public final class FeedContentStorageDirect implements ContentStorageDirect {
+    private static final String LOCATION = "FeedContentStorageDirect.";
+    private final ContentStorage mContentStorage;
+
+    FeedContentStorageDirect(ContentStorage contentStorage) {
+        mContentStorage = contentStorage;
+    }
+
+    @Override
+    public Result<Map<String, byte[]>> get(List<String> keys) {
+        if (keys.isEmpty()) {
+            return Result.success(Collections.emptyMap());
+        }
+
+        return FutureTaskConsumer.consume(LOCATION + "get",
+                (consumer) -> mContentStorage.get(keys, consumer), Result.failure());
+    }
+
+    @Override
+    public Result<Map<String, byte[]>> getAll(String prefix) {
+        return FutureTaskConsumer.consume(LOCATION + "getAll",
+                (consumer) -> mContentStorage.getAll(prefix, consumer), Result.failure());
+    }
+
+    @Override
+    public CommitResult commit(ContentMutation mutation) {
+        return FutureTaskConsumer.consume(LOCATION + "commit",
+                (consumer) -> mContentStorage.commit(mutation, consumer), CommitResult.FAILURE);
+    }
+
+    @Override
+    public Result<List<String>> getAllKeys() {
+        return FutureTaskConsumer.consume(
+                LOCATION + "getAllKeys", mContentStorage::getAllKeys, Result.failure());
+    }
+}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorageDirect.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorageDirect.java
new file mode 100644
index 0000000..8417c79
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorageDirect.java
@@ -0,0 +1,55 @@
+// 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.feed;
+
+import com.google.android.libraries.feed.api.host.storage.CommitResult;
+import com.google.android.libraries.feed.api.host.storage.JournalMutation;
+import com.google.android.libraries.feed.api.host.storage.JournalStorage;
+import com.google.android.libraries.feed.api.host.storage.JournalStorageDirect;
+import com.google.android.libraries.feed.common.Result;
+
+import java.util.List;
+
+/**
+ * Wrapper around {@link JournalStorage}, providing a synchronous implementation.
+ */
+public final class FeedJournalStorageDirect implements JournalStorageDirect {
+    private static final String LOCATION = "FeedJournalStorageDirect.";
+    private final JournalStorage mJournalStorage;
+
+    FeedJournalStorageDirect(JournalStorage journalStorage) {
+        this.mJournalStorage = journalStorage;
+    }
+
+    @Override
+    public Result<List<byte[]>> read(String journalName) {
+        return FutureTaskConsumer.consume(LOCATION + "read",
+                (consumer) -> mJournalStorage.read(journalName, consumer), Result.failure());
+    }
+
+    @Override
+    public CommitResult commit(JournalMutation mutation) {
+        return FutureTaskConsumer.consume(LOCATION + "commit",
+                (consumer) -> mJournalStorage.commit(mutation, consumer), CommitResult.FAILURE);
+    }
+
+    @Override
+    public Result<Boolean> exists(String journalName) {
+        return FutureTaskConsumer.consume(LOCATION + "exists",
+                (consumer) -> mJournalStorage.exists(journalName, consumer), Result.failure());
+    }
+
+    @Override
+    public Result<List<String>> getAllJournals() {
+        return FutureTaskConsumer.consume(
+                LOCATION + "getAllJournals", mJournalStorage::getAllJournals, Result.failure());
+    }
+
+    @Override
+    public CommitResult deleteAll() {
+        return FutureTaskConsumer.consume(
+                LOCATION + "deleteAll", mJournalStorage::deleteAll, CommitResult.FAILURE);
+    }
+}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index 57894f3..4dbc8316 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -150,8 +150,10 @@
 
         FeedSchedulerBridge schedulerBridge = new FeedSchedulerBridge(profile);
         sFeedScheduler = schedulerBridge;
-        FeedContentStorage contentStorage = new FeedContentStorage(profile);
-        FeedJournalStorage journalStorage = new FeedJournalStorage(profile);
+        ContentStorageDirect contentStorageDirect =
+                new FeedContentStorageDirect(new FeedContentStorage(profile));
+        JournalStorageDirect journalStorageDirect =
+                new FeedJournalStorageDirect(new FeedJournalStorage(profile));
         NetworkClient networkClient = sTestNetworkClient == null ?
             new FeedNetworkBridge(profile) : sTestNetworkClient;
         sFeedLoggingBridge = new FeedLoggingBridge(profile);
@@ -159,8 +161,8 @@
                 sFeedLoggingBridge, networkClient, schedulerBridge, DebugBehavior.SILENT,
                 ContextUtils.getApplicationContext(), applicationInfo,
                 new BasicTooltipSupportedApi())
-                                .setContentStorage(contentStorage)
-                                .setJournalStorage(journalStorage)
+                                .setContentStorageDirect(contentStorageDirect)
+                                .setJournalStorageDirect(journalStorageDirect)
                                 .build();
         schedulerBridge.initializeFeedDependencies(sProcessScope.getRequestManager());
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FutureTaskConsumer.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FutureTaskConsumer.java
new file mode 100644
index 0000000..b87bf77
--- /dev/null
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FutureTaskConsumer.java
@@ -0,0 +1,40 @@
+// 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.feed;
+
+import com.google.android.libraries.feed.common.concurrent.SimpleSettableFuture;
+import com.google.android.libraries.feed.common.functional.Consumer;
+
+import org.chromium.base.Callback;
+import org.chromium.base.Log;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Processes a {@link Callback} on a {@link SimpleSettableFuture}. This is necessary to provide a
+ * synchronous interface to the Feed consumer.
+ */
+public class FutureTaskConsumer {
+    private static final String TAG = "FutureTaskConsumer";
+    /**
+     * Sets the task result on a Future and returns it.
+     *
+     * @param location Caller location for error logging.
+     * @param task Callback on which to run the results.
+     * @param failure Default result in case of failure.
+     * @param <T> Type of result expected by the Feed {@link Consumer}.
+     * @return Result of the task.
+     */
+    public static <T> T consume(String location, Callback<Consumer<T>> task, T failure) {
+        SimpleSettableFuture<T> sharedStatesFuture = new SimpleSettableFuture<>();
+        task.onResult(sharedStatesFuture::put);
+        try {
+            return sharedStatesFuture.get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.e(TAG, "%s: %s", location, e.toString());
+            return failure;
+        }
+    }
+}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 182d4a1..f4645951 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -14,9 +14,11 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorage.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedContentStorageDirect.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorageDirect.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLifecycleBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java",
@@ -30,6 +32,7 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedRefreshTask.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedScheduler.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSchedulerBridge.java",
+    "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FutureTaskConsumer.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/TestNetworkClient.java",
@@ -47,6 +50,7 @@
     "junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java",
     "junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java",
     "junit/src/org/chromium/chrome/browser/feed/FeedOfflineBridgeTest.java",
+    "junit/src/org/chromium/chrome/browser/feed/FutureTaskConsumerTest.java",
     "junit/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManagerTest.java",
     "junit/src/org/chromium/chrome/browser/feed/action/FeedActionHandlerTest.java",
   ]
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 22591d1..545468e1 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1169,7 +1169,7 @@
               <category android:name="androidx.browser.customtabs.category.ColorSchemeCustomization"/>
             </intent-filter>
         </service>
-        <service android:name="androidx.browser.customtabs.PostMessageService" />
+        <service android:name="android.support.customtabs.PostMessageService" />
 
         <!-- Crash reporting services. -->
         <service android:name="org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService"
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 7c4c3c4..337c7b9 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -1578,7 +1578,7 @@
         <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
       </intent-filter>
     </service>
-    <service android:name="androidx.browser.customtabs.PostMessageService"/>
+    <service android:name="android.support.customtabs.PostMessageService"/>
     <service
         android:exported="true"
         android:externalService="true"
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 6d74dd5c..ea03517 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1054,10 +1054,7 @@
     private void setInitialOverviewState() {
         boolean isOverviewVisible = mOverviewModeController.overviewVisible();
 
-        // Experiment: show tab switcher on return after {x} minutes (enable-tab-switcher-on-return}
-        long lastBackgroundedTimeMillis = mInactivityTracker.getLastBackgroundedTimeMs();
-        if (ReturnToChromeExperimentsUtil.shouldShowTabSwitcher(lastBackgroundedTimeMillis)
-                && isMainIntentFromLauncher(getIntent()) && !isOverviewVisible) {
+        if (shouldShowTabSwitcherOnStart() && !isOverviewVisible) {
             if (getCurrentTabModel() != null) {
                 RecordHistogram.recordCountHistogram(
                         TAB_COUNT_ON_RETURN, getCurrentTabModel().getCount());
@@ -1071,6 +1068,12 @@
         }
     }
 
+    private boolean shouldShowTabSwitcherOnStart() {
+        long lastBackgroundedTimeMillis = mInactivityTracker.getLastBackgroundedTimeMs();
+        return ReturnToChromeExperimentsUtil.shouldShowTabSwitcher(lastBackgroundedTimeMillis)
+                && isMainIntentFromLauncher(getIntent());
+    }
+
     private boolean isMainIntentFromLauncher(Intent intent) {
         return intent != null && TextUtils.equals(intent.getAction(), Intent.ACTION_MAIN)
                 && intent.hasCategory(Intent.CATEGORY_LAUNCHER);
@@ -1252,6 +1255,10 @@
      * Create an initial tab for cold start without restored tabs.
      */
     private void createInitialTab() {
+        // If the grid tab switcher is enabled and the tab switcher will be shown on start,
+        //  do not create a new tab. With the grid, creating a new tab is now a one tap action.
+        if (shouldShowTabSwitcherOnStart() && FeatureUtilities.isGridTabSwitcherEnabled()) return;
+
         String url = HomepageManager.getHomepageUri();
         if (TextUtils.isEmpty(url)) {
             url = UrlConstants.NTP_URL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index c0a4746..d0916c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -16,6 +16,9 @@
 import android.os.Bundle;
 import android.os.StrictMode;
 import android.support.annotation.IntDef;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.TrustedWebUtils;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CommandLine;
@@ -54,10 +57,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.UUID;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.TrustedWebUtils;
-
 /**
  * Dispatches incoming intents to the appropriate activity based on the current configuration and
  * Intent fired.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
index 70985ed..e24e9a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
@@ -11,6 +11,7 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
@@ -40,8 +41,6 @@
 
 import java.util.List;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Tab Launcher to be used to launch new tabs from background Android Services,
  * when it is not known whether an activity is available. It will send an intent to launch the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
index 57ccd40..c1e0f06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
@@ -9,13 +9,12 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.util.IntentUtils;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * A model class that parses intent from third-party apps for data related with various browser
  * services related Intent types.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
index 0abfec2c..505d60f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
@@ -5,6 +5,9 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.TrustedWebUtils;
 import android.support.v7.app.AppCompatActivity;
 
 import org.chromium.base.Log;
@@ -15,10 +18,6 @@
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.TrustedWebUtils;
-
 /**
  * Launched by {@link android.support.customtabs.TrustedWebUtils#launchBrowserSiteSettings}.
  * Verifies that url provided in intent has valid Digital Asset Link with the calling application,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
index 460629d..741695f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
@@ -11,6 +11,8 @@
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsService.Relation;
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
@@ -46,8 +48,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsService.Relation;
 import dagger.Reusable;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
index 5027891..e860b0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
@@ -5,6 +5,9 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.net.Uri;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.PostMessageBackend;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
@@ -18,10 +21,6 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.PostMessageBackend;
-
 /**
  * A class that handles postMessage communications with a designated {@link CustomTabsSessionToken}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
index 7bffd3b..d819ca9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
@@ -10,6 +10,7 @@
 import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.util.SparseArray;
 
 import org.chromium.base.Callback;
@@ -18,8 +19,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
index 6745237..fcc8cc95 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
@@ -10,10 +10,9 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.widget.RemoteViews;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Interface to handle browser services calls whenever the session id matched.
  * TODO(yusufo): Add a way to handle mayLaunchUrl as well.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
index 3f04db4..7e88948 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
@@ -17,6 +17,9 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.support.annotation.Nullable;
+import android.support.customtabs.trusted.TrustedWebActivityService;
+import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
+import android.support.customtabs.trusted.TrustedWebActivityServiceWrapper;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
@@ -34,10 +37,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import androidx.browser.trusted.TrustedWebActivityService;
-import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
-import androidx.browser.trusted.TrustedWebActivityServiceWrapper;
-
 /**
  * Uses a Trusted Web Activity client to display notifications.
  */
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 3b0db84..ae8dfae 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
@@ -8,6 +8,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsService;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
@@ -40,7 +41,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsService;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
index d774c520..82da66ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
@@ -6,6 +6,7 @@
 
 import android.graphics.Bitmap;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.util.ArrayMap;
 
 import java.util.Collections;
@@ -14,8 +15,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Stores the splash images received from TWA clients between the call to
  * {@link android.support.customtabs.CustomTabsService#receiveFile} and a Trusted Web Activity
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
index 3847f8b1..3472d63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
@@ -13,6 +13,8 @@
 import android.graphics.Matrix;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.customtabs.TrustedWebUtils;
+import android.support.customtabs.TrustedWebUtils.SplashScreenParamKey;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -32,9 +34,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.TrustedWebUtils;
-import androidx.browser.customtabs.TrustedWebUtils.SplashScreenParamKey;
-
 /**
  * Orchestrates the flow of showing and removing splash screens for apps based on Trusted Web
  * Activities.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index 630833556..6b57a5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -15,6 +15,11 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsService.Relation;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.PostMessageServiceConnection;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.SparseBooleanArray;
@@ -45,12 +50,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsService.Relation;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.PostMessageServiceConnection;
-
 /** Manages the clients' state for Custom Tabs. This class is threadsafe. */
 class ClientManager {
     // Values for the "CustomTabs.MayLaunchUrlType" UMA histogram. Append-only.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
index 1362ef9..8970624 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
@@ -13,6 +13,7 @@
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -34,8 +35,6 @@
 import java.util.List;
 import java.util.Set;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Container for all parameters related to creating a customizable button.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 4cbb26c..b8aed09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -4,10 +4,10 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason.USER_NAVIGATION;
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
 
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason.USER_NAVIGATION;
 
 import android.app.Activity;
 import android.app.PendingIntent;
@@ -24,6 +24,9 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -87,10 +90,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * The activity for custom tabs. It will be launched on top of a client's task.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
index 7d532c91..a280b952 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -9,6 +9,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
@@ -36,8 +37,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Delegate that manages bottom bar area inside of {@link CustomTabActivity}.
  */
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 cb865b5..f9c287b3 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
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_SYSTEM;
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_SYSTEM;
 
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
@@ -21,6 +21,10 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabColorSchemeParams;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.TrustedWebUtils;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.View;
@@ -52,11 +56,6 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
-import androidx.browser.customtabs.CustomTabColorSchemeParams;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.TrustedWebUtils;
-
 /**
  * A model class that parses the incoming intent for Custom Tabs specific customization data.
  *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
index e564da8..b4e249a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsSessionToken;
+
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -13,9 +16,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * An observer for firing navigation events on {@link CustomTabsCallback}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
index f7e9540c..7e97613 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -6,6 +6,7 @@
 
 import android.content.Intent;
 import android.support.annotation.NonNull;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.v7.app.AppCompatDelegate;
 
 import org.chromium.base.ObserverList;
@@ -17,8 +18,6 @@
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.IntentUtils;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Maintains and provides the night mode state for {@link CustomTabActivity}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index 0c25cc3..a8224ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -11,6 +11,7 @@
 import android.net.Uri;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 
@@ -33,8 +34,6 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * A {@link TabObserver} that also handles custom tabs specific logging and messaging.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
index dacb04b0..c22ff34c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
@@ -11,6 +11,8 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.WorkerThread;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
@@ -20,8 +22,6 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
 import dagger.Lazy;
 
 /**
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 ae1c4ea..2c602ec 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
@@ -19,6 +19,11 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.PostMessageServiceConnection;
 import android.text.TextUtils;
 import android.widget.RemoteViews;
 
@@ -84,12 +89,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.PostMessageServiceConnection;
-
 /**
  * Implementation of the ICustomTabsService interface.
  *
@@ -345,11 +344,7 @@
                         .onSessionDisconnected(session);
             }
         };
-
-        // TODO(peconn): Make this not an anonymous class once PostMessageServiceConnection is made
-        // non-abstract in AndroidX.
-        PostMessageServiceConnection serviceConnection =
-                new PostMessageServiceConnection(session) {};
+        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(session);
         PostMessageHandler handler = new PostMessageHandler(serviceConnection);
         return mClientManager.newSession(
                 session, Binder.getCallingUid(), onDisconnect, handler, serviceConnection);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
index bfba782e..028c4ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
@@ -10,6 +10,8 @@
 import android.os.IBinder;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.browserservices.Origin;
@@ -18,9 +20,6 @@
 
 import java.util.List;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Custom tabs connection service, used by the embedded Chrome activities.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index 730e0cfac..fb75a3fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -9,6 +9,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
@@ -27,8 +28,6 @@
 import org.chromium.network.mojom.ReferrerPolicy;
 import org.chromium.ui.base.WindowAndroid;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Holds a hidden tab which may be used to preload pages before a CustomTabActivity is launched.
  *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
index 1301ff5..8fa4dee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
@@ -5,14 +5,13 @@
 package org.chromium.chrome.browser.customtabs;
 
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Notifies the provided {@link CustomTabsConnection} of page load metrics, such as time until first
  * contentful paint.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 477747d..8caf375 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -10,6 +10,7 @@
 import android.provider.Browser;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.view.Window;
 
@@ -55,7 +56,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
index 56842ab..05ba38c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
@@ -10,6 +10,7 @@
 import android.content.Intent;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
@@ -18,8 +19,6 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Handles the incoming intents: the one that starts the activity, as well as subsequent intents
  * received in onNewIntent.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
index 435fec3..4a3be88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
@@ -6,8 +6,7 @@
 
 import android.content.Context;
 import android.os.Bundle;
-
-import androidx.browser.customtabs.PostMessageBackend;
+import android.support.customtabs.PostMessageBackend;
 
 /**
  * A {@link PostMessageBackend} which delegates incoming notifications to the {@link
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 f4b2453d..dd0cf78 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
@@ -11,6 +11,8 @@
 import android.net.Uri;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.PostMessageBackend;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,8 +55,6 @@
 
 import javax.inject.Inject;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.PostMessageBackend;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
index aa07693b..ecaf288 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
@@ -4,16 +4,17 @@
 
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
-import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_FAILED;
-import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_FINISHED;
-import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_STARTED;
-import static androidx.browser.customtabs.CustomTabsCallback.TAB_HIDDEN;
-import static androidx.browser.customtabs.CustomTabsCallback.TAB_SHOWN;
+import static android.support.customtabs.CustomTabsCallback.NAVIGATION_FAILED;
+import static android.support.customtabs.CustomTabsCallback.NAVIGATION_FINISHED;
+import static android.support.customtabs.CustomTabsCallback.NAVIGATION_STARTED;
+import static android.support.customtabs.CustomTabsCallback.TAB_HIDDEN;
+import static android.support.customtabs.CustomTabsCallback.TAB_SHOWN;
 
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsCallback;
 import android.text.TextUtils;
 
 import org.chromium.base.VisibleForTesting;
@@ -27,8 +28,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-
 /**
  * An observer for firing navigation events to the CCT dynamic module.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
index eb98eff9..deb2a5e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT;
 
 import android.content.Context;
+import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 import android.view.View;
 
@@ -38,7 +39,6 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import androidx.browser.customtabs.CustomTabsIntent;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
index 3cd42a5..76cf447f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
@@ -8,6 +8,7 @@
 import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.LAST_USED_PROFILE;
 
 import android.content.Context;
+import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.WarmupManager;
@@ -22,7 +23,6 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
 import dagger.Module;
 import dagger.Provides;
 
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 75e9ecb..66a1d84 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
@@ -8,6 +8,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
@@ -43,8 +44,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Manages UI effects for reader mode including hiding and showing the
  * reader mode and reader mode preferences toolbar icon and hiding the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
index 7fcad25..a6662256 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
@@ -11,12 +11,14 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.sync.SyncAndServicesPreferences;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInCallback;
 import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 
 /**
@@ -79,6 +81,9 @@
                 // Show sync settings if user pressed the "Settings" button.
                 if (setUp) {
                     openSignInSettings(activity);
+                } else if (ChromeFeatureList.isEnabled(
+                                   ChromeFeatureList.SYNC_MANUAL_START_ANDROID)) {
+                    ProfileSyncService.get().setFirstSetupComplete();
                 }
                 setFirstRunFlowSignInComplete(true);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
index dae6e65a..1a7c05a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
@@ -17,6 +17,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.provider.Browser;
+import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -32,8 +33,6 @@
 
 import java.util.Locale;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * A class containing some utility static methods.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
index 0c8c0245..824c255 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
@@ -10,6 +10,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Browser;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
@@ -40,8 +41,6 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.widget.Toast;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Serves as an interface between Download Home UI and offline page related items that are to be
  * displayed in the downloads UI.
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 2f7bf40..cc6e303 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
@@ -131,7 +131,7 @@
             if (suggestion.isStarred()) {
                 return SuggestionIcon.BOOKMARK;
             } else if (suggestion.getType() == OmniboxSuggestionType.HISTORY_URL) {
-                return mEnableSuggestionFavicons ? SuggestionIcon.GLOBE : SuggestionIcon.HISTORY;
+                return SuggestionIcon.HISTORY;
             } else {
                 return SuggestionIcon.GLOBE;
             }
@@ -142,8 +142,7 @@
 
                 case OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED:
                 case OmniboxSuggestionType.SEARCH_HISTORY:
-                    return mEnableSuggestionFavicons ? SuggestionIcon.MAGNIFIER
-                                                     : SuggestionIcon.HISTORY;
+                    return SuggestionIcon.HISTORY;
 
                 default:
                     return SuggestionIcon.MAGNIFIER;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
index 781fc41..b67de7cd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
@@ -602,6 +602,9 @@
 
     private void confirmSettings() {
         RecordUserAction.record("Signin_Signin_ConfirmAdvancedSyncSettings");
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.SYNC_MANUAL_START_ANDROID)) {
+            ProfileSyncService.get().setFirstSetupComplete();
+        }
         UnifiedConsentServiceBridge.recordSyncSetupDataTypesHistogram();
         // Settings will be applied when mSyncSetupInProgressHandle is released in onDestroy.
         getActivity().finish();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
index 361d772..bc320d8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
@@ -11,6 +11,7 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceFragmentCompat;
 
@@ -29,8 +30,6 @@
 import org.chromium.components.sync.StopSource;
 import org.chromium.ui.UiUtils;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Helper methods for sync preferences.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
index 5e65be5a..b505c73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
@@ -12,8 +12,10 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.sync.SyncAndServicesPreferences;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -137,6 +139,9 @@
                             PreferencesLauncher.launchSettingsPage(getActivity(),
                                     SyncAndServicesPreferences.class,
                                     SyncAndServicesPreferences.createArguments(true));
+                        } else if (ChromeFeatureList.isEnabled(
+                                           ChromeFeatureList.SYNC_MANUAL_START_ANDROID)) {
+                            ProfileSyncService.get().setFirstSetupComplete();
                         }
 
                         recordSigninCompletedHistogramAccountInfo();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index d14bb2f0..da6e38b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -12,6 +12,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.Passphrase;
 
@@ -451,8 +452,10 @@
             assert mSetupInProgressCounter > 0;
             if (--mSetupInProgressCounter == 0) {
                 setSetupInProgress(false);
-                // The user has finished setting up sync at least once.
-                setFirstSetupComplete();
+                if (!ChromeFeatureList.isEnabled(ChromeFeatureList.SYNC_MANUAL_START_ANDROID)) {
+                    // The user has finished setting up sync at least once.
+                    setFirstSetupComplete();
+                }
             }
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
index b714c536..6692173 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
@@ -13,6 +13,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v7.app.AlertDialog;
@@ -42,8 +43,6 @@
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Dialog to ask to user to enter their sync passphrase.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
index 2f26f0f..3f337695 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
@@ -10,6 +10,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
 import android.text.SpannableString;
@@ -38,8 +39,6 @@
 import java.util.Date;
 import java.util.List;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Dialog to ask the user select what type of password to use for encryption.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index bee31fac..63a844a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -11,6 +11,7 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.provider.ContactsContract;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordUserAction;
@@ -36,8 +37,6 @@
 import java.net.URI;
 import java.util.Locale;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * A default {@link ContextMenuItemDelegate} that supports the context menu functionality in Tab.
  */
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 400be28..98e684b2 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
@@ -68,12 +68,12 @@
         mSuspensionTracker = new SuspensionTracker(mBridge, mNotificationSuspender);
         mTokenTracker = new TokenTracker(mBridge);
         mPageViewObservers = new ArrayList<>();
+        mClient = AppHooks.get().createDigitalWellbeingClient();
 
         mSuspensionTracker.getAllSuspendedWebsites().then(
                 (suspendedSites) -> { notifyObserversOfSuspensions(suspendedSites, true); });
 
         mOptInState = getOptInState();
-        mClient = AppHooks.get().createDigitalWellbeingClient();
     }
 
     /* package */ NotificationSuspender getNotificationSuspender() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
index d3fb555..13415c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashController.java
@@ -166,6 +166,11 @@
         return mSplashView;
     }
 
+    @VisibleForTesting
+    public boolean wasSplashScreenHiddenForTests() {
+        return mSplashShownTimestamp > 0 && mSplashView == null;
+    }
+
     @Override
     public void onPreInflationStartup() {
         mDidPreInflationStartup = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index c467487..fc79a59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -814,8 +814,8 @@
     }
 
     @VisibleForTesting
-    View getSplashScreenForTests() {
-        return mSplashController.getSplashScreenForTests();
+    SplashController getSplashControllerForTests() {
+        return mSplashController;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
index d327fd70..49cda5b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
@@ -10,6 +10,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
@@ -22,8 +23,6 @@
 import org.chromium.content_public.common.ScreenOrientationValues;
 import org.chromium.webapk.lib.common.splash.SplashLayout;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Stores info about a web app.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
index b1e0f83..be2e6e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
@@ -9,6 +9,7 @@
 import android.net.Uri;
 import android.os.StrictMode;
 import android.provider.Browser;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -26,8 +27,6 @@
 import java.net.URISyntaxException;
 import java.util.List;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Asynchronously creates Tabs for navigation originating from an installed PWA.
  *
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
index 02e5753e..310017d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.net.Uri;
+import android.support.customtabs.CustomTabsService;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -36,8 +37,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsService;
-
 /** Tests for OriginVerifier. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index ccf87f7..00364e5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -13,6 +13,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ServiceTestRule;
@@ -37,8 +38,6 @@
 
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
-
 /**
  * Tests the TrustedWebActivityClient.
  *
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
index c719db2..3ee1926 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
@@ -8,6 +8,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.TrustedWebUtils;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 
@@ -34,10 +37,6 @@
 
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.TrustedWebUtils;
-
 /**
  * Instrumentation tests for launching
  * {@link org.chromium.chrome.browser.customtabs.CustomTabActivity} in Trusted Web Activity Mode.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index b9fc507..df2f619 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -7,6 +7,9 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Process;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSessionToken;
+import android.support.customtabs.PostMessageServiceConnection;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -29,10 +32,6 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.PostMessageServiceConnection;
-
 /** Tests for ClientManager. */
 @RunWith(BaseJUnit4ClassRunner.class)
 public class ClientManagerTest {
@@ -174,10 +173,7 @@
     @SmallTest
     public void testPostMessageOriginVerification() {
         final ClientManager cm = mClientManager;
-        // TODO(peconn): Get rid of this anonymous class once PostMessageServiceConnection is made
-        // non-abstract. Same with the other occurrences below.
-        PostMessageServiceConnection serviceConnection =
-                new PostMessageServiceConnection(mSession) {};
+        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
@@ -220,8 +216,7 @@
     @SmallTest
     public void testPostMessageOriginDifferentRelations() {
         final ClientManager cm = mClientManager;
-        PostMessageServiceConnection serviceConnection =
-                new PostMessageServiceConnection(mSession) {};
+        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
@@ -260,8 +255,7 @@
     @SmallTest
     public void testPostMessageOriginHttpNotAllowed() {
         final ClientManager cm = mClientManager;
-        PostMessageServiceConnection serviceConnection =
-                new PostMessageServiceConnection(mSession) {};
+        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index 5b5b132..cf1768ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -6,6 +6,7 @@
 
 import android.content.Intent;
 import android.graphics.Color;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.view.MenuItem;
@@ -31,8 +32,6 @@
 
 import java.util.concurrent.ExecutionException;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Instrumentation tests for {@link CustomTabActivity} launched in incognito mode.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 6761dd7..2dd5efa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -34,6 +34,11 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.DrawableRes;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSession;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
@@ -138,12 +143,6 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Instrumentation tests for app menu, context menu, and toolbar of a {@link CustomTabActivity}.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
index ec03c34..e63f864 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
@@ -13,6 +13,12 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsClient;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsServiceConnection;
+import android.support.customtabs.CustomTabsSession;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -45,13 +51,6 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsClient;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsServiceConnection;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /** Tests for CustomTabsConnection. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class CustomTabsConnectionTest {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
index 86e2116e..2c9929c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
@@ -10,6 +10,12 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Process;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsClient;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsServiceConnection;
+import android.support.customtabs.CustomTabsSession;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 
 import org.junit.Assert;
@@ -25,13 +31,6 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsClient;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsServiceConnection;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Utility class that contains convenience calls related with custom tabs testing.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 6734ed1..0945878 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -9,6 +9,11 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsSession;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -48,12 +53,6 @@
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /** Tests for detached resource requests. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class DetachedResourceRequestTest {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
index 47545e7c..a031eaa5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
@@ -12,6 +12,8 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.util.Pair;
@@ -67,9 +69,6 @@
 import java.util.Locale;
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * Instrumentation tests for showing the publisher URL for a trusted CDN.
  */
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 12963bdf5..6fb583bb 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
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
 import android.content.Intent;
+import android.support.customtabs.CustomTabsCallback;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -47,8 +48,6 @@
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-
 /**
  * Instrumentation tests for the CustomTabsDynamicModuleNavigationObserver.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
index d9443ba..f8e36c76 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
@@ -12,6 +12,8 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.PostMessageBackend;
 import android.support.test.filters.SmallTest;
 
 import org.junit.After;
@@ -39,9 +41,6 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.net.test.util.TestWebServer;
 
-import androidx.browser.customtabs.CustomTabsService;
-import androidx.browser.customtabs.PostMessageBackend;
-
 /**
  * Instrumentation tests for the CCT Dynamic Module post message API.
  */
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 f791d70..ff4bca4 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
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
+import android.support.customtabs.CustomTabsCallback;
 import android.support.test.InstrumentationRegistry;
 
 import org.junit.Assert;
@@ -35,8 +36,6 @@
 import java.io.InputStream;
 import java.util.concurrent.TimeoutException;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-
 /**
  * Utility class that contains fake CCT dynamic module classes and convenience calls
  * related with CCT dynamic module testing.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
index 1961cdd..ab7b3b01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -16,6 +16,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.provider.Browser;
+import android.support.customtabs.CustomTabsIntent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.test.mock.MockPackageManager;
@@ -51,8 +52,6 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Instrumentation tests for {@link ExternalNavigationHandler}.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java
index 5c0bdfa9..b757859 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java
@@ -42,7 +42,6 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.common.ContentSwitches;
-import org.chromium.net.test.EmbeddedTestServerRule;
 import org.chromium.webapk.lib.client.WebApkValidator;
 import org.chromium.webapk.lib.common.WebApkConstants;
 
@@ -55,8 +54,6 @@
 
     public final NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
 
-    public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
-
     public MockCertVerifierRuleAndroid mCertVerifierRule =
             new MockCertVerifierRuleAndroid(mNativeLibraryTestRule, 0 /* net::OK */);
 
@@ -64,8 +61,7 @@
     public RuleChain mRuleChain = RuleChain.emptyRuleChain()
                                           .around(mActivityTestRule)
                                           .around(mNativeLibraryTestRule)
-                                          .around(mCertVerifierRule)
-                                          .around(mTestServerRule);
+                                          .around(mCertVerifierRule);
 
     private static final long STARTUP_TIMEOUT = ScalableTimeout.scaleTimeout(10000);
 
@@ -100,7 +96,9 @@
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
-                return mActivityTestRule.getActivity().getSplashScreenForTests() == null;
+                return mActivityTestRule.getActivity()
+                        .getSplashControllerForTests()
+                        .wasSplashScreenHiddenForTests();
             }
         });
     }
@@ -121,8 +119,9 @@
     @Before
     public void setUp() throws Exception {
         WebApkUpdateManager.setUpdatesEnabledForTesting(false);
-        mTestServerRule.setServerUsesHttps(true);
-        Uri mapToUri = Uri.parse(mTestServerRule.getServer().getURL("/"));
+        mActivityTestRule.getEmbeddedTestServerRule().setServerUsesHttps(true);
+        Uri mapToUri =
+                Uri.parse(mActivityTestRule.getEmbeddedTestServerRule().getServer().getURL("/"));
         CommandLine.getInstance().appendSwitchWithValue(
                 ContentSwitches.HOST_RESOLVER_RULES, "MAP * " + mapToUri.getAuthority());
         WebApkValidator.disableValidationForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
index 38d5d314..e589293 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
@@ -8,6 +8,7 @@
 
 import android.content.Intent;
 import android.net.Uri;
+import android.support.customtabs.TrustedWebUtils;
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,8 +29,6 @@
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-import androidx.browser.customtabs.TrustedWebUtils;
-
 /**
  * Custom {@link ChromeActivityTestRule} for tests using {@link WebappActivity}.
  */
@@ -225,7 +224,8 @@
                 // We also wait till the splash screen has finished initializing.
                 if (getActivity().getActivityTab() == null) return false;
 
-                View splashScreen = getActivity().getSplashScreenForTests();
+                View splashScreen =
+                        getActivity().getSplashControllerForTests().getSplashScreenForTests();
                 if (splashScreen == null) return false;
 
                 return (!(splashScreen instanceof ViewGroup)
@@ -234,10 +234,9 @@
         }, STARTUP_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
 
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        View splashScreen = getActivity().getSplashScreenForTests();
-        if (splashScreen == null) {
-            Assert.fail("No splash screen available.");
-        }
+        View splashScreen = getActivity().getSplashControllerForTests().getSplashScreenForTests();
+        Assert.assertNotNull("No splash screen available.", splashScreen);
+
         // TODO(pkotwicz): Change return type in order to accommodate new-style WebAPKs.
         // (crbug.com/958288)
         return (splashScreen instanceof ViewGroup) ? (ViewGroup) splashScreen : null;
@@ -256,6 +255,6 @@
     }
 
     public boolean isSplashScreenVisible() {
-        return getActivity().getSplashScreenForTests() != null;
+        return getActivity().getSplashControllerForTests().getSplashScreenForTests() != null;
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
index 7d18a33..2297878 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
@@ -12,6 +12,8 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsSessionToken;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -29,9 +31,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.customtabs.TranslucentCustomTabActivity;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /** Unit tests for {@link SessionDataHolder}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index dd1ee52..3176221 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -15,6 +15,9 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
+import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback;
+import android.support.customtabs.trusted.TrustedWebActivityServiceWrapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -29,10 +32,6 @@
 import org.chromium.chrome.browser.notifications.NotificationBuilderBase;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 
-import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
-import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback;
-import androidx.browser.trusted.TrustedWebActivityServiceWrapper;
-
 /**
  * Unit tests for {@link TrustedWebActivityClient}.
  */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
index 13e33886..836d35c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
@@ -4,12 +4,14 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
+import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+
 import static org.junit.Assert.assertEquals;
 
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
-import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
-
 import android.content.Intent;
+import android.support.customtabs.CustomTabColorSchemeParams;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -18,9 +20,6 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
-import androidx.browser.customtabs.CustomTabColorSchemeParams;
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /** Tests for {@link CustomTabIntentDataProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 4b3f827..726128a8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -13,6 +13,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.customtabs.CustomTabsSessionToken;
 import android.view.View;
 
 import org.junit.rules.TestWatcher;
@@ -54,8 +55,6 @@
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
 
-import androidx.browser.customtabs.CustomTabsSessionToken;
-
 /**
  * A TestRule that sets up the mocks and contains helper methods for JUnit/Robolectric tests scoped
  * to the content layer of Custom Tabs code.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FutureTaskConsumerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FutureTaskConsumerTest.java
new file mode 100644
index 0000000..0f575ae
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FutureTaskConsumerTest.java
@@ -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.
+
+package org.chromium.chrome.browser.feed;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+
+import com.google.android.libraries.feed.common.functional.Consumer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link FutureTaskConsumer}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class FutureTaskConsumerTest {
+    @Test
+    @SmallTest
+    public void testConsume() {
+        Integer expected = 42;
+        Integer failure = -1;
+
+        Callback<Consumer<Integer>> callback = new Callback<Consumer<Integer>>() {
+            @Override
+            public void onResult(Consumer<Integer> result) {
+                result.accept(expected);
+            }
+        };
+
+        Integer actual = FutureTaskConsumer.consume("", callback, failure);
+
+        assertEquals(expected, actual);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
index d2806eb..e1317c0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
@@ -11,6 +11,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserManager;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -42,8 +43,6 @@
 import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
 import org.chromium.webapk.test.WebApkTestHelper;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /** JUnit tests for first run triggering code. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
index 8c041a3..205da3b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java
@@ -11,20 +11,15 @@
 
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -85,8 +80,6 @@
     Resources mResources;
     @Mock
     SuggestionHost mSuggestionHost;
-    @Rule
-    public TestRule mFeatureProcessor = new Features.JUnitProcessor();
 
     @Before
     public void setUp() {
@@ -156,39 +149,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    public void getSuggestionIconTypeForSearch_FavIcons() {
-        mProcessor.onNativeInitialized();
-        int[][] testSuites = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.PEDAL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_TEXT, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.CLIPBOARD_IMAGE, SuggestionIcon.MAGNIFIER},
-        };
-
-        for (int[] test : testSuites) {
-            assertSuggestionIconTypeIs(createSearchSuggestion(test[0]), test[1]);
-        }
-    }
-
-    @Test
     public void getSuggestionIconTypeForUrl_Default() {
         int[][] testSuites = {
                 {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.GLOBE},
@@ -220,39 +180,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    public void getSuggestionIconTypeForUrl_FavIcons() {
-        mProcessor.onNativeInitialized();
-        int[][] testSuites = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.PEDAL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.CLIPBOARD_TEXT, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.CLIPBOARD_IMAGE, SuggestionIcon.GLOBE},
-        };
-
-        for (int[] test : testSuites) {
-            assertSuggestionIconTypeIs(createUrlSuggestion(test[0]), test[1]);
-        }
-    }
-
-    @Test
     public void getSuggestionIconTypeForBookmarks_Default() {
         int[][] testSuites = {
                 {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.BOOKMARK},
@@ -282,37 +209,4 @@
             assertSuggestionIconTypeIs(createBookmarkSuggestion(test[0]), test[1]);
         }
     }
-
-    @Test
-    @EnableFeatures(ChromeFeatureList.OMNIBOX_SHOW_SUGGESTION_FAVICONS)
-    public void getSuggestionIconTypeForBookmarks_FavIcons() {
-        mProcessor.onNativeInitialized();
-        int[][] testSuites = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.CLIPBOARD_URL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.PEDAL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.CLIPBOARD_TEXT, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.CLIPBOARD_IMAGE, SuggestionIcon.BOOKMARK},
-        };
-
-        for (int[] test : testSuites) {
-            assertSuggestionIconTypeIs(createBookmarkSuggestion(test[0]), test[1]);
-        }
-    }
 }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
index 92a7084..b26af1a 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
@@ -6,6 +6,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Browser;
+import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.IntentHandler;
@@ -19,8 +20,6 @@
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
 import org.chromium.chrome.browser.util.UrlUtilities;
 
-import androidx.browser.customtabs.CustomTabsIntent;
-
 /**
  * Asynchronously creates Tabs for navigation originating from {@link NoTouchActivity}.
  *
diff --git a/chrome/app/OWNERS b/chrome/app/OWNERS
index 2b82288..423a023 100644
--- a/chrome/app/OWNERS
+++ b/chrome/app/OWNERS
@@ -19,6 +19,8 @@
 
 per-file app_management_strings.grdp=file://chrome/browser/ui/webui/app_management/OWNERS
 
+per-file global_media_controls_strings.grdp=file://chrome/browser/ui/global_media_controls/OWNERS
+
 per-file media_router_strings.grdp=file://chrome/browser/media/router/OWNERS
 
 per-file onboarding_welcome_strings.grdp=file://chrome/browser/ui/webui/welcome/OWNERS
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 03f3c74..f569e738 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -194,6 +194,9 @@
       <!-- Media Router specific strings -->
       <part file="media_router_strings.grdp" />
 
+      <!-- Global Media Controls specific strings -->
+      <part file="global_media_controls_strings.grdp" />
+
       <!-- Profiles specific strings -->
       <part file="profiles_strings.grdp" />
 
diff --git a/chrome/app/global_media_controls_strings.grdp b/chrome/app/global_media_controls_strings.grdp
new file mode 100644
index 0000000..4ee94f6
--- /dev/null
+++ b/chrome/app/global_media_controls_strings.grdp
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Global Media Controls-specific strings (included from generated_resources.grd). -->
+<grit-part>
+  <message name="IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT" desc="Tooltip for the Global Media Controls icon, which appears in the toolbar. The tooltip appears on mouseover of the icon.">
+   Global Media Controls
+  </message>
+</grit-part>
diff --git a/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT.png.sha1 b/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT.png.sha1
new file mode 100644
index 0000000..78b8e905
--- /dev/null
+++ b/chrome/app/global_media_controls_strings_grdp/IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT.png.sha1
@@ -0,0 +1 @@
+6c90b12caf43ac61e604d1b867dfc10e0dbbb9b2
\ No newline at end of file
diff --git a/chrome/app/global_media_controls_strings_grdp/OWNERS b/chrome/app/global_media_controls_strings_grdp/OWNERS
new file mode 100644
index 0000000..399a33f
--- /dev/null
+++ b/chrome/app/global_media_controls_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/global_media_controls/OWNERS
diff --git a/chrome/app/global_media_controls_strings_grdp/README.md b/chrome/app/global_media_controls_strings_grdp/README.md
new file mode 100644
index 0000000..f013bb5
--- /dev/null
+++ b/chrome/app/global_media_controls_strings_grdp/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5793ae2..09dffde 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1067,10 +1067,6 @@
     {"predictor", ui::input_prediction::kScrollPredictorNameLsq}};
 const FeatureEntry::FeatureParam kResamplingInputEventsKalmanEnabled[] = {
     {"predictor", ui::input_prediction::kScrollPredictorNameKalman}};
-const FeatureEntry::FeatureParam
-    kResamplingInputEventsKalmanTimeFilteredEnabled[] = {
-        {"predictor",
-         ui::input_prediction::kScrollPredictorNameKalmanTimeFiltered}};
 const FeatureEntry::FeatureParam kResamplingInputEventsLinearFirstEnabled[] = {
     {"predictor", ui::input_prediction::kScrollPredictorNameLinearFirst}};
 const FeatureEntry::FeatureParam kResamplingInputEventsLinearSecondEnabled[] = {
@@ -1083,9 +1079,6 @@
      {ui::input_prediction::kScrollPredictorNameKalman,
       kResamplingInputEventsKalmanEnabled,
       base::size(kResamplingInputEventsKalmanEnabled), nullptr},
-     {ui::input_prediction::kScrollPredictorNameKalmanTimeFiltered,
-      kResamplingInputEventsKalmanTimeFilteredEnabled,
-      base::size(kResamplingInputEventsKalmanTimeFilteredEnabled), nullptr},
      {ui::input_prediction::kScrollPredictorNameLinearFirst,
       kResamplingInputEventsLinearFirstEnabled,
       base::size(kResamplingInputEventsLinearFirstEnabled), nullptr},
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9bf0dad..5c315f9 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -848,25 +848,6 @@
     return crash_handler->GetDeathSignalSocket();
   }
 
-#if defined(OS_CHROMEOS)
-  // Mash services are utility processes, but crashes are reported using the
-  // service name as the process type to make the crash console easier to read.
-  if (command_line.HasSwitch(switches::kMashServiceName)) {
-    static base::NoDestructor<
-        std::map<std::string, breakpad::CrashHandlerHostLinux*>>
-        crash_handlers;
-    std::string service_name =
-        command_line.GetSwitchValueASCII(switches::kMashServiceName);
-    auto it = crash_handlers->find(service_name);
-    if (it == crash_handlers->end()) {
-      auto insert_result = crash_handlers->insert(
-          std::make_pair(service_name, CreateCrashHandlerHost(service_name)));
-      it = insert_result.first;
-    }
-    return it->second->GetDeathSignalSocket();
-  }
-#endif  // defined(OS_CHROMEOS)
-
   std::string process_type =
       command_line.GetSwitchValueASCII(switches::kProcessType);
 
diff --git a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_installer_utils_unittest.cc b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_installer_utils_unittest.cc
index b1078c8..7a2eb4e8 100644
--- a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_installer_utils_unittest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_cert_installer_utils_unittest.cc
@@ -150,6 +150,6 @@
                          ArcCertInstallerUtilsTest,
                          testing::Values("",
                                          "name of the smart card",
-                                         std::string("A", 2048)));
+                                         std::string(2048, 'A')));
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.h b/chrome/browser/chromeos/login/lock/screen_locker.h
index ca846c6..69587c9 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/screen_locker.h
@@ -207,7 +207,7 @@
   // lock request is failed.
   void OnStartLockCallback(bool locked);
 
-  // Callback to be invoked when the |cert_provider_based_auth_preparer_|
+  // Callback to be invoked when the |challenge_response_auth_keys_loader_|
   // completes building the currently available challenge-response keys. Used
   // only during the challenge-response unlock.
   void OnChallengeResponseKeysPrepared(
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
index 24c3788a..820763f2 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/authpolicy/authpolicy_helper.h"
 #include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
+#include "chrome/browser/chromeos/login/challenge_response_auth_keys_loader.h"
 #include "chrome/browser/chromeos/login/lock_screen_utils.h"
 #include "chrome/browser/chromeos/login/mojo_system_info_dispatcher.h"
 #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
@@ -35,6 +36,7 @@
 #include "chromeos/components/proximity_auth/screenlock_bridge.h"
 #include "chromeos/dbus/media_perception/media_perception.pb.h"
 #include "components/user_manager/known_user.h"
+#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
@@ -105,14 +107,6 @@
   input_method::InputMethodManager::Get()->GetImeKeyboard()->SetCapsLockEnabled(
       false);
 
-  // Enable pin for any users who can use it.
-  if (user_manager::UserManager::IsInitialized()) {
-    for (user_manager::User* user :
-         user_manager::UserManager::Get()->GetLoggedInUsers()) {
-      UpdatePinKeyboardState(user->GetAccountId());
-    }
-  }
-
   system_info_updater_->StartRequest();
 
   ash::LoginScreen::Get()->GetModel()->SetUserList(
@@ -120,15 +114,12 @@
   ash::LoginScreen::Get()->SetAllowLoginAsGuest(false /*show_guest*/);
 
   if (user_manager::UserManager::IsInitialized()) {
+    // Enable pin and challenge-response authentication for any users who can
+    // use them.
     for (user_manager::User* user :
          user_manager::UserManager::Get()->GetLoggedInUsers()) {
-      const bool enable_challenge_response =
-          ChallengeResponseAuthKeysLoader::CanAuthenticateUser(
-              user->GetAccountId());
-      ash::LoginScreen::Get()
-          ->GetModel()
-          ->SetChallengeResponseAuthEnabledForUser(user->GetAccountId(),
-                                                   enable_challenge_response);
+      UpdatePinKeyboardState(user->GetAccountId());
+      UpdateChallengeResponseAuthAvailability(user->GetAccountId());
     }
   }
 
@@ -335,6 +326,14 @@
                                  weak_factory_.GetWeakPtr(), account_id));
 }
 
+void ViewsScreenLocker::UpdateChallengeResponseAuthAvailability(
+    const AccountId& account_id) {
+  const bool enable_challenge_response =
+      ChallengeResponseAuthKeysLoader::CanAuthenticateUser(account_id);
+  ash::LoginScreen::Get()->GetModel()->SetChallengeResponseAuthEnabledForUser(
+      account_id, enable_challenge_response);
+}
+
 void ViewsScreenLocker::OnAllowedInputMethodsChanged() {
   if (focused_pod_account_id_) {
     std::string user_input_method = lock_screen_utils::GetUserLastInputMethod(
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.h b/chrome/browser/chromeos/login/lock/views_screen_locker.h
index fa6e528c..e5e015e 100644
--- a/chrome/browser/chromeos/login/lock/views_screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/views_screen_locker.h
@@ -83,6 +83,7 @@
 
  private:
   void UpdatePinKeyboardState(const AccountId& account_id);
+  void UpdateChallengeResponseAuthAvailability(const AccountId& account_id);
   void OnAllowedInputMethodsChanged();
   void OnPinCanAuthenticate(const AccountId& account_id, bool can_authenticate);
   void OnExternalBinaryAuthTimeout();
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
index 96dc935..5de9007 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/chromeos/login/ui/login_display_host_mojo.h"
 
-#include <string>
 #include <utility>
 
 #include "ash/public/cpp/login_screen.h"
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
 #include "chrome/browser/chromeos/login/mojo_system_info_dispatcher.h"
@@ -22,6 +22,8 @@
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chromeos/login/auth/user_context.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
@@ -352,8 +354,18 @@
 void LoginDisplayHostMojo::HandleAuthenticateUserWithChallengeResponse(
     const AccountId& account_id,
     base::OnceCallback<void(bool)> callback) {
-  // TODO(crbug.com/826417): Implement the challenge-response system for login.
-  std::move(callback).Run(false);
+  if (!ChallengeResponseAuthKeysLoader::CanAuthenticateUser(account_id)) {
+    LOG(ERROR)
+        << "Challenge-response authentication isn't supported for the user";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  challenge_response_auth_keys_loader_.LoadAvailableKeys(
+      account_id,
+      base::BindOnce(&LoginDisplayHostMojo::OnChallengeResponseKeysPrepared,
+                     weak_factory_.GetWeakPtr(), account_id,
+                     std::move(callback)));
 }
 
 void LoginDisplayHostMojo::HandleHardlockPod(const AccountId& account_id) {
@@ -428,4 +440,28 @@
       login_display_.get());
 }
 
+void LoginDisplayHostMojo::OnChallengeResponseKeysPrepared(
+    const AccountId& account_id,
+    base::OnceCallback<void(bool)> on_auth_complete_callback,
+    std::vector<ChallengeResponseKey> challenge_response_keys) {
+  if (challenge_response_keys.empty()) {
+    // TODO(crbug.com/826417): Indicate the error in the UI.
+    std::move(on_auth_complete_callback).Run(false);
+    return;
+  }
+
+  CHECK(!pending_auth_state_);
+  pending_auth_state_ = std::make_unique<AuthState>(
+      account_id, std::move(on_auth_complete_callback));
+
+  const user_manager::User* const user =
+      user_manager::UserManager::Get()->FindUser(account_id);
+  DCHECK(user);
+  UserContext user_context(*user);
+  *user_context.GetMutableChallengeResponseKeys() =
+      std::move(challenge_response_keys);
+
+  existing_user_controller_->Login(user_context, chromeos::SigninSpecifics());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
index 3ec24b3..946912d0 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
@@ -9,13 +9,16 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "chrome/browser/chromeos/login/challenge_response_auth_keys_loader.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host_common.h"
 #include "chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
 #include "chromeos/login/auth/auth_status_consumer.h"
+#include "chromeos/login/auth/challenge_response_key.h"
 
 namespace chromeos {
 
@@ -125,6 +128,14 @@
  private:
   void LoadOobeDialog();
 
+  // Callback to be invoked when the |challenge_response_auth_keys_loader_|
+  // completes building the currently available challenge-response keys. Used
+  // only during the challenge-response authentication.
+  void OnChallengeResponseKeysPrepared(
+      const AccountId& account_id,
+      base::OnceCallback<void(bool)> on_auth_complete_callback,
+      std::vector<ChallengeResponseKey> challenge_response_keys);
+
   // State associated with a pending authentication attempt.
   struct AuthState {
     AuthState(AccountId account_id, base::OnceCallback<void(bool)> callback);
@@ -165,6 +176,8 @@
   // first OnStartSigninScreen and remains true afterward.
   bool signin_screen_started_ = false;
 
+  ChallengeResponseAuthKeysLoader challenge_response_auth_keys_loader_;
+
   base::WeakPtrFactory<LoginDisplayHostMojo> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginDisplayHostMojo);
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
index ec94c87..ccd6e6d 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/login/challenge_response_auth_keys_loader.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
 #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
 #include "chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h"
@@ -49,6 +50,14 @@
                                  weak_factory_.GetWeakPtr(), account_id));
 }
 
+void LoginDisplayMojo::UpdateChallengeResponseAuthAvailability(
+    const AccountId& account_id) {
+  const bool enable_challenge_response =
+      ChallengeResponseAuthKeysLoader::CanAuthenticateUser(account_id);
+  ash::LoginScreen::Get()->GetModel()->SetChallengeResponseAuthEnabledForUser(
+      account_id, enable_challenge_response);
+}
+
 void LoginDisplayMojo::ClearAndEnablePassword() {}
 
 void LoginDisplayMojo::Init(const user_manager::UserList& filtered_users,
@@ -72,10 +81,12 @@
   ash::LoginScreen::Get()->SetAllowLoginAsGuest(show_guest);
   user_selection_screen->SetUsersLoaded(true /*loaded*/);
 
-  // Enable pin for any users who can use it.
   if (user_manager::UserManager::IsInitialized()) {
+    // Enable pin and challenge-response authentication for any users who can
+    // use them.
     for (const user_manager::User* user : filtered_users) {
       UpdatePinKeyboardState(user->GetAccountId());
+      UpdateChallengeResponseAuthAvailability(user->GetAccountId());
     }
   }
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.h b/chrome/browser/chromeos/login/ui/login_display_mojo.h
index 6ba39f3..3588f11 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.h
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.h
@@ -27,8 +27,9 @@
   explicit LoginDisplayMojo(LoginDisplayHostMojo* host);
   ~LoginDisplayMojo() override;
 
-  // Updates the state of the PIN keyboard.
+  // Updates the state of the authentication methods supported for the user.
   void UpdatePinKeyboardState(const AccountId& account_id);
+  void UpdateChallengeResponseAuthAvailability(const AccountId& account_id);
 
   // LoginDisplay:
   void ClearAndEnablePassword() override;
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager.h b/chrome/browser/chromeos/printing/cups_print_job_manager.h
index 701ab90..2a0af18b 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager.h
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager.h
@@ -23,7 +23,7 @@
 
 class CupsPrintJobManager : public KeyedService {
  public:
-  class Observer {
+  class Observer : public base::CheckedObserver {
    public:
     virtual void OnPrintJobCreated(base::WeakPtr<CupsPrintJob> job) {}
     virtual void OnPrintJobStarted(base::WeakPtr<CupsPrintJob> job) {}
@@ -44,7 +44,7 @@
     virtual void OnPrintJobCancelled(base::WeakPtr<CupsPrintJob> job) {}
 
    protected:
-    virtual ~Observer() {}
+    ~Observer() override {}
   };
 
   static CupsPrintJobManager* CreateInstance(Profile* profile);
@@ -80,7 +80,7 @@
   void RecordJobDuration(base::WeakPtr<CupsPrintJob> job);
 
   std::unique_ptr<CupsPrintJobNotificationManager> notification_manager_;
-  base::ObserverList<Observer>::Unchecked observers_;
+  base::ObserverList<Observer> observers_;
 
   // Keyed by CupsPrintJob's unique ID
   std::map<std::string, base::TimeTicks> print_job_start_times_;
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.cc b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
index 57aa8a3..d7b213c6 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_notification.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
@@ -97,6 +97,7 @@
   switch (button_commands_[*button_index]) {
     case ButtonCommand::CANCEL_PRINTING:
       DCHECK(print_job_);
+      cancelled_by_user_ = true;
 
       print_job_manager->CancelPrintJob(print_job_.get());
       // print_job_ was deleted in CancelPrintJob.  Forget the pointer.
@@ -105,7 +106,6 @@
       // Clean up the notification.
       NotificationDisplayService::GetForProfile(profile_)->Close(
           NotificationHandler::Type::TRANSIENT, notification_id_);
-      cancelled_by_user_ = true;
       notification_manager_->OnPrintJobNotificationRemoved(this);
       break;
     case ButtonCommand::PAUSE_PRINTING:
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.cc
index 58d7dd95..6b04785 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.cc
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/shared_memory.h"
 #include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/unguessable_token.h"
@@ -36,6 +37,18 @@
 
 namespace chromeos {
 
+// List of extension URLs that will communicate with wilco_dtc
+// through the extensions native messaging system.
+//
+// Note: the list size must be kept in sync with
+// |kWilcoDtcSupportdHostOriginsSize|.
+const char* const kWilcoDtcSupportdHostOrigins[] = {
+    "chrome-extension://echlnkcmdobkdgcjgjbiceoceeoenjkj/"};
+
+// Size of |kWilcoDtcSupportdHostOrigins| array.
+const size_t kWilcoDtcSupportdHostOriginsSize =
+    base::size(kWilcoDtcSupportdHostOrigins);
+
 // Native application name that is used for passing UI messages between the
 // wilco_dtc daemon and extensions.
 const char kWilcoDtcSupportdUiMessageHost[] = "com.google.wilco_dtc";
@@ -52,16 +65,6 @@
 
 namespace {
 
-// List of extension IDs that will receive UI messages from the wilco_dtc
-// through the extensions native messaging system.
-//
-// Note: the list must be kept in sync with the allowed origins list in
-// src/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc.
-//
-// TODO(crbug.com/907932,b/123926112): Populate the list once extension IDs are
-// determined.
-const char* const kAllowedExtensionIds[] = {};
-
 // Extensions native message host implementation that is used when an
 // extension requests a message channel to the wilco_dtc daemon.
 //
@@ -372,10 +375,12 @@
   std::vector<std::pair<Profile*, std::string>> recipient_extensions;
   for (auto* profile :
        g_browser_process->profile_manager()->GetLoadedProfiles()) {
-    for (const auto* extension_id : kAllowedExtensionIds) {
+    for (const auto* extension_url : kWilcoDtcSupportdHostOrigins) {
+      GURL url = GURL(extension_url);
       if (extensions::ExtensionRegistry::Get(profile)
               ->enabled_extensions()
-              .GetByID(extension_id)) {
+              .GetExtensionOrAppByURL(url)) {
+        std::string extension_id = url.host();
         recipient_extensions.emplace_back(profile, extension_id);
       }
     }
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h
index f3fcc2a..d3e3dbeb 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_WILCO_DTC_SUPPORTD_MESSAGING_H_
 #define CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_WILCO_DTC_SUPPORTD_MESSAGING_H_
 
+#include <stddef.h>
 #include <memory>
 #include <string>
 
@@ -16,6 +17,10 @@
 
 namespace chromeos {
 
+extern const char* const kWilcoDtcSupportdHostOrigins[];
+
+extern const size_t kWilcoDtcSupportdHostOriginsSize;
+
 extern const char kWilcoDtcSupportdUiMessageHost[];
 
 extern const char kWilcoDtcSupportdUiMessageTooBigExtensionsError[];
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
index 1ba3053c..078befe 100644
--- a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
@@ -127,7 +127,9 @@
     {arc::ArcSupportMessageHost::kHostName,
      arc::ArcSupportMessageHost::kHostOrigin, 1,
      &arc::ArcSupportMessageHost::Create},
-    {chromeos::kWilcoDtcSupportdUiMessageHost, nullptr, 0,
+    {chromeos::kWilcoDtcSupportdUiMessageHost,
+     chromeos::kWilcoDtcSupportdHostOrigins,
+     chromeos::kWilcoDtcSupportdHostOriginsSize,
      &chromeos::CreateExtensionOwnedWilcoDtcSupportdMessageHost},
 };
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 5516d337..8882b4eb0 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -8,13 +8,18 @@
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/policy/browser_dm_token_storage.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/extensions/api/safe_browsing_private.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
+#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/event_router.h"
@@ -58,6 +63,30 @@
   InitRealtimeReportingClient();
 }
 
+// TODO(rogerta): once new event types are implemented, will likely want to
+// move this to a more common place.
+base::Value BuildRealtimeReport(Profile* profile, base::Value event) {
+  base::Value context(base::Value::Type::DICTIONARY);
+
+  ProfileAttributesStorage& storage =
+      g_browser_process->profile_manager()->GetProfileAttributesStorage();
+  ProfileAttributesEntry* entry = nullptr;
+  if (storage.GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
+    context.SetStringPath("profile.profileName", entry->GetName());
+    context.SetStringPath("profile.gaiaEmail", entry->GetUserName());
+  }
+
+  context.SetStringPath("profile.profilePath", profile->GetPath().value());
+  context.SetStringPath("browser.userAgent", GetUserAgent());
+
+  base::Value report(base::Value::Type::DICTIONARY);
+  report.SetKey(policy::RealtimeReportingJobConfiguration::kContextKey,
+                std::move(context));
+  report.SetKey(policy::RealtimeReportingJobConfiguration::kEventKey,
+                std::move(event));
+  return report;
+}
+
 SafeBrowsingPrivateEventRouter::~SafeBrowsingPrivateEventRouter() {}
 
 void SafeBrowsingPrivateEventRouter::OnPolicySpecifiedPasswordReuseDetected(
@@ -242,7 +271,7 @@
   if (!identity_manager_)
     return;
 
-  // |device_management_service| may be null in tests.    If there is no device
+  // |device_management_service| may be null in tests.  If there is no device
   // management service don't enable the real-time reporting API since the
   // router won't be able to create the reporting server client below.
   policy::DeviceManagementService* device_management_service =
@@ -300,7 +329,10 @@
   wrapper.SetStringKey("time", now_str);
   wrapper.SetKey(name, std::move(event));
 
-  client_->UploadRealtimeReport(std::move(wrapper), base::DoNothing());
+  client_->UploadRealtimeReport(
+      BuildRealtimeReport(Profile::FromBrowserContext(context_),
+                          std::move(wrapper)),
+      base::DoNothing());
 }
 
 std::string SafeBrowsingPrivateEventRouter::GetProfileUserName() {
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index bf670267..f0a9a724 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -10,8 +10,11 @@
 #include "base/values.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
 #include "chrome/common/extensions/api/safe_browsing_private.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
+#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/test_event_router.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -67,44 +70,49 @@
 
 class SafeBrowsingPrivateEventRouterTest : public testing::Test {
  public:
-  SafeBrowsingPrivateEventRouterTest() = default;
+  SafeBrowsingPrivateEventRouterTest()
+      : profile_manager_(TestingBrowserProcess::GetGlobal()) {
+    EXPECT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile("test-user");
+  }
+
   ~SafeBrowsingPrivateEventRouterTest() override = default;
 
   void TriggerOnPolicySpecifiedPasswordReuseDetectedEvent() {
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnPolicySpecifiedPasswordReuseDetected(GURL("https://phishing.com/"),
                                                  "user_name_1",
                                                  /*is_phishing_url*/ true);
   }
 
   void TriggerOnPolicySpecifiedPasswordChangedEvent() {
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnPolicySpecifiedPasswordChanged("user_name_2");
   }
 
   void TriggerOnDangerousDownloadOpenedEvent() {
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnDangerousDownloadOpened(GURL("https://evil.com/malware.exe"),
                                     "/path/to/malware.exe",
                                     "sha256_or_malware_exe");
   }
 
   void TriggerOnSecurityInterstitialShownEvent() {
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnSecurityInterstitialShown(GURL("https://phishing.com/"), "PHISHING",
                                       0);
   }
 
   void TriggerOnSecurityInterstitialProceededEvent() {
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->OnSecurityInterstitialProceeded(GURL("https://phishing.com/"),
                                           "PHISHING", -201);
   }
 
   void SetUpRouters() {
-    event_router_ = extensions::CreateAndUseTestEventRouter(&profile_);
+    event_router_ = extensions::CreateAndUseTestEventRouter(profile_);
     SafeBrowsingPrivateEventRouterFactory::GetInstance()->SetTestingFactory(
-        &profile_, base::BindRepeating(&BuildSafeBrowsingPrivateEventRouter));
+        profile_, base::BindRepeating(&BuildSafeBrowsingPrivateEventRouter));
 
     // Make sure real-time feature is eanbled so that the tests will run.
     scoped_feature_list_.InitAndEnableFeature(
@@ -115,14 +123,15 @@
     // manage expectations.
     client_ = new policy::MockCloudPolicyClient();
     std::unique_ptr<policy::CloudPolicyClient> client(client_);
-    SafeBrowsingPrivateEventRouterFactory::GetForProfile(&profile_)
+    SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_)
         ->SetCloudPolicyClientForTesting(std::move(client));
   }
 
  protected:
   content::TestBrowserThreadBundle thread_bundle_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  TestingProfile profile_;
+  TestingProfileManager profile_manager_;
+  TestingProfile* profile_;
   extensions::TestEventRouter* event_router_ = nullptr;
   policy::MockCloudPolicyClient* client_;
 
@@ -137,9 +146,9 @@
           kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  base::Value wrapper;
+  base::Value report;
   EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
-      .WillOnce(CaptureArg(&wrapper));
+      .WillOnce(CaptureArg(&report));
 
   TriggerOnPolicySpecifiedPasswordReuseDetectedEvent();
   base::RunLoop().RunUntilIdle();
@@ -149,9 +158,13 @@
   EXPECT_EQ("user_name_1", captured_args.FindKey("userName")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
-  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* wrapper =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventKey);
+  ASSERT_NE(nullptr, wrapper);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper->type());
   base::Value* event =
-      wrapper.FindKey(SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent);
+      wrapper->FindKey(SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent);
   EXPECT_NE(nullptr, event);
   EXPECT_EQ("https://phishing.com/",
             *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyUrl));
@@ -165,9 +178,9 @@
       api::safe_browsing_private::OnPolicySpecifiedPasswordChanged::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  base::Value wrapper;
+  base::Value report;
   EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
-      .WillOnce(CaptureArg(&wrapper));
+      .WillOnce(CaptureArg(&report));
 
   TriggerOnPolicySpecifiedPasswordChangedEvent();
   base::RunLoop().RunUntilIdle();
@@ -176,9 +189,13 @@
   EXPECT_EQ("user_name_2", captured_args.GetString());
 
   Mock::VerifyAndClearExpectations(client_);
-  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
-  base::Value* event =
-      wrapper.FindKey(SafeBrowsingPrivateEventRouter::kKeyPasswordChangedEvent);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* wrapper =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventKey);
+  ASSERT_NE(nullptr, wrapper);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper->type());
+  base::Value* event = wrapper->FindKey(
+      SafeBrowsingPrivateEventRouter::kKeyPasswordChangedEvent);
   EXPECT_NE(nullptr, event);
   EXPECT_EQ("user_name_2", *event->FindStringKey(
                                SafeBrowsingPrivateEventRouter::kKeyUserName));
@@ -190,9 +207,9 @@
       api::safe_browsing_private::OnDangerousDownloadOpened::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  base::Value wrapper;
+  base::Value report;
   EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
-      .WillOnce(CaptureArg(&wrapper));
+      .WillOnce(CaptureArg(&report));
 
   TriggerOnDangerousDownloadOpenedEvent();
   base::RunLoop().RunUntilIdle();
@@ -207,8 +224,12 @@
             captured_args.FindKey("downloadDigestSha256")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
-  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
-  base::Value* event = wrapper.FindKey(
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* wrapper =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventKey);
+  ASSERT_NE(nullptr, wrapper);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper->type());
+  base::Value* event = wrapper->FindKey(
       SafeBrowsingPrivateEventRouter::kKeyDangerousDownloadEvent);
   EXPECT_NE(nullptr, event);
   EXPECT_EQ(
@@ -223,9 +244,9 @@
       api::safe_browsing_private::OnSecurityInterstitialProceeded::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  base::Value wrapper;
+  base::Value report;
   EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
-      .WillOnce(CaptureArg(&wrapper));
+      .WillOnce(CaptureArg(&report));
 
   TriggerOnSecurityInterstitialProceededEvent();
   base::RunLoop().RunUntilIdle();
@@ -237,9 +258,13 @@
   EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
-  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* wrapper =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventKey);
+  ASSERT_NE(nullptr, wrapper);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper->type());
   base::Value* event =
-      wrapper.FindKey(SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent);
+      wrapper->FindKey(SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent);
   EXPECT_NE(nullptr, event);
   EXPECT_EQ("PHISHING",
             *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyReason));
@@ -255,9 +280,9 @@
       api::safe_browsing_private::OnSecurityInterstitialShown::kEventName);
   event_router_->AddEventObserver(&event_observer);
 
-  base::Value wrapper;
+  base::Value report;
   EXPECT_CALL(*client_, UploadRealtimeReport(_, _))
-      .WillOnce(CaptureArg(&wrapper));
+      .WillOnce(CaptureArg(&report));
 
   TriggerOnSecurityInterstitialShownEvent();
   base::RunLoop().RunUntilIdle();
@@ -269,9 +294,13 @@
   EXPECT_EQ("", captured_args.FindKey("userName")->GetString());
 
   Mock::VerifyAndClearExpectations(client_);
-  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, report.type());
+  base::Value* wrapper =
+      report.FindKey(policy::RealtimeReportingJobConfiguration::kEventKey);
+  ASSERT_NE(nullptr, wrapper);
+  EXPECT_EQ(base::Value::Type::DICTIONARY, wrapper->type());
   base::Value* event =
-      wrapper.FindKey(SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent);
+      wrapper->FindKey(SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent);
   EXPECT_NE(nullptr, event);
   EXPECT_EQ("PHISHING",
             *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyReason));
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
index 511f922..d916775 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
+++ b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
@@ -12,6 +12,15 @@
 
 ReputationWebContentsObserver::~ReputationWebContentsObserver() {}
 
+void ReputationWebContentsObserver::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->IsSameDocument()) {
+    return;
+  }
+  last_shown_safety_tip_type_ = SafetyTipType::kNone;
+}
+
 void ReputationWebContentsObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->IsInMainFrame()) {
@@ -52,6 +61,7 @@
     return;
   }
 
+  last_shown_safety_tip_type_ = type;
   ShowSafetyTipDialog(web_contents(), type, url);
 #endif  // !defined(OS_ANDROID)
 }
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
index 2c33457..c9309f18 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
+++ b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
@@ -29,9 +29,15 @@
   ~ReputationWebContentsObserver() override;
 
   // content::WebContentsObserver:
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
+  SafetyTipType last_shown_safety_tip_type() const {
+    return last_shown_safety_tip_type_;
+  }
+
  private:
   friend class content::WebContentsUserData<ReputationWebContentsObserver>;
 
@@ -44,6 +50,10 @@
                                    const GURL& url);
 
   Profile* profile_;
+  // Used to cache the last shown safety tip type so that Page Info can fetch
+  // this information without performing a reputation check. Resets to kNone on
+  // new top frame navigations.
+  safety_tips::SafetyTipType last_shown_safety_tip_type_ = SafetyTipType::kNone;
 
   base::WeakPtrFactory<ReputationWebContentsObserver> weak_factory_;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
index 49c3dd3..9f250a5 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
@@ -32,8 +32,9 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/info_map.h"
+#include "extensions/browser/extension_util.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -165,29 +166,33 @@
 
 // This function is security sensitive.  Be sure to check with a security
 // person before you modify it.
-bool NaClBrowserDelegateImpl::MapUrlToLocalFilePath(
-    const GURL& file_url,
-    bool use_blocking_api,
-    const base::FilePath& profile_directory,
-    base::FilePath* file_path) {
+NaClBrowserDelegate::MapUrlToLocalFilePathCallback
+NaClBrowserDelegateImpl::GetMapUrlToLocalFilePathCallback(
+    const base::FilePath& profile_directory) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  scoped_refptr<extensions::InfoMap> extension_info_map =
-      GetExtensionInfoMap(profile_directory);
-  return extension_info_map->MapUrlToLocalFilePath(
-      file_url, use_blocking_api, file_path);
+  auto extensions = std::make_unique<extensions::ExtensionSet>();
+  extensions->InsertAll(
+      extensions::ExtensionRegistry::Get(
+          profile_manager_->GetProfileByPath(profile_directory))
+          ->enabled_extensions());
+  return base::BindRepeating(&extensions::util::MapUrlToLocalFilePath,
+                             base::Owned(std::move(extensions)));
 #else
-  return false;
+  return base::BindRepeating([](const GURL& url, bool use_blocking_api,
+                                base::FilePath* file_path) { return false; });
 #endif
 }
 
 bool NaClBrowserDelegateImpl::IsNonSfiModeAllowed(
     const base::FilePath& profile_directory,
     const GURL& manifest_url) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  const extensions::ExtensionSet* extension_set =
-      &GetExtensionInfoMap(profile_directory)->extensions();
-  return IsExtensionOrSharedModuleWhitelisted(manifest_url, extension_set,
-                                              allowed_nonsfi_origins_);
+  auto* registry = extensions::ExtensionRegistry::Get(
+      profile_manager_->GetProfileByPath(profile_directory));
+  return IsExtensionOrSharedModuleWhitelisted(
+      manifest_url, &registry->enabled_extensions(), allowed_nonsfi_origins_);
 #else
   return false;
 #endif
@@ -209,16 +214,3 @@
   if (infobar_service)
     NaClInfoBarDelegate::Create(infobar_service);
 }
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-scoped_refptr<extensions::InfoMap> NaClBrowserDelegateImpl::GetExtensionInfoMap(
-    const base::FilePath& profile_directory) {
-  // Get the profile associated with the renderer.
-  Profile* profile = profile_manager_->GetProfileByPath(profile_directory);
-  DCHECK(profile);
-  scoped_refptr<extensions::InfoMap> extension_info_map =
-      extensions::ExtensionSystem::Get(profile)->info_map();
-  DCHECK(extension_info_map.get());
-  return extension_info_map;
-}
-#endif
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
index fdcc379..f7b02e6 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
@@ -16,10 +16,6 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/memory/ref_counted.h"
 #include "extensions/common/url_pattern.h"
-
-namespace extensions {
-class InfoMap;
-}
 #endif
 
 class ProfileManager;
@@ -39,10 +35,8 @@
   std::string GetVersionString() const override;
   ppapi::host::HostFactory* CreatePpapiHostFactory(
       content::BrowserPpapiHost* ppapi_host) override;
-  bool MapUrlToLocalFilePath(const GURL& url,
-                             bool is_blocking,
-                             const base::FilePath& profile_directory,
-                             base::FilePath* file_path) override;
+  MapUrlToLocalFilePathCallback GetMapUrlToLocalFilePathCallback(
+      const base::FilePath& profile_directory) override;
   void SetDebugPatterns(const std::string& debug_patterns) override;
   bool URLMatchesDebugPatterns(const GURL& manifest_url) override;
   bool IsNonSfiModeAllowed(const base::FilePath& profile_directory,
@@ -55,8 +49,6 @@
                                       int render_view_id);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  scoped_refptr<extensions::InfoMap> GetExtensionInfoMap(
-      const base::FilePath& profile_directory);
   std::vector<URLPattern> debug_patterns_;
 #endif
 
diff --git a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
index 48e685c3..0816e28 100644
--- a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
+++ b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
@@ -333,11 +333,10 @@
       base::BindOnce(&WritePermissionGrantImpl::OnPermissionRequestComplete,
                      this, std::move(callback)));
 
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&ShowWritePermissionPromptOnUIThread, process_id, frame_id,
-                     origin_, path(), is_directory_,
-                     std::move(result_callback)));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&ShowWritePermissionPromptOnUIThread,
+                                process_id, frame_id, origin_, path(),
+                                is_directory_, std::move(result_callback)));
 }
 
 bool ChromeNativeFileSystemPermissionContext::WritePermissionGrantImpl::
@@ -492,7 +491,7 @@
     int frame_id,
     base::OnceCallback<void(PermissionStatus)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
           &ShowDirectoryAccessConfirmationPromptOnUIThread, process_id,
@@ -526,7 +525,8 @@
   // file selection is only supported if all files are in the same
   // directory.
   base::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
       base::BindOnce(&ShouldBlockAccessToPath, paths[0]),
       base::BindOnce(&ChromeNativeFileSystemPermissionContext::
                          DidConfirmSensitiveDirectoryAccess,
@@ -688,7 +688,7 @@
   auto result_callback =
       BindResultCallbackToCurrentSequence(std::move(callback));
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(&ShowNativeFileSystemRestrictedDirectoryDialogOnUIThread,
                      process_id, frame_id, origin, paths[0],
diff --git a/chrome/browser/native_file_system/native_file_system_permission_request_manager.cc b/chrome/browser/native_file_system/native_file_system_permission_request_manager.cc
index 21feff4..7cfbc3f 100644
--- a/chrome/browser/native_file_system/native_file_system_permission_request_manager.cc
+++ b/chrome/browser/native_file_system/native_file_system_permission_request_manager.cc
@@ -74,7 +74,7 @@
   if (!CanShowRequest())
     return;
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
           &NativeFileSystemPermissionRequestManager::DequeueAndShowRequest,
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
index c476d787..ede5fe4 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
@@ -25,7 +25,7 @@
 
   void SetUp() override { config_.initial_daily_shown_per_type = 100; }
 
-  void InitFakeDay() {
+  void InitFakeClock() {
     clock_.SetNow(kFakeNow);
     ToLocalHour(0, clock_.Now(), 0, &beginning_of_today_);
   }
@@ -87,7 +87,7 @@
 TEST_F(SchedulerUtilsTest, NotificationsShownToday) {
   // Create fake client.
   auto new_client = CreateNewClientState(SchedulerClientType::kTest1, config());
-  InitFakeDay();
+  InitFakeClock();
   base::Time now = clock()->Now();
   // Test case 1:
   int count = NotificationsShownToday(new_client.get(), clock());
@@ -123,5 +123,6 @@
   count = NotificationsShownToday(new_client.get(), clock());
   EXPECT_EQ(count, 2);
 }
+
 }  // namespace
 }  // namespace notifications
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index f0859b30..4a4720d 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -949,7 +949,7 @@
 
 static const char kExpectedPDFAXTreePattern[] =
     "embeddedObject\n"
-    "  group\n"
+    "  document\n"
     "    region 'Page 1'\n"
     "      paragraph\n"
     "        staticText '1 First Section'\n"
@@ -2385,7 +2385,11 @@
     AddPropertyFilter(property_filters, "value='*'");
     // The value attribute on the document object contains the URL of the
     // current page which will not be the same every time the test is run.
+    // The PDF plugin uses the 'chrome-extension' protocol, so block that as
+    // well.
     AddPropertyFilter(property_filters, "value='http*'", PropertyFilter::DENY);
+    AddPropertyFilter(property_filters, "value='chrome-extension*'",
+                      PropertyFilter::DENY);
     // Object attributes.value
     AddPropertyFilter(property_filters, "layout-guess:*",
                       PropertyFilter::ALLOW);
@@ -2395,6 +2399,7 @@
     AddPropertyFilter(property_filters, "check*");
     AddPropertyFilter(property_filters, "horizontal");
     AddPropertyFilter(property_filters, "multiselectable");
+    AddPropertyFilter(property_filters, "isPageBreakingObject*");
 
     // Deny most empty values
     AddPropertyFilter(property_filters, "*=''", PropertyFilter::DENY);
@@ -2435,3 +2440,7 @@
                        ParagraphsAndHeadingUntagged) {
   RunPDFTest(FILE_PATH_LITERAL("paragraphs-and-heading-untagged.pdf"));
 }
+
+IN_PROC_BROWSER_TEST_P(PDFExtensionAccessibilityTreeDumpTest, MultiPage) {
+  RunPDFTest(FILE_PATH_LITERAL("multi-page.pdf"));
+}
diff --git a/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
index bc694a3..d191d304 100644
--- a/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/non_recording_site_data_cache_unittest.cc
@@ -23,11 +23,10 @@
   NonRecordingSiteDataCacheTest()
       : use_in_memory_db_for_testing_(
             LevelDBSiteDataStore::UseInMemoryDBForTesting()),
-        factory_(SiteDataCacheFactory::CreateForTesting(
-            test_browser_thread_bundle_.GetMainThreadTaskRunner())),
+        factory_(std::make_unique<SiteDataCacheFactory>()),
         off_the_record_profile_(parent_profile_.GetOffTheRecordProfile()) {}
 
-  ~NonRecordingSiteDataCacheTest() override { factory_.reset(); }
+  ~NonRecordingSiteDataCacheTest() override = default;
 
   void SetUp() override {
     recording_data_cache_ = base::WrapUnique(new SiteDataCacheImpl(
@@ -56,7 +55,7 @@
   std::unique_ptr<base::AutoReset<bool>> use_in_memory_db_for_testing_;
 
   // The data cache factory that will be used by the caches tested here.
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> factory_;
+  std::unique_ptr<SiteDataCacheFactory> factory_;
 
   // The on the record profile.
   TestingProfile parent_profile_;
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
index 271b02d9..fc16b0731 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
@@ -16,6 +17,8 @@
 
 namespace performance_manager {
 
+class GraphImpl;
+
 SiteDataCacheFacade::SiteDataCacheFacade(
     content::BrowserContext* browser_context)
     : browser_context_(browser_context) {
@@ -45,10 +48,11 @@
 void SiteDataCacheFacade::WaitUntilCacheInitializedForTesting() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::RunLoop run_loop;
-  SiteDataCacheFactory::GetInstance()->task_runner_for_testing()->PostTask(
-      FROM_HERE, base::Bind(
+  PerformanceManager::GetInstance()->CallOnGraph(
+      FROM_HERE, base::BindOnce(
                      [](base::OnceClosure quit_closure,
-                        const std::string browser_context_id) {
+                        const std::string& browser_context_id,
+                        GraphImpl* graph_unused) {
                        auto* cache = SiteDataCacheFactory::GetInstance()
                                          ->GetDataCacheForBrowserContext(
                                              browser_context_id);
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
index c7b0717..6f153d6 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
@@ -5,25 +5,34 @@
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.h"
 
 #include "base/auto_reset.h"
+#include "base/callback.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/performance_manager/persistence/site_data/leveldb_site_data_store.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h"
+#include "chrome/browser/performance_manager/persistence/site_data/unittest_utils.h"
 #include "chrome/test/base/testing_profile.h"
-#include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 namespace performance_manager {
 
-TEST(SiteDataCacheFacadeTest, IsDataCacheRecordingForTesting) {
-  content::TestBrowserThreadBundle test_browser_thread_bundle;
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> factory(
-      SiteDataCacheFactory::CreateForTesting(base::CreateSequencedTaskRunner({
-          base::ThreadPool(),
-      })));
+using SiteDataCacheFacadeTest = testing::TestWithPerformanceManager;
+
+TEST_F(SiteDataCacheFacadeTest, IsDataCacheRecordingForTesting) {
+  // Create the SiteDataCacheFactory instance and pass it to the PM sequence for
+  // ownership.
+  PerformanceManager::GetInstance()->CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::unique_ptr<SiteDataCacheFactory> site_data_cache_factory,
+             performance_manager::GraphImpl* graph) {
+            graph->PassToGraph(std::move(site_data_cache_factory));
+          },
+          std::make_unique<SiteDataCacheFactory>()));
 
   TestingProfile profile;
   // Uses an in-memory database.
@@ -34,24 +43,30 @@
 
   SiteDataCacheFacade data_cache_facade(&profile);
   data_cache_facade.WaitUntilCacheInitializedForTesting();
-  base::RunLoop run_loop;
-  data_cache_facade.IsDataCacheRecordingForTesting(
-      base::BindLambdaForTesting([&](bool is_recording) {
-        cache_is_recording = is_recording;
-        run_loop.QuitClosure().Run();
-      }));
-  run_loop.Run();
+  {
+    base::RunLoop run_loop;
+    auto quit_closure = run_loop.QuitClosure();
+    data_cache_facade.IsDataCacheRecordingForTesting(
+        base::BindLambdaForTesting([&](bool is_recording) {
+          cache_is_recording = is_recording;
+          std::move(quit_closure).Run();
+        }));
+    run_loop.Run();
+  }
   EXPECT_TRUE(cache_is_recording);
 
-  base::RunLoop run_loop2;
   SiteDataCacheFacade off_record_data_cache_facade(
       profile.GetOffTheRecordProfile());
-  off_record_data_cache_facade.IsDataCacheRecordingForTesting(
-      base::BindLambdaForTesting([&](bool is_recording) {
-        cache_is_recording = is_recording;
-        run_loop2.QuitClosure().Run();
-      }));
-  run_loop2.Run();
+  {
+    base::RunLoop run_loop;
+    auto quit_closure = run_loop.QuitClosure();
+    off_record_data_cache_facade.IsDataCacheRecordingForTesting(
+        base::BindLambdaForTesting([&](bool is_recording) {
+          cache_is_recording = is_recording;
+          quit_closure.Run();
+        }));
+    run_loop.Run();
+  }
 
   EXPECT_FALSE(cache_is_recording);
 }
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
index bfc355fb..4d69bf2 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/task_runner_util.h"
@@ -26,10 +27,10 @@
   return g_instance;
 }
 
-SiteDataCacheFactory::SiteDataCacheFactory(
-    const scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : task_runner_(task_runner) {
+SiteDataCacheFactory::SiteDataCacheFactory() {
   DETACH_FROM_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(nullptr, g_instance);
+  g_instance = this;
 }
 
 SiteDataCacheFactory::~SiteDataCacheFactory() {
@@ -39,33 +40,6 @@
 }
 
 // static
-std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter>
-SiteDataCacheFactory::Create() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK_EQ(nullptr, g_instance);
-  // TODO(sebmarchand): Make this GraphOwned!
-  auto task_runner = PerformanceManager::GetInstance()->task_runner();
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> instance(
-      new SiteDataCacheFactory(task_runner),
-      base::OnTaskRunnerDeleter(task_runner));
-
-  g_instance = instance.get();
-  return instance;
-}
-
-// static
-std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter>
-SiteDataCacheFactory::CreateForTesting(
-    const scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  DCHECK_EQ(nullptr, g_instance);
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> instance(
-      new SiteDataCacheFactory(task_runner),
-      base::OnTaskRunnerDeleter(task_runner));
-  g_instance = instance.get();
-  return instance;
-}
-
-// static
 void SiteDataCacheFactory::OnBrowserContextCreatedOnUIThread(
     SiteDataCacheFactory* factory,
     content::BrowserContext* browser_context,
@@ -81,9 +55,16 @@
     DCHECK(browser_context->IsOffTheRecord());
     parent_context_id = parent_context->UniqueId();
   }
-  factory->task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SiteDataCacheFactory::OnBrowserContextCreated,
+  PerformanceManager::GetInstance()->CallOnGraph(
+      FROM_HERE, base::BindOnce(
+                     [](SiteDataCacheFactory* factory,
+                        const std::string& browser_context_id,
+                        const base::FilePath& context_path,
+                        base::Optional<std::string> parent_context_id,
+                        GraphImpl* graph_unused) {
+                       factory->OnBrowserContextCreated(
+                           browser_context_id, context_path, parent_context_id);
+                     },
                      base::Unretained(factory), browser_context->UniqueId(),
                      browser_context->GetPath(), parent_context_id));
 }
@@ -94,11 +75,13 @@
     content::BrowserContext* browser_context) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(factory);
-  // See OnBrowserContextCreatedOnUIThread for why it's safe to use a raw
-  // pointer in BindOnce.
-  factory->task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SiteDataCacheFactory::OnBrowserContextDestroyed,
+  PerformanceManager::GetInstance()->CallOnGraph(
+      FROM_HERE, base::BindOnce(
+                     [](SiteDataCacheFactory* factory,
+                        const std::string& browser_context_id,
+                        performance_manager::GraphImpl* graph_unused) {
+                       factory->OnBrowserContextDestroyed(browser_context_id);
+                     },
                      base::Unretained(factory), browser_context->UniqueId()));
 }
 
@@ -138,17 +121,17 @@
     const std::string& browser_context_id,
     base::OnceCallback<void(bool)> cb) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  PostTaskAndReplyWithResult(
-      task_runner_.get(), FROM_HERE,
+  PerformanceManager::GetInstance()->CallOnGraph(
+      FROM_HERE,
       base::BindOnce(
-          [](SiteDataCacheFactory* tracker,
-             const std::string& browser_context_id) {
-            auto it = tracker->data_cache_map_.find(browser_context_id);
-            CHECK(it != tracker->data_cache_map_.end());
-            return it->second->IsRecordingForTesting();
+          [](SiteDataCacheFactory* factory,
+             const std::string& browser_context_id,
+             base::OnceCallback<void(bool)> cb, GraphImpl* graph_unused) {
+            auto it = factory->data_cache_map_.find(browser_context_id);
+            CHECK(it != factory->data_cache_map_.end());
+            std::move(cb).Run(it->second->IsRecordingForTesting());
           },
-          this, browser_context_id),
-      std::move(cb));
+          this, browser_context_id, std::move(cb)));
 }
 
 void SiteDataCacheFactory::OnBrowserContextCreated(
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
index fb6657ce..efca2d03 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory.h
@@ -17,6 +17,7 @@
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache.h"
+#include "chrome/browser/performance_manager/public/graph/graph.h"
 #include "content/public/browser/browser_context.h"
 
 namespace content {
@@ -30,9 +31,13 @@
 // This class is responsible for tracking the SiteDataCache instances associated
 // with each browser context. It is meant to be used as a bridge between the
 // browser contexts living on the UI thread and the PerformanceManager sequence.
-class SiteDataCacheFactory {
+//
+// This can be created on any sequence but it then should be passed to the
+// graph and used on the PerformanceManager sequence.
+class SiteDataCacheFactory : public GraphOwnedDefaultImpl {
  public:
-  ~SiteDataCacheFactory();
+  SiteDataCacheFactory();
+  ~SiteDataCacheFactory() override;
 
   // Retrieves the currently registered instance.
   // The caller needs to ensure that the lifetime of the registered instance
@@ -40,21 +45,6 @@
   // This function can be called from any sequence with those caveats.
   static SiteDataCacheFactory* GetInstance();
 
-  // Creates, initializes and registers an instance. The created instance will
-  // use the task runner from PerformanceManager for all its operations and thus
-  // should be created after the PerformanceManager global instance.
-  // The instance will be deleted on the instance's task runner.
-  //
-  // This function should only be called from the UI thread.
-  static std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter>
-  Create();
-
-  // Create an instance that will live and be destroyed on |task_runner|.
-  //
-  // This function should only be called from the UI thread.
-  static std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter>
-  CreateForTesting(const scoped_refptr<base::SequencedTaskRunner> task_runner);
-
   // Functions that should be called when a new browser context is created or
   // destroyed. They should be called from the UI thread, a task will then be
   // posted to the task_runner owned by |factory| to create the data store
@@ -104,14 +94,7 @@
   void IsDataCacheRecordingForTesting(const std::string& browser_context_id,
                                       base::OnceCallback<void(bool)> cb);
 
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing() {
-    return task_runner_;
-  }
-
  private:
-  explicit SiteDataCacheFactory(
-      const scoped_refptr<base::SequencedTaskRunner> task_runner);
-
   // Implementation of the corresponding *OnUIThread public static functions
   // that runs on this object's task runner.
   void OnBrowserContextCreated(const std::string& browser_context_id,
@@ -119,10 +102,6 @@
                                base::Optional<std::string> parent_context_id);
   void OnBrowserContextDestroyed(const std::string& browser_context_id);
 
-  // The task runner on which this object lives, this is expected to be the
-  // performance task runner in practice.
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
   // A map that associates a BrowserContext's ID with a SiteDataCache. This
   // object owns the caches.
   base::flat_map<std::string, std::unique_ptr<SiteDataCache>> data_cache_map_;
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
index 62f1191..1a5b2ba 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_factory_unittest.cc
@@ -10,75 +10,73 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/performance_manager/persistence/site_data/unittest_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
 
-class SiteDataCacheFactoryTest : public ::testing::Test {
- protected:
-  SiteDataCacheFactoryTest()
-      : task_runner_(base::CreateSequencedTaskRunner({
-            base::ThreadPool(),
-        })),
-        factory_(SiteDataCacheFactory::CreateForTesting(task_runner_)) {}
-
-  ~SiteDataCacheFactoryTest() override {
-    factory_.reset();
-    test_browser_thread_bundle_.RunUntilIdle();
-  }
-
-  void SetUp() override {
-    EXPECT_EQ(SiteDataCacheFactory::GetInstance(), factory_.get());
-  }
-
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter> factory_;
-  TestingProfile profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(SiteDataCacheFactoryTest);
-};
+using SiteDataCacheFactoryTest = testing::TestWithPerformanceManager;
 
 TEST_F(SiteDataCacheFactoryTest, EndToEnd) {
-  SiteDataCacheFactory::OnBrowserContextCreatedOnUIThread(factory_.get(),
-                                                          &profile_, nullptr);
-
-  base::RunLoop run_loop;
-  task_runner_->PostTask(
+  std::unique_ptr<SiteDataCacheFactory> factory =
+      std::make_unique<SiteDataCacheFactory>();
+  SiteDataCacheFactory* factory_raw = factory.get();
+  PerformanceManager::GetInstance()->CallOnGraph(
       FROM_HERE,
       base::BindOnce(
-          [](SiteDataCacheFactory* factory,
-             const std::string& browser_context_id,
-             base::OnceClosure quit_closure) {
-            DCHECK_NE(nullptr, factory->GetDataCacheForBrowserContext(
-                                   browser_context_id));
-            DCHECK_NE(nullptr, factory->GetInspectorForBrowserContext(
-                                   browser_context_id));
-            std::move(quit_closure).Run();
+          [](std::unique_ptr<SiteDataCacheFactory> site_data_cache_factory,
+             performance_manager::GraphImpl* graph) {
+            graph->PassToGraph(std::move(site_data_cache_factory));
           },
-          factory_.get(), profile_.UniqueId(), run_loop.QuitClosure()));
-  run_loop.Run();
+          std::move(factory)));
 
-  SiteDataCacheFactory::OnBrowserContextDestroyedOnUIThread(factory_.get(),
-                                                            &profile_);
+  TestingProfile profile;
+  SiteDataCacheFactory::OnBrowserContextCreatedOnUIThread(factory_raw, &profile,
+                                                          nullptr);
 
-  base::RunLoop run_loop2;
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          [](SiteDataCacheFactory* factory,
-             const std::string& browser_context_id,
-             base::OnceClosure quit_closure) {
-            DCHECK_EQ(nullptr, factory->GetDataCacheForBrowserContext(
-                                   browser_context_id));
-            DCHECK_EQ(nullptr, factory->GetInspectorForBrowserContext(
-                                   browser_context_id));
-            std::move(quit_closure).Run();
-          },
-          factory_.get(), profile_.UniqueId(), run_loop.QuitClosure()));
-  test_browser_thread_bundle_.RunUntilIdle();
+  {
+    base::RunLoop run_loop;
+    PerformanceManager::GetInstance()->CallOnGraph(
+        FROM_HERE,
+        base::BindOnce(
+            [](SiteDataCacheFactory* factory,
+               const std::string& browser_context_id,
+               base::OnceClosure quit_closure,
+               performance_manager::GraphImpl* graph_unused) {
+              DCHECK_NE(nullptr, factory->GetDataCacheForBrowserContext(
+                                     browser_context_id));
+              DCHECK_NE(nullptr, factory->GetInspectorForBrowserContext(
+                                     browser_context_id));
+              std::move(quit_closure).Run();
+            },
+            base::Unretained(factory_raw), profile.UniqueId(),
+            run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  SiteDataCacheFactory::OnBrowserContextDestroyedOnUIThread(factory_raw,
+                                                            &profile);
+  {
+    base::RunLoop run_loop;
+    PerformanceManager::GetInstance()->CallOnGraph(
+        FROM_HERE,
+        base::BindOnce(
+            [](SiteDataCacheFactory* factory,
+               const std::string& browser_context_id,
+               base::OnceClosure quit_closure,
+               performance_manager::GraphImpl* graph_unused) {
+              DCHECK_EQ(nullptr, factory->GetDataCacheForBrowserContext(
+                                     browser_context_id));
+              DCHECK_EQ(nullptr, factory->GetInspectorForBrowserContext(
+                                     browser_context_id));
+              std::move(quit_closure).Run();
+            },
+            base::Unretained(factory_raw), profile.UniqueId(),
+            run_loop.QuitClosure()));
+    run_loop.Run();
+  }
 }
 
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl_unittest.cc
index c8536adc..7f92043 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_impl_unittest.cc
@@ -47,8 +47,7 @@
 class SiteDataCacheImplTest : public ::testing::Test {
  protected:
   SiteDataCacheImplTest()
-      : data_cache_factory_(SiteDataCacheFactory::CreateForTesting(
-            test_browser_thread_bundle_.GetMainThreadTaskRunner())) {
+      : data_cache_factory_(std::make_unique<SiteDataCacheFactory>()) {
     PerformanceManagerClock::SetClockForTesting(&test_clock_);
     data_cache_ = std::make_unique<SiteDataCacheImpl>(profile_.UniqueId(),
                                                       profile_.GetPath());
@@ -128,8 +127,7 @@
 
   // Owned by |data_cache_|.
   ::testing::StrictMock<MockSiteCache>* mock_db_ = nullptr;
-  std::unique_ptr<SiteDataCacheFactory, base::OnTaskRunnerDeleter>
-      data_cache_factory_;
+  std::unique_ptr<SiteDataCacheFactory> data_cache_factory_;
   std::unique_ptr<SiteDataCacheImpl> data_cache_;
 
   std::unique_ptr<SiteDataReader> reader_;
diff --git a/chrome/browser/performance_manager/persistence/site_data/unittest_utils.cc b/chrome/browser/performance_manager/persistence/site_data/unittest_utils.cc
index d39094f6..b69f31b9 100644
--- a/chrome/browser/performance_manager/persistence/site_data/unittest_utils.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/unittest_utils.cc
@@ -42,5 +42,24 @@
   std::move(callback).Run();
 }
 
+TestWithPerformanceManager::TestWithPerformanceManager() = default;
+
+TestWithPerformanceManager::~TestWithPerformanceManager() = default;
+
+void TestWithPerformanceManager::SetUp() {
+  EXPECT_EQ(nullptr, PerformanceManager::GetInstance());
+  performance_manager_ = PerformanceManager::Create();
+  // Make sure creation registers the created instance.
+  EXPECT_EQ(performance_manager_.get(), PerformanceManager::GetInstance());
+}
+
+void TestWithPerformanceManager::TearDown() {
+  PerformanceManager::Destroy(std::move(performance_manager_));
+  // Make sure destruction unregisters the instance.
+  EXPECT_EQ(nullptr, PerformanceManager::GetInstance());
+
+  test_browser_thread_bundle_.RunUntilIdle();
+}
+
 }  // namespace testing
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/unittest_utils.h b/chrome/browser/performance_manager/persistence/site_data/unittest_utils.h
index 12a20dc..218f4a6b 100644
--- a/chrome/browser/performance_manager/persistence/site_data/unittest_utils.h
+++ b/chrome/browser/performance_manager/persistence/site_data/unittest_utils.h
@@ -9,9 +9,12 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "chrome/browser/performance_manager/performance_manager.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_impl.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_store.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
 namespace testing {
@@ -50,6 +53,21 @@
   DISALLOW_COPY_AND_ASSIGN(NoopSiteDataStore);
 };
 
+class TestWithPerformanceManager : public ::testing::Test {
+ public:
+  TestWithPerformanceManager();
+  ~TestWithPerformanceManager() override;
+
+  void SetUp() override;
+  void TearDown() override;
+
+ private:
+  std::unique_ptr<PerformanceManager> performance_manager_;
+  content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWithPerformanceManager);
+};
+
 }  // namespace testing
 }  // namespace performance_manager
 
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
index 3dfc945..577c945 100644
--- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
+++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.js
@@ -267,11 +267,15 @@
   applyTime_: function() {
     const now = this.getInputTime_();
 
-    // Add timezone offset to get real time.
-    const timezoneDelta = getTimezoneDelta(
-        /** @type {string} */ (loadTimeData.getValue('currentTimezoneId')),
-        this.selectedTimezone_);
-    now.setMilliseconds(now.getMilliseconds() + timezoneDelta);
+    if (this.isTimezoneVisible_) {
+      // Add timezone offset to get real time. This is only necessary when the
+      // timezone was updated, which is only possible when the dropdown is
+      // visible.
+      const timezoneDelta = getTimezoneDelta(
+          /** @type {string} */ (loadTimeData.getValue('currentTimezoneId')),
+          this.selectedTimezone_);
+      now.setMilliseconds(now.getMilliseconds() + timezoneDelta);
+    }
 
     const seconds = Math.floor(now / 1000);
     this.browserProxy_.setTimeInSeconds(seconds);
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_manager.js b/chrome/browser/resources/chromeos/switch_access/menu_manager.js
index b480f97..1334a22 100644
--- a/chrome/browser/resources/chromeos/switch_access/menu_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/menu_manager.js
@@ -45,6 +45,12 @@
 
     /**
      * The node that the menu has been opened for.
+     * @private {chrome.automation.AutomationNode}
+     */
+    this.menuOriginNode_;
+
+    /**
+     * The node that the menu has been opened for.
      * @private {!chrome.automation.AutomationNode}
      */
     this.menuOriginNode_ = desktop;
@@ -56,6 +62,12 @@
     this.inMenu_ = false;
 
     /**
+     * Keeps track of when there's a selection in the current node.
+     * @private {boolean}
+     */
+    this.selectionExists_ = false;
+
+    /**
      * Keeps track of when the clipboard is empty.
      * @private {boolean}
      */
@@ -111,6 +123,9 @@
       chrome.accessibilityPrivate.setSwitchAccessMenuState(
           true, navNode.location, actions.length);
       this.menuOriginNode_ = navNode;
+      this.menuOriginNode_.addEventListener(
+          chrome.automation.EventType.TEXT_SELECTION_CHANGED,
+          this.onSelectionChanged_.bind(this), false /** Don't use capture. */);
     } else {
       console.log('Unable to show Switch Access menu.');
     }
@@ -161,6 +176,7 @@
     }
 
     if (actionNode) {
+      this.menuOriginNode_ = navNode;
       this.node_ = actionNode;
       this.updateFocusRing_();
     }
@@ -175,8 +191,11 @@
     if (this.node_)
       this.node_ = null;
 
+    this.menuOriginNode_.removeEventListener(
+        chrome.automation.EventType.TEXT_SELECTION_CHANGED,
+        this.onSelectionChanged_.bind(this), false /** Don't use capture. */);
     chrome.accessibilityPrivate.setSwitchAccessMenuState(
-        false, SAConstants.EMPTY_LOCATION, 0);
+        false /** Hide the menu. */, SAConstants.EMPTY_LOCATION, 0);
   }
 
   /**
@@ -301,7 +320,9 @@
    */
   updateClipboardHasData() {
     this.clipboardHasData_ = true;
-    this.reloadMenu_(this.menuOriginNode_);
+    if (this.menuOriginNode_) {
+      this.reloadMenu_(this.menuOriginNode_);
+    }
   }
 
   /**
@@ -313,6 +334,39 @@
   }
 
   /**
+   * Returns if there is a selection in the current node.
+   * @private
+   * @returns {boolean} whether or not there's a selection
+   */
+  nodeHasSelection_() {
+    let previousSelectionState = this.selectionExists_;
+    if (this.menuOriginNode_) {
+      if (this.menuOriginNode_.textSelStart !==
+          this.menuOriginNode_.textSelEnd) {
+        return true;
+      } else {
+        return false;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check to see if there is a change in the selection in the current node and
+   * reload the menu if so.
+   * @private
+   */
+  onSelectionChanged_() {
+    let newSelectionState = this.nodeHasSelection_();
+    if (this.selectionExists_ != newSelectionState) {
+      this.selectionExists_ = newSelectionState;
+      if (this.menuOriginNode_) {
+        this.reloadMenu_(this.menuOriginNode_);
+      }
+    }
+  }
+
+  /**
    * Determines which menu actions are relevant, given the current node. If
    * there are no node-specific actions, return |null|, to indicate that we
    * should select the current node automatically.
@@ -362,8 +416,10 @@
         if (this.navigationManager_.selectionStarted()) {
           actions.push(SAConstants.MenuAction.SELECT_END);
         }
-        actions.push(SAConstants.MenuAction.CUT);
-        actions.push(SAConstants.MenuAction.COPY);
+        if (this.selectionExists_) {
+          actions.push(SAConstants.MenuAction.CUT);
+          actions.push(SAConstants.MenuAction.COPY);
+        }
         if (this.clipboardHasData_) {
           actions.push(SAConstants.MenuAction.PASTE);
         }
@@ -470,7 +526,8 @@
         break;
       case SAConstants.MenuAction.SELECT_START:
         this.navigationManager_.saveSelectStart();
-        this.reloadMenu_(this.navigationManager_.currentNode());
+        if (this.menuOriginNode_)
+          this.reloadMenu_(this.menuOriginNode_);
         exitAfterAction = false;
         break;
       case SAConstants.MenuAction.SELECT_END:
diff --git a/chrome/browser/resources/local_ntp/customize.css b/chrome/browser/resources/local_ntp/customize.css
index 51efc24f2..7e0e808d 100644
--- a/chrome/browser/resources/local_ntp/customize.css
+++ b/chrome/browser/resources/local_ntp/customize.css
@@ -296,6 +296,13 @@
   z-index: 10000;
 }
 
+/* Prevent footer from overlapping with header at small window heights. */
+@media only screen and (max-height: 116px) {
+  #bg-sel-menu {
+    max-height: 116px;
+  }
+}
+
 @media (prefers-color-scheme: dark) {
   #bg-sel-menu {
     background-color: rgb(var(--dark-mode-dialog-rgb));
diff --git a/chrome/browser/resources/safety_tips/PRESUBMIT.py b/chrome/browser/resources/safety_tips/PRESUBMIT.py
new file mode 100644
index 0000000..766fec6
--- /dev/null
+++ b/chrome/browser/resources/safety_tips/PRESUBMIT.py
@@ -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.
+
+"""Presubmit checks for Safety Tips proto for the component updater.
+"""
+
+"""Returns true if any line in changed_contents contains the string |s|.
+|changed_contents| is a tuple containing (line number, line text) pairs.
+"""
+def ContainsLine(changed_contents, s):
+  for _, line in changed_contents:
+    if line.strip().startswith(s):
+      return True
+  return False
+
+def CheckVersionUpdatedInProto(input_api, output_api):
+  def IsSafetyTipProto(x):
+    return (input_api.os_path.basename(x.LocalPath()) ==
+            'safety_tips.asciipb')
+
+  safety_tips_proto = input_api.AffectedFiles(file_filter=IsSafetyTipProto)
+  if not safety_tips_proto:
+    return []
+
+  contents = safety_tips_proto[0].ChangedContents()
+  # Must not have any changes containing flagged_page:
+  if ContainsLine(contents, 'flagged_page:'):
+      return [output_api.PresubmitError(
+      'Do not check in the full safety_tips.asciipb proto. '
+      'Only increment |version_id|.')]
+
+  # It's enticing to do something fancy like checking whether the ID was in fact
+  # incremented or whether this is a whitespace-only or comment-only change.
+  # However, currently deleted lines don't show up in ChangedContents() and
+  # attempting to parse the asciipb file any more than we are doing above is
+  # likely not worth the trouble.
+  #
+  # At worst, the submitter can skip the presubmit check on upload if it isn't
+  # correct.
+  if not ContainsLine(contents, 'version_id:'):
+    return [output_api.PresubmitError(
+        'Increment |version_id| in safety_tips.asciipb if you are '
+        'updating the file types proto.')]
+
+  return []
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return CheckVersionUpdatedInProto(input_api, output_api)
diff --git a/chrome/browser/resources/safety_tips/README.md b/chrome/browser/resources/safety_tips/README.md
new file mode 100644
index 0000000..7fc21e08
--- /dev/null
+++ b/chrome/browser/resources/safety_tips/README.md
@@ -0,0 +1,29 @@
+### Update Instructions for Safety Tips Component
+
+Safety Tips component pushes binary protos to clients via Chrome's component
+updater. The proto files are stored in a Google Cloud Storage bucket
+(`gs://chrome-components-safety-tips`) under versioned directories
+(e.g. `gs://chrome-components-safety-tips/5/all` for version `5`, `all` for all
+platforms).
+
+Follow these instructions to write binary protos under versioned directories:
+
+ 1. Overwrite `safety_tips.asciipb` with the data. Don't forget to increment
+ `version_id`.
+ 2. Build the binary proto: `ninja -C out/Release`. This will write the binary
+proto at `out/Release/gen/chrome/browser/resources/safety_tips/safety_tips.pb`.
+ 3. Push the binary proto to cloud storage:
+ `chrome/browser/resources/safety_tips/push_proto.py -d out/Release`
+ 4. Revert `safety_tips.asciipb`, update `version_id` only for future reference
+ and check it in.
+ 5. In a day or two, navigate to chrome://components on Canary and check that
+ the version of the "Safety Tips" component is updated (you might need to press
+ the "Check for update" button).
+
+ **Do not check in the full proto**.
+
+#### In case of emergency
+
+If the Safety Tips component needs to be disabled immediately, increment the
+version number and push an empty proto.
+
diff --git a/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py b/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
index a12e8e78..42f3e888 100755
--- a/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
+++ b/chrome/browser/resources/safety_tips/gen_safety_tips_proto.py
@@ -15,6 +15,9 @@
 
 # Subdirectory to be copied to Google Cloud Storage. Contains a copy of the
 # generated proto under a versioned directory.
+# TODO(meacer): Remove this. Safety tips does not read the proto from a local
+# resource bundle, it only uses the proto passed from component updater. It does
+# not need two copies of the file.
 GS_COPY_DIR = "gs_copy"
 
 # Import the binary proto generator. Walks up to the root of the source tree
@@ -45,6 +48,9 @@
       assert flagged_page.url
       assert flagged_page.type != safety_tips_pb2.FlaggedPage.UNKNOWN
 
+    flagged_urls = [flagged_page.url for flagged_page in pb.flagged_page]
+    assert sorted(flagged_urls) == flagged_urls, "Please sort flagged_page entries by URL."
+
   def ProcessPb(self, opts, pb):
     binary_pb_str = pb.SerializeToString()
     outfile = os.path.join(opts.outdir, opts.outbasename)
diff --git a/chrome/browser/resources/safety_tips/push_proto.py b/chrome/browser/resources/safety_tips/push_proto.py
new file mode 100755
index 0000000..2404cb9
--- /dev/null
+++ b/chrome/browser/resources/safety_tips/push_proto.py
@@ -0,0 +1,89 @@
+#!/usr/bin/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.
+
+# Build and push the {vers}/all/ssl_error_assistant.pb file to GCS so
+# that the component update system will pick it up and push it to users.
+# See README.md before running this.
+#
+# Requires ninja and gsutil to be in the user's path.
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+
+
+DEST_BUCKET = 'gs://chrome-components-safety-tips'
+RESOURCE_SUBDIR = 'chrome/browser/resources/safety_tips'
+
+# Subdirectory to be copied to Google Cloud Storage. Contains a copy of the
+# generated proto under a versioned directory.
+GS_COPY_DIR = "gs_copy"
+
+# TODO(meacer): This is pretty much a duplicate of
+#               chrome/browser/safe_browsing/push_file_type_proto.py. Consider
+#               refactoring and reusing code.
+def main():
+  parser = optparse.OptionParser()
+  parser.add_option('-d', '--dir',
+                    help='An up-to-date GN/Ninja build directory, '
+                    'such as ./out/Debug')
+
+  (opts, _) = parser.parse_args()
+  if opts.dir is None:
+    parser.print_help()
+    return 1
+
+  # Clear out the target dir before we build so we can be sure we've got
+  # the freshest version.
+  target_dir = os.path.join(opts.dir, "gen", RESOURCE_SUBDIR)
+  if os.path.isdir(target_dir):
+    shutil.rmtree(target_dir)
+
+  gn_command = ['ninja',
+                '-C', opts.dir,
+                RESOURCE_SUBDIR + ':make_safety_tips_protobuf']
+  print "Running the following"
+  print "   " + (' '.join(gn_command))
+  if subprocess.call(gn_command):
+    print "Ninja failed."
+    return 1
+
+  # Use the versioned files under the copy directory to push to the GCS bucket.
+  copy_dir = os.path.join(target_dir, GS_COPY_DIR)
+  os.chdir(copy_dir)
+
+  # Sanity check that there is a versioned copy under the directory.
+  dirs = os.listdir('.')
+  assert len(dirs) == 1 and dirs[0].isdigit(), (
+      "There must be a single versioned dir under " + copy_dir)
+
+  # Push the files with their directories, in the form
+  #   {vers}/{platform}/download_file_types.pb
+  # Don't overwrite existing files, in case we forgot to increment the
+  # version.
+  version_dir = dirs[0]
+  command = ['gsutil', 'cp', '-Rn', version_dir, DEST_BUCKET]
+
+  print '\nGoing to run the following command'
+  print '   ', ' '.join(command)
+  print '\nIn directory'
+  print '   ', copy_dir
+  print '\nWhich should push the following files'
+  expected_files = [os.path.join(dp, f) for dp, _, fn in
+                    os.walk(version_dir) for f in fn]
+  for f in expected_files:
+    print '   ', f
+
+  shall = raw_input('\nAre you sure (y/N) ').lower() == 'y'
+  if not shall:
+    print 'aborting'
+    return 1
+  return subprocess.call(command)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/chrome/browser/resources/safety_tips/safety_tips.asciipb b/chrome/browser/resources/safety_tips/safety_tips.asciipb
index 9306e6c7..0634d4d 100644
--- a/chrome/browser/resources/safety_tips/safety_tips.asciipb
+++ b/chrome/browser/resources/safety_tips/safety_tips.asciipb
@@ -11,6 +11,7 @@
 version_id: 1
 
 # See chrome/browser/lookalikes/safety_tips.proto for the full format.
+# These entries must be sorted by url.
 flagged_page {
   url: "http://example.test/test-path-for-safety-tips/test.html"
   type: BAD_REP
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index a53102f5..22c9fa9 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
+#include "chrome/browser/ui/tabs/tab_group_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/extensions/extension_metrics.h"
@@ -440,6 +441,19 @@
 
       RestoreTabsToBrowser(*(*i), browser, initial_tab_count,
                            selected_tab_index, created_contents);
+
+      // Tabs will be grouped appropriately in RestoreTabsToBrowser. Now restore
+      // the groups' visual data.
+      if (base::FeatureList::IsEnabled(features::kTabGroups)) {
+        for (auto& tab_group : (*i)->tab_groups) {
+          TabGroupVisualData restored_data(std::move(tab_group->title),
+                                           tab_group->color);
+          browser->tab_strip_model()->SetVisualDataForGroup(
+              TabGroupId::FromRawToken(tab_group->group_id),
+              std::move(restored_data));
+        }
+      }
+
       NotifySessionServiceOfRestoredTabs(browser, initial_tab_count);
       // This needs to be done after restore because closing the last tab will
       // close the whole window.
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 09831c1..37723c2 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -71,6 +71,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/gfx/color_palette.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -928,12 +929,37 @@
   return result;
 }
 
+// Building session state from scratch and from an existing browser use
+// different code paths. So, create a parametrized test fixture to run each test
+// with and without a command reset. The bool test parameter determines whether
+// to do a command reset when quitting and restoring.
+class SessionRestoreTabGroupsTest : public SessionRestoreTest,
+                                    public testing::WithParamInterface<bool> {
+ protected:
+  void SetUpOnMainThread() override {
+    feature_override_.InitAndEnableFeature(features::kTabGroups);
+    SessionRestoreTest::SetUpOnMainThread();
+  }
+
+  Browser* QuitBrowserAndRestore(Browser* browser, int expected_tab_count) {
+    // The test parameter determines whether to do a command reset.
+    if (GetParam()) {
+      SessionService* const session_service =
+          SessionServiceFactory::GetForProfile(browser->profile());
+      session_service->ResetFromCurrentBrowsers();
+    }
+
+    return SessionRestoreTest::QuitBrowserAndRestore(browser,
+                                                     expected_tab_count);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_override_;
+};
+
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TabsWithGroups) {
-  base::test::ScopedFeatureList feature_override;
-  feature_override.InitAndEnableFeature(features::kTabGroups);
-
+IN_PROC_BROWSER_TEST_P(SessionRestoreTabGroupsTest, TabsWithGroups) {
   constexpr int kNumTabs = 6;
   const std::array<base::Optional<int>, kNumTabs> group_spec = {
       0, 0, base::nullopt, base::nullopt, 1, 1};
@@ -957,41 +983,48 @@
   EXPECT_EQ(groups, GetTabGroups(new_browser->tab_strip_model()));
 }
 
-// Test that tab groups are restored correctly after the command set is rebuilt
-// from the browser state.
-IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TabsWithGroupsCommandReset) {
-  base::test::ScopedFeatureList feature_override;
-  feature_override.InitAndEnableFeature(features::kTabGroups);
-
-  constexpr int kNumTabs = 6;
-  const std::array<base::Optional<int>, kNumTabs> group_spec = {
-      0, 0, base::nullopt, base::nullopt, 1, 1};
-
-  // Open |kNumTabs| tabs.
-  ui_test_utils::NavigateToURL(browser(), url1_);
-  for (int i = 1; i < kNumTabs; ++i) {
+IN_PROC_BROWSER_TEST_P(SessionRestoreTabGroupsTest, GroupMetadataRestored) {
+  // Open up 4 more tabs, making 5 including the initial tab.
+  for (int i = 0; i < 4; ++i) {
     ui_test_utils::NavigateToURLWithDisposition(
         browser(), url1_, WindowOpenDisposition::NEW_FOREGROUND_TAB,
         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
   }
-  ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count());
 
-  CreateTabGroups(browser()->tab_strip_model(), group_spec);
-  ASSERT_NO_FATAL_FAILURE(
-      CheckTabGrouping(browser()->tab_strip_model(), group_spec));
-  const auto groups = GetTabGroups(browser()->tab_strip_model());
+  TabStripModel* const tsm = browser()->tab_strip_model();
+  ASSERT_EQ(5, tsm->count());
 
-  // Rebuild commands.
-  SessionService* const session_service =
-      SessionServiceFactory::GetForProfile(browser()->profile());
-  ASSERT_TRUE(session_service);
-  session_service->ResetFromCurrentBrowsers();
+  // Group the first 2 and second 2 tabs, making for 2 groups with 2 tabs and 1
+  // ungrouped tab in the strip.
+  const TabGroupId group1 = tsm->AddToNewGroup({0, 1});
+  const TabGroupId group2 = tsm->AddToNewGroup({2, 3});
 
-  Browser* new_browser = QuitBrowserAndRestore(browser(), kNumTabs);
-  ASSERT_EQ(kNumTabs, new_browser->tab_strip_model()->count());
-  EXPECT_EQ(groups, GetTabGroups(new_browser->tab_strip_model()));
+  // Get the default visual data for the first group and set custom visual data
+  // for the second.
+  const TabGroupVisualData group1_data = *tsm->GetVisualDataForGroup(group1);
+  const TabGroupVisualData group2_data(base::ASCIIToUTF16("Foo"),
+                                       gfx::kGoogleBlue600);
+  tsm->SetVisualDataForGroup(group2, group2_data);
+
+  Browser* const new_browser = QuitBrowserAndRestore(browser(), 5);
+  TabStripModel* const new_tsm = new_browser->tab_strip_model();
+  ASSERT_EQ(5, new_tsm->count());
+
+  // Check that the restored visual data is the same.
+  const TabGroupVisualData* const group1_restored_data =
+      new_tsm->GetVisualDataForGroup(group1);
+  const TabGroupVisualData* const group2_restored_data =
+      new_tsm->GetVisualDataForGroup(group2);
+  EXPECT_EQ(group1_data.title(), group1_restored_data->title());
+  EXPECT_EQ(group1_data.color(), group1_restored_data->color());
+  EXPECT_EQ(group2_data.title(), group2_restored_data->title());
+  EXPECT_EQ(group2_data.color(), group2_restored_data->color());
 }
 
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutReset,
+                         SessionRestoreTabGroupsTest,
+                         testing::Values(false, true));
+
 // Ensure tab groups aren't restored if |features::kTabGroups| is disabled.
 // Regression test for crbug.com/983962.
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest,
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 112a50f..00875884 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -200,6 +200,22 @@
   ScheduleCommand(sessions::CreateTabGroupCommand(tab_id, group));
 }
 
+void SessionService::SetTabGroupMetadata(const SessionID& window_id,
+                                         const base::Token& group_id,
+                                         const base::string16& title,
+                                         SkColor color) {
+  if (!ShouldTrackChangesToWindow(window_id))
+    return;
+
+  // Any group metadata changes happening in a closing window can be ignored.
+  if (base::Contains(pending_window_close_ids_, window_id) ||
+      base::Contains(window_closing_ids_, window_id))
+    return;
+
+  ScheduleCommand(
+      sessions::CreateTabGroupMetadataUpdateCommand(group_id, title, color));
+}
+
 void SessionService::SetPinnedState(const SessionID& window_id,
                                     const SessionID& tab_id,
                                     bool is_pinned) {
@@ -766,6 +782,14 @@
                         tab_strip->IsTabPinned(i), tab_to_available_range);
   }
 
+  // Set the visual data for each tab group.
+  for (const TabGroupId& group_id : tab_strip->ListTabGroups()) {
+    const TabGroupVisualData* data = tab_strip->GetVisualDataForGroup(group_id);
+    base_session_service_->AppendRebuildCommand(
+        sessions::CreateTabGroupMetadataUpdateCommand(
+            group_id.token(), data->title(), data->color()));
+  }
+
   base_session_service_->AppendRebuildCommand(
       sessions::CreateSetSelectedTabInWindowCommand(
           browser->session_id(),
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index 172f18a..a9819e0 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/strings/string16.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/time/time.h"
 #include "base/token.h"
@@ -27,6 +28,7 @@
 #include "components/sessions/core/base_session_service_delegate.h"
 #include "components/sessions/core/session_service_commands.h"
 #include "components/sessions/core/tab_restore_service_client.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/ui_base_types.h"
 
 class Profile;
@@ -125,11 +127,20 @@
                            const SessionID& tab_id,
                            int new_index);
 
-  // Sets a tab's group ID, if any.
+  // Sets a tab's group ID, if any. Note that a group can't be split between
+  // multiple windows.
   void SetTabGroup(const SessionID& window_id,
                    const SessionID& tab_id,
                    base::Optional<base::Token> group);
 
+  // Updates the metadata associated with a tab group. |window_id| should be the
+  // window where the group currently resides. Note that a group can't be split
+  // between multiple windows.
+  void SetTabGroupMetadata(const SessionID& window_id,
+                           const base::Token& group_id,
+                           const base::string16& title,
+                           SkColor color);
+
   // Sets the pinned state of the tab.
   void SetPinnedState(const SessionID& window_id,
                       const SessionID& tab_id,
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index b88bdd8..01179e6 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -43,6 +43,7 @@
 #include "content/public/common/page_state.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 using content::NavigationEntry;
 using sessions::SerializedNavigationEntry;
@@ -1173,13 +1174,14 @@
 
   ASSERT_EQ(1U, windows.size());
   ASSERT_EQ(1U, windows[0]->tabs.size());
+  ASSERT_EQ(0U, windows[0]->tab_groups.size());
 
   // Verify that the recorded tab has no group.
   sessions::SessionTab* tab = windows[0]->tabs[0].get();
   EXPECT_EQ(base::nullopt, tab->group);
 }
 
-TEST_F(SessionServiceTest, TabGroupIdsSaved) {
+TEST_F(SessionServiceTest, TabGroupsSaved) {
   const auto group1_token = base::Token::CreateRandom();
   const auto group2_token = base::Token::CreateRandom();
   constexpr int kNumTabs = 5;
@@ -1198,6 +1200,7 @@
 
   ASSERT_EQ(1U, windows.size());
   ASSERT_EQ(kNumTabs, static_cast<int>(windows[0]->tabs.size()));
+  ASSERT_EQ(2U, windows[0]->tab_groups.size());
 
   for (int tab_ndx = 0; tab_ndx < kNumTabs; ++tab_ndx) {
     sessions::SessionTab* tab = windows[0]->tabs[tab_ndx].get();
@@ -1205,6 +1208,45 @@
   }
 }
 
+TEST_F(SessionServiceTest, TabGroupMetadataSaved) {
+  constexpr int kNumGroups = 2;
+  const std::array<base::Token, kNumGroups> group_ids = {
+      base::Token::CreateRandom(), base::Token::CreateRandom()};
+  const std::array<base::string16, kNumGroups> titles = {
+      base::ASCIIToUTF16("Foo"), base::ASCIIToUTF16("Bar")};
+  const std::array<SkColor, kNumGroups> colors = {SK_ColorBLUE, SK_ColorGREEN};
+
+  // Create |kNumGroups| tab groups, each with one tab.
+  for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) {
+    const SessionID tab_id =
+        CreateTabWithTestNavigationData(window_id, group_ndx);
+    service()->SetTabGroup(window_id, tab_id, group_ids[group_ndx]);
+    service()->SetTabGroupMetadata(window_id, group_ids[group_ndx],
+                                   titles[group_ndx], colors[group_ndx]);
+  }
+
+  std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
+  ReadWindows(&windows, nullptr);
+
+  ASSERT_EQ(1U, windows.size());
+  ASSERT_EQ(2U, windows[0]->tabs.size());
+  ASSERT_EQ(2U, windows[0]->tab_groups.size());
+
+  // There's no guaranteed order in |SessionWindow::tab_groups|, so use a map.
+  base::flat_map<base::Token, sessions::SessionTabGroup*> tab_groups;
+  for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) {
+    tab_groups.emplace(windows[0]->tab_groups[group_ndx]->group_id,
+                       windows[0]->tab_groups[group_ndx].get());
+  }
+
+  for (int group_ndx = 0; group_ndx < kNumGroups; ++group_ndx) {
+    const base::Token group_id = group_ids[group_ndx];
+    ASSERT_TRUE(base::Contains(tab_groups, group_id));
+    EXPECT_EQ(titles[group_ndx], tab_groups[group_id]->title);
+    EXPECT_EQ(colors[group_ndx], tab_groups[group_id]->color);
+  }
+}
+
 // Functions used by GetSessionsAndDestroy.
 namespace {
 
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
index 1037b94..f683792 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h"
 
+#include <utility>
+
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
@@ -46,10 +48,8 @@
   // Invalidate old dialog results.
   controller->last_dialog_id_++;
   controller->phone_url_ = url;
-  controller->is_loading_ = false;
-  controller->send_failed_ = false;
   controller->hide_default_handler_ = hide_default_handler;
-  controller->ShowNewDialog();
+  controller->InvalidateOldDialog();
 }
 
 // static
@@ -66,7 +66,7 @@
 
 ClickToCallSharingDialogController::ClickToCallSharingDialogController(
     content::WebContents* web_contents)
-    : web_contents_(web_contents),
+    : SharingDialogController(web_contents),
       sharing_service_(SharingServiceFactory::GetForBrowserContext(
           web_contents->GetBrowserContext())) {}
 
@@ -78,42 +78,6 @@
       IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_TITLE_LABEL);
 }
 
-void ClickToCallSharingDialogController::ShowNewDialog() {
-  if (dialog_)
-    dialog_->Hide();
-
-  // Treat the dialog as closed as the process of closing the native widget
-  // might be async.
-  dialog_ = nullptr;
-
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
-  auto* window = browser ? browser->window() : nullptr;
-  if (!window)
-    return;
-
-  dialog_ = window->ShowClickToCallDialog(web_contents_, this);
-  UpdateIcon();
-}
-
-void ClickToCallSharingDialogController::ShowErrorDialog() {
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
-  if (!browser)
-    return;
-
-  if (web_contents_ == browser->tab_strip_model()->GetActiveWebContents())
-    ShowNewDialog();
-}
-
-void ClickToCallSharingDialogController::UpdateIcon() {
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
-  auto* window = browser ? browser->window() : nullptr;
-  if (!window)
-    return;
-
-  auto* icon_container = window->GetOmniboxPageActionIconContainer();
-  if (icon_container)
-    icon_container->UpdatePageActionIcon(PageActionIconType::kClickToCall);
-}
 
 std::vector<SharingDeviceInfo>
 ClickToCallSharingDialogController::GetSyncedDevices() {
@@ -138,9 +102,7 @@
 
 void ClickToCallSharingDialogController::OnDeviceChosen(
     const SharingDeviceInfo& device) {
-  is_loading_ = true;
-  send_failed_ = false;
-  UpdateIcon();
+  StartLoading();
 
   std::string phone_number_string(phone_url_.GetContent());
   url::RawCanonOutputT<base::char16> unescaped_phone_number;
@@ -164,35 +126,26 @@
   if (dialog_id != last_dialog_id_)
     return;
 
-  is_loading_ = false;
-  send_failed_ = !success;
-  UpdateIcon();
-
-  if (!success)
-    ShowErrorDialog();
+  StopLoading(!success);
 }
 
 void ClickToCallSharingDialogController::OnAppChosen(const App& app) {
   ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(phone_url_,
-                                                         web_contents_);
-}
-
-void ClickToCallSharingDialogController::OnDialogClosed(SharingDialog* dialog) {
-  // Ignore already replaced dialogs.
-  if (dialog != dialog_)
-    return;
-
-  dialog_ = nullptr;
-  UpdateIcon();
+                                                         web_contents());
 }
 
 void ClickToCallSharingDialogController::OnHelpTextClicked() {
-  ShowSingletonTab(chrome::FindBrowserWithWebContents(web_contents_),
+  ShowSingletonTab(chrome::FindBrowserWithWebContents(web_contents()),
                    GURL(chrome::kSyncLearnMoreURL));
 }
 
-SharingDialog* ClickToCallSharingDialogController::GetDialog() const {
-  return dialog_;
+SharingDialog* ClickToCallSharingDialogController::DoShowDialog(
+    BrowserWindow* window) {
+  return window->ShowClickToCallDialog(web_contents_, this);
+}
+
+PageActionIconType ClickToCallSharingDialogController::GetIconType() {
+  return PageActionIconType::kClickToCall;
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ClickToCallSharingDialogController)
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
index 57fcab3..e885ab94 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/sharing/sharing_dialog_controller.h"
 #include "chrome/browser/sharing/sharing_service.h"
+#include "chrome/browser/ui/page_action/page_action_icon_container.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "url/gurl.h"
 
@@ -19,7 +20,6 @@
 class WebContents;
 }  // namespace content
 
-class SharingDialog;
 class SharingDeviceInfo;
 
 class ClickToCallSharingDialogController
@@ -43,24 +43,15 @@
   std::vector<App> GetApps() override;
   void OnDeviceChosen(const SharingDeviceInfo& device) override;
   void OnAppChosen(const App& app) override;
-
-  // Called by the ClickToCallDialogView when it is being closed.
-  void OnDialogClosed(SharingDialog* dialog);
+  PageActionIconType GetIconType() override;
 
   // Called by the ClickToCallDialogView when the help text got clicked.
   void OnHelpTextClicked();
 
-  // Returns the currently open ClickToCallDialog or nullptr if there is no
-  // dialog open.
-  SharingDialog* GetDialog() const;
-
-  bool is_loading() const { return is_loading_; }
-
-  bool send_failed() const { return send_failed_; }
-
  protected:
   explicit ClickToCallSharingDialogController(
       content::WebContents* web_contents);
+  SharingDialog* DoShowDialog(BrowserWindow* window) override;
 
  private:
   friend class content::WebContentsUserData<ClickToCallSharingDialogController>;
@@ -69,25 +60,14 @@
   // |success| is false and updates the omnibox icon.
   void OnMessageSentToDevice(int dialog_id, bool success);
 
-  // Shows a new ClickToCallDialogView and closes the old one.
-  void ShowNewDialog();
-
-  // Shows an error dialog if we're still on the same tab.
-  void ShowErrorDialog();
-
-  // Updates the omnibox icon if available.
-  void UpdateIcon();
-
   content::WebContents* web_contents_ = nullptr;
   SharingService* sharing_service_ = nullptr;
 
   GURL phone_url_;
-  SharingDialog* dialog_ = nullptr;
-  bool is_loading_ = false;
-  bool send_failed_ = false;
   bool hide_default_handler_ = false;
 
   // ID of the last shown dialog used to ignore events from old dialogs.
+  // TODO(yasmo): Maybe can be moved to the base class.
   int last_dialog_id_ = 0;
 
   base::WeakPtrFactory<ClickToCallSharingDialogController> weak_ptr_factory_{
diff --git a/chrome/browser/sharing/sharing_dialog_controller.cc b/chrome/browser/sharing/sharing_dialog_controller.cc
index cf46895..69b9c5d 100644
--- a/chrome/browser/sharing/sharing_dialog_controller.cc
+++ b/chrome/browser/sharing/sharing_dialog_controller.cc
@@ -4,6 +4,12 @@
 
 #include "chrome/browser/sharing/sharing_dialog_controller.h"
 
+#include <utility>
+
+#include "chrome/browser/sharing/sharing_dialog.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+
 SharingDialogController::App::App(const gfx::VectorIcon& icon,
                                   base::string16 name,
                                   std::string identifier)
@@ -12,3 +18,74 @@
 SharingDialogController::App::App(App&& other) = default;
 
 SharingDialogController::App::~App() = default;
+
+SharingDialogController::SharingDialogController(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
+
+void SharingDialogController::ShowNewDialog() {
+  if (dialog_)
+    dialog_->Hide();
+
+  // Treat the dialog as closed as the process of closing the native widget
+  // might be async.
+  dialog_ = nullptr;
+
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
+  auto* window = browser ? browser->window() : nullptr;
+  if (!window)
+    return;
+
+  dialog_ = DoShowDialog(window);
+  UpdateIcon();
+}
+
+void SharingDialogController::UpdateIcon() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
+  auto* window = browser ? browser->window() : nullptr;
+  if (!window)
+    return;
+
+  auto* icon_container = window->GetOmniboxPageActionIconContainer();
+  if (icon_container)
+    icon_container->UpdatePageActionIcon(GetIconType());
+}
+
+void SharingDialogController::OnDialogClosed(SharingDialog* dialog) {
+  // Ignore already replaced dialogs.
+  if (dialog != dialog_)
+    return;
+
+  dialog_ = nullptr;
+  UpdateIcon();
+}
+
+void SharingDialogController::ShowErrorDialog() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
+  if (!browser)
+    return;
+
+  if (web_contents_ == browser->tab_strip_model()->GetActiveWebContents())
+    ShowNewDialog();
+}
+
+void SharingDialogController::StartLoading() {
+  is_loading_ = true;
+  send_failed_ = false;
+  UpdateIcon();
+}
+
+void SharingDialogController::StopLoading(bool send_failed) {
+  is_loading_ = false;
+  send_failed_ = send_failed;
+  UpdateIcon();
+
+  if (send_failed)
+    ShowErrorDialog();
+}
+
+void SharingDialogController::InvalidateOldDialog() {
+  is_loading_ = false;
+  send_failed_ = false;
+  ShowNewDialog();
+}
diff --git a/chrome/browser/sharing/sharing_dialog_controller.h b/chrome/browser/sharing/sharing_dialog_controller.h
index fbcf69e..9f5121e 100644
--- a/chrome/browser/sharing/sharing_dialog_controller.h
+++ b/chrome/browser/sharing/sharing_dialog_controller.h
@@ -10,13 +10,20 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/ui/page_action/page_action_icon_container.h"
 
+class BrowserWindow;
 class SharingDeviceInfo;
+class SharingDialog;
 
 namespace gfx {
 struct VectorIcon;
 }  // namespace gfx
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 // The controller for desktop dialog with the list of synced devices and apps.
 class SharingDialogController {
  public:
@@ -32,7 +39,7 @@
     std::string identifier;
   };
 
-  SharingDialogController() = default;
+  explicit SharingDialogController(content::WebContents* web_contents);
   virtual ~SharingDialogController() = default;
 
   // Title of the dialog.
@@ -49,6 +56,36 @@
 
   // Called when user chooses a local app to complete the task.
   virtual void OnAppChosen(const App& app) = 0;
+
+  virtual PageActionIconType GetIconType() = 0;
+
+  // Called by the ClickToCallDialogView when it is being closed.
+  void OnDialogClosed(SharingDialog* dialog);
+  void StartLoading();
+  void StopLoading(bool send_failed);
+  void InvalidateOldDialog();
+  // Shows an error dialog if we're still on the same tab.
+  void ShowErrorDialog();
+  // Returns the currently open SharingDialog or nullptr if there is no
+  // dialog open.
+  SharingDialog* dialog() const { return dialog_; }
+  bool is_loading() const { return is_loading_; }
+  bool send_failed() const { return send_failed_; }
+  content::WebContents* web_contents() const { return web_contents_; }
+
+ protected:
+  virtual SharingDialog* DoShowDialog(BrowserWindow* window) = 0;
+
+ private:
+  // Updates the omnibox icon if available.
+  void UpdateIcon();
+  // Shows a new ClickToCallDialogView and closes the old one.
+  void ShowNewDialog();
+
+  SharingDialog* dialog_ = nullptr;
+  bool is_loading_ = false;
+  bool send_failed_ = false;
+  content::WebContents* web_contents_ = nullptr;
 };
 
 #endif  // CHROME_BROWSER_SHARING_SHARING_DIALOG_CONTROLLER_H_
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index 46bf598..d59b982c 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -725,8 +725,8 @@
     return;
   }
 
-  base::CreateCOMSTATaskRunnerWithTraits(
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT})
+  base::CreateCOMSTATaskRunner(
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT})
       ->PostTask(FROM_HERE, base::BindOnce(&MigrateTaskbarPinsCallback,
                                            taskbar_path, implicit_apps_path));
 }
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index f271b01..76036ce8 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
@@ -63,6 +64,19 @@
   }
 }
 
+security_state::SafetyTipStatus GetSecurityStateSafetyTipType(
+    safety_tips::SafetyTipType type) {
+  switch (type) {
+    case safety_tips::SafetyTipType::kNone:
+      return security_state::SafetyTipStatus::SAFETY_TIP_STATUS_NONE;
+    case safety_tips::SafetyTipType::kBadReputation:
+      return security_state::SafetyTipStatus::SAFETY_TIP_STATUS_BAD_REPUTATION;
+    default:
+      NOTREACHED();
+      return security_state::SafetyTipStatus::SAFETY_TIP_STATUS_NONE;
+  }
+}
+
 }  // namespace
 
 using password_manager::metrics_util::PasswordType;
@@ -88,6 +102,14 @@
   // information is still being initialized, thus no need to check for that.
   state->malicious_content_status = GetMaliciousContentStatus();
 
+  safety_tips::ReputationWebContentsObserver* reputation_web_contents_observer =
+      safety_tips::ReputationWebContentsObserver::FromWebContents(
+          web_contents());
+  state->safety_tip_status =
+      reputation_web_contents_observer
+          ? GetSecurityStateSafetyTipType(
+                reputation_web_contents_observer->last_shown_safety_tip_type())
+          : security_state::SafetyTipStatus::SAFETY_TIP_STATUS_NONE;
   return state;
 }
 
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index 12fbbbac..6236fa90 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -46,6 +46,7 @@
 #include "chrome/common/channel_info.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/browser_sync/browser_sync_switches.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
@@ -261,7 +262,12 @@
     // intervention). We can get rid of the browser_default eventually, but
     // need to take care that ProfileSyncService doesn't get tripped up between
     // those two cases. Bug 88109.
-    init_params.start_behavior = browser_defaults::kSyncAutoStarts
+    bool is_auto_start = browser_defaults::kSyncAutoStarts;
+#if defined(OS_ANDROID)
+    if (base::FeatureList::IsEnabled(switches::kSyncManualStartAndroid))
+      is_auto_start = false;
+#endif
+    init_params.start_behavior = is_auto_start
                                      ? syncer::ProfileSyncService::AUTO_START
                                      : syncer::ProfileSyncService::MANUAL_START;
   }
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 17f680a..8da7549 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1067,6 +1067,19 @@
                      selection.new_model.active(), selection.reason);
 }
 
+void Browser::OnTabGroupVisualDataChanged(
+    TabStripModel* tab_strip_model,
+    TabGroupId group,
+    const TabGroupVisualData* visual_data) {
+  SessionService* const session_service =
+      SessionServiceFactory::GetForProfile(profile_);
+  if (session_service) {
+    session_service->SetTabGroupMetadata(session_id(), group.token(),
+                                         visual_data->title(),
+                                         visual_data->color());
+  }
+}
+
 void Browser::TabPinnedStateChanged(TabStripModel* tab_strip_model,
                                     WebContents* contents,
                                     int index) {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index c4ac889..df292eaf 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -509,6 +509,10 @@
       TabStripModel* tab_strip_model,
       const TabStripModelChange& change,
       const TabStripSelectionChange& selection) override;
+  void OnTabGroupVisualDataChanged(
+      TabStripModel* tab_strip_model,
+      TabGroupId group,
+      const TabGroupVisualData* visual_data) override;
   void TabPinnedStateChanged(TabStripModel* tab_strip_model,
                              content::WebContents* contents,
                              int index) override;
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
index b939b741..a60816f 100644
--- a/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
@@ -47,7 +47,10 @@
     media_session::mojom::AudioFocusRequestStatePtr session) {
   const std::string id = session->request_id->ToString();
 
-  if (base::Contains(sessions_, id))
+  // If we have an existing unfrozen item then this is a duplicate call and
+  // we should ignore it.
+  auto it = sessions_.find(id);
+  if (it != sessions_.end() && !it->second.frozen())
     return;
 
   media_session::mojom::MediaControllerPtr controller;
@@ -59,16 +62,27 @@
         mojo::MakeRequest(&controller), *session->request_id);
   }
 
-  sessions_.emplace(
-      std::piecewise_construct, std::forward_as_tuple(id),
-      std::forward_as_tuple(
-          this, id, session->source_name.value_or(std::string()),
-          std::move(controller), std::move(session->session_info)));
+  if (it != sessions_.end()) {
+    // If the notification was previously frozen then we should reset the
+    // controller because the mojo pipe would have been reset.
+    it->second.SetController(std::move(controller),
+                             std::move(session->session_info));
+  } else {
+    sessions_.emplace(
+        std::piecewise_construct, std::forward_as_tuple(id),
+        std::forward_as_tuple(
+            this, id, session->source_name.value_or(std::string()),
+            std::move(controller), std::move(session->session_info)));
+  }
 }
 
 void MediaDialogController::OnFocusLost(
     media_session::mojom::AudioFocusRequestStatePtr session) {
-  sessions_.erase(session->request_id->ToString());
+  auto it = sessions_.find(session->request_id->ToString());
+  if (it == sessions_.end())
+    return;
+
+  it->second.Freeze();
 }
 
 void MediaDialogController::ShowNotification(const std::string& id) {
@@ -85,6 +99,15 @@
   delegate_->HideMediaSession(id);
 }
 
+void MediaDialogController::RemoveItem(const std::string& id) {
+  sessions_.erase(id);
+}
+
+scoped_refptr<base::SequencedTaskRunner> MediaDialogController::GetTaskRunner()
+    const {
+  return task_runner_for_testing_;
+}
+
 void MediaDialogController::OnReceivedAudioFocusRequests(
     std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) {
   for (auto& session : sessions)
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller.h b/chrome/browser/ui/global_media_controls/media_dialog_controller.h
index f6d9a9d..22ed04e0b 100644
--- a/chrome/browser/ui/global_media_controls/media_dialog_controller.h
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller.h
@@ -45,6 +45,8 @@
   // media_message_center::MediaNotificationController implementation.
   void ShowNotification(const std::string& id) override;
   void HideNotification(const std::string& id) override;
+  void RemoveItem(const std::string& id) override;
+  scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
 
  private:
   friend class MediaDialogControllerTest;
@@ -69,6 +71,9 @@
   mojo::Receiver<media_session::mojom::AudioFocusObserver>
       audio_focus_observer_receiver_{this};
 
+  // Task runner used for testing.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing_;
+
   base::WeakPtrFactory<MediaDialogController> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(MediaDialogController);
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
index e9e6fa9..54c6acab 100644
--- a/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
 #include "components/media_message_center/media_notification_item.h"
@@ -40,11 +41,15 @@
 
 class MediaDialogControllerTest : public testing::Test {
  public:
-  MediaDialogControllerTest() = default;
+  MediaDialogControllerTest()
+      : task_runner_(new base::TestMockTimeTaskRunner(
+            base::TestMockTimeTaskRunner::Type::kStandalone)) {}
+
   ~MediaDialogControllerTest() override = default;
 
   void SetUp() override {
     controller_ = std::make_unique<MediaDialogController>(nullptr, &delegate_);
+    controller_->task_runner_for_testing_ = task_runner_.get();
   }
 
  protected:
@@ -91,9 +96,21 @@
     controller_->OnReceivedAudioFocusRequests(std::move(requests));
   }
 
+  void SimulateFreezeTimerExpired() {
+    task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(2500));
+  }
+
+  bool IsSessionFrozen(const base::UnguessableToken& id) const {
+    auto item_itr = controller_->sessions_.find(id.ToString());
+    EXPECT_NE(controller_->sessions_.end(), item_itr);
+    return item_itr->second.frozen();
+  }
+
   MockMediaDialogControllerDelegate& delegate() { return delegate_; }
 
  private:
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+
   MockMediaDialogControllerDelegate delegate_;
   std::unique_ptr<MediaDialogController> controller_;
 
@@ -107,12 +124,20 @@
 
   SimulateFocusGained(id, true);
   SimulateNecessaryMetadata(id);
+  EXPECT_FALSE(IsSessionFrozen(id));
 
   // Ensure that the session was shown.
   testing::Mock::VerifyAndClearExpectations(&delegate());
 
-  EXPECT_CALL(delegate(), HideMediaSession(id.ToString()));
+  EXPECT_CALL(delegate(), HideMediaSession(id.ToString())).Times(0);
   SimulateFocusLost(id);
+  EXPECT_TRUE(IsSessionFrozen(id));
+
+  // Ensure that the session was not hidden.
+  testing::Mock::VerifyAndClearExpectations(&delegate());
+
+  EXPECT_CALL(delegate(), HideMediaSession(id.ToString()));
+  SimulateFreezeTimerExpired();
 }
 
 TEST_F(MediaDialogControllerTest, DoesNotShowUncontrollableSession) {
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index b0c14e4..be73c58 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -332,6 +332,7 @@
       site_url_(url),
       site_identity_status_(SITE_IDENTITY_STATUS_UNKNOWN),
       safe_browsing_status_(SAFE_BROWSING_STATUS_NONE),
+      safety_tip_status_(security_state::SAFETY_TIP_STATUS_NONE),
       site_connection_status_(SITE_CONNECTION_STATUS_UNKNOWN),
       show_ssl_decision_revoke_button_(false),
       content_settings_(HostContentSettingsMapFactory::GetForProfile(profile)),
@@ -763,6 +764,13 @@
 #endif
   }
 
+  if (visible_security_state.safety_tip_status !=
+      security_state::SAFETY_TIP_STATUS_NONE) {
+    site_details_message_ = l10n_util::GetStringUTF16(
+        IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION);
+    safety_tip_status_ = visible_security_state.safety_tip_status;
+  }
+
   // Site Connection
   // We consider anything less than 80 bits encryption to be weak encryption.
   // TODO(wtc): Bug 1198735: report mixed/unsafe content for unencrypted and
@@ -971,6 +979,7 @@
   info.connection_status_description = UTF16ToUTF8(site_connection_details_);
   info.identity_status = site_identity_status_;
   info.safe_browsing_status = safe_browsing_status_;
+  info.safety_tip_status = safety_tip_status_;
   info.identity_status_description = UTF16ToUTF8(site_details_message_);
   info.certificate = certificate_;
   info.show_ssl_decision_revoke_button = show_ssl_decision_revoke_button_;
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 25dc0e2..f570ed6 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -274,6 +274,9 @@
   // Safe Browsing status of the website.
   SafeBrowsingStatus safe_browsing_status_;
 
+  // Safety tip status of the website.
+  security_state::SafetyTipStatus safety_tip_status_;
+
   // For secure connection |certificate_| is set to the server certificate.
   scoped_refptr<net::X509Certificate> certificate_;
 
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index ffd5b70..340f335e 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -235,6 +235,7 @@
 PageInfoUI::IdentityInfo::IdentityInfo()
     : identity_status(PageInfo::SITE_IDENTITY_STATUS_UNKNOWN),
       safe_browsing_status(PageInfo::SAFE_BROWSING_STATUS_NONE),
+      safety_tip_status(security_state::SAFETY_TIP_STATUS_NONE),
       connection_status(PageInfo::SITE_CONNECTION_STATUS_UNKNOWN),
       show_ssl_decision_revoke_button(false),
       show_change_password_buttons(false) {}
@@ -284,6 +285,13 @@
                                        IDS_PAGE_INFO_BILLING_DETAILS);
   }
 
+  if (identity_info.safety_tip_status ==
+      security_state::SAFETY_TIP_STATUS_BAD_REPUTATION) {
+    return CreateSecurityDescription(
+        SecuritySummaryColor::RED, IDS_PAGE_INFO_SAFETY_TIP_SUMMARY,
+        IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION);
+  }
+
   switch (identity_info.identity_status) {
     case PageInfo::SITE_IDENTITY_STATUS_INTERNAL_PAGE:
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/ui/page_info/page_info_ui.h b/chrome/browser/ui/page_info/page_info_ui.h
index 6f96254..734c633 100644
--- a/chrome/browser/ui/page_info/page_info_ui.h
+++ b/chrome/browser/ui/page_info/page_info_ui.h
@@ -112,6 +112,8 @@
     PageInfo::SiteIdentityStatus identity_status;
     // Site's Safe Browsing status.
     PageInfo::SafeBrowsingStatus safe_browsing_status;
+    // Site's safety tip status.
+    security_state::SafetyTipStatus safety_tip_status;
     // Textual description of the site's identity status that is displayed to
     // the user.
     std::string identity_status_description;
diff --git a/chrome/browser/ui/views/chrome_web_dialog_view.cc b/chrome/browser/ui/views/chrome_web_dialog_view.cc
index ef11e5a4..8e5c87ff 100644
--- a/chrome/browser/ui/views/chrome_web_dialog_view.cc
+++ b/chrome/browser/ui/views/chrome_web_dialog_view.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
 #include "ui/views/controls/webview/web_dialog_view.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/multi_user_window_manager.h"
@@ -83,4 +84,32 @@
   return window;
 }
 
+gfx::NativeWindow ShowWebDialogWithBounds(gfx::NativeView parent,
+                                          content::BrowserContext* context,
+                                          ui::WebDialogDelegate* delegate,
+                                          const gfx::Rect& bounds) {
+  // Use custom dialog frame instead of platform frame when possible.
+  bool use_dialog_frame = views::DialogDelegate::CanSupportCustomFrame(parent);
+  views::WebDialogView* view = new views::WebDialogView(
+      context, delegate, std::make_unique<ChromeWebContentsHandler>(),
+      use_dialog_frame);
+
+  views::Widget::InitParams params;
+  params.delegate = view;
+  params.bounds = bounds;
+  params.parent = parent;
+  if (use_dialog_frame) {
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+    params.remove_standard_frame = true;
+#if !defined(OS_MACOSX)
+    // Except on Mac, the bubble frame includes its own shadow; remove any
+    // native shadowing. On Mac, the window server provides the shadow.
+    params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
+#endif
+  }
+
+  gfx::NativeWindow window = ShowWebDialogWidget(std::move(params), view);
+  return window;
+}
+
 }  // namespace chrome
diff --git a/chrome/browser/ui/views/chrome_web_dialog_view.h b/chrome/browser/ui/views/chrome_web_dialog_view.h
index ca99a5a..04dccca 100644
--- a/chrome/browser/ui/views/chrome_web_dialog_view.h
+++ b/chrome/browser/ui/views/chrome_web_dialog_view.h
@@ -26,6 +26,15 @@
     ui::WebDialogDelegate* delegate,
     base::Optional<views::Widget::InitParams> extra_params);
 
+// The implementation is more aligned with the appearance of constrained
+// web dialog.
+// TODO(weili): Solely use this function on non-ChromeOS platform, and
+// above ShowWebDialogWithParams() on ChromeOS. Or merge these two if possible.
+gfx::NativeWindow ShowWebDialogWithBounds(gfx::NativeView parent,
+                                          content::BrowserContext* context,
+                                          ui::WebDialogDelegate* delegate,
+                                          const gfx::Rect& bounds);
+
 }  // namespace chrome
 
 #endif  // CHROME_BROWSER_UI_VIEWS_CHROME_WEB_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
index 9f1a4f0..c2a8e91 100644
--- a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.cc
@@ -6,7 +6,9 @@
 
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/native_theme/native_theme.h"
@@ -20,6 +22,9 @@
   button_controller()->set_notify_action(
       views::ButtonController::NotifyAction::NOTIFY_ON_PRESS);
   EnableCanvasFlippingForRTLUI(false);
+  SetTooltipText(
+      l10n_util::GetStringUTF16(IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT));
+
   ToolbarButton::Init();
 
   // We start hidden and only show once |controller_| tells us to.
diff --git a/chrome/browser/ui/views/hats/hats_web_dialog.cc b/chrome/browser/ui/views/hats/hats_web_dialog.cc
index eb0af7d..e3eb17d3 100644
--- a/chrome/browser/ui/views/hats/hats_web_dialog.cc
+++ b/chrome/browser/ui/views/hats/hats_web_dialog.cc
@@ -27,8 +27,8 @@
 namespace {
 
 // Default width/height of the dialog in screen size.
-const int kDefaultHatsDialogWidth = 400;
-const int kDefaultHatsDialogHeight = 420;
+const int kDefaultHatsDialogWidth = 448;
+const int kDefaultHatsDialogHeight = 440;
 
 // Placeholder strings in html file to be replaced when the file is loaded.
 constexpr char kScriptSrcReplacementToken[] = "$SCRIPT_SRC";
@@ -84,15 +84,13 @@
   DCHECK(location_bar);
   gfx::Rect bounds(location_bar->bounds());
   views::View::ConvertRectToScreen(browser_view->toolbar(), &bounds);
-  views::Widget::InitParams params;
-  params.bounds = gfx::Rect(
+  bounds = gfx::Rect(
       bounds.x() +
           std::max(0, bounds.width() / 2 - kDefaultHatsDialogWidth / 2),
       bounds.bottom() - views::BubbleBorder::GetBorderAndShadowInsets().top(),
       kDefaultHatsDialogWidth, kDefaultHatsDialogHeight);
-  chrome::ShowWebDialogWithParams(
-      browser_view->GetWidget()->GetNativeView(), profile, hats_dialog,
-      base::make_optional<views::Widget::InitParams>(std::move(params)));
+  chrome::ShowWebDialogWithBounds(browser_view->GetWidget()->GetNativeView(),
+                                  profile, hats_dialog, bounds);
 }
 
 HatsWebDialog::HatsWebDialog(const std::string& site_id) : site_id_(site_id) {
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
index 984acaa..c9ee0354 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -10,17 +10,22 @@
 #include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
 #include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h"
 #include "chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "ui/accessibility/ax_action_data.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -33,6 +38,11 @@
 // An engagement score below MEDIUM.
 const int kLowEngagement = 1;
 
+class ClickEvent : public ui::Event {
+ public:
+  ClickEvent() : ui::Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {}
+};
+
 // Simulates a link click navigation. We don't use
 // ui_test_utils::NavigateToURL(const GURL&) because it simulates the user
 // typing the URL, causing the site to have a site engagement score of at
@@ -84,6 +94,38 @@
   NavigateToURLSync(browser, navigated_url);
 }
 
+// Clicks the location icon to open the page info bubble.
+void OpenPageInfoBubble(Browser* browser) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
+  LocationIconView* location_icon_view =
+      browser_view->toolbar()->location_bar()->location_icon_view();
+  ASSERT_TRUE(location_icon_view);
+  ClickEvent event;
+  location_icon_view->ShowBubble(event);
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleViewBase::GetPageInfoBubble();
+  EXPECT_NE(nullptr, page_info);
+  page_info->set_close_on_deactivate(false);
+}
+
+void CheckPageInfoShowsSafetyTipInfo(Browser* browser) {
+  OpenPageInfoBubble(browser);
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleViewBase::GetPageInfoBubble();
+  CHECK(page_info);
+  EXPECT_EQ(page_info->GetWindowTitle(),
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY));
+}
+
+void CheckPageInfoDoesNotShowSafetyTipInfo(Browser* browser) {
+  OpenPageInfoBubble(browser);
+  views::BubbleDialogDelegateView* page_info =
+      PageInfoBubbleViewBase::GetPageInfoBubble();
+  CHECK(page_info);
+  EXPECT_NE(page_info->GetWindowTitle(),
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY));
+}
+
 }  // namespace
 
 class SafetyTipPageInfoBubbleViewBrowserTest : public InProcessBrowserTest {
@@ -132,6 +174,8 @@
   SetEngagementScore(browser(), kNavigatedUrl, kHighEngagement);
   NavigateToURLSync(browser(), kNavigatedUrl);
   EXPECT_FALSE(IsUIShowing());
+
+  CheckPageInfoDoesNotShowSafetyTipInfo(browser());
 }
 
 // Until we have heuristics, trigger on all low engagement sites.
@@ -141,6 +185,8 @@
   SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
   NavigateToURLSync(browser(), kNavigatedUrl);
   EXPECT_TRUE(IsUIShowing());
+
+  CheckPageInfoShowsSafetyTipInfo(browser());
 }
 
 // After the user clicks 'leave site', the user should end up on a safe domain.
@@ -153,6 +199,8 @@
   EXPECT_FALSE(IsUIShowing());
   EXPECT_NE(kNavigatedUrl,
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+
+  CheckPageInfoDoesNotShowSafetyTipInfo(browser());
 }
 
 // If the user clicks 'leave site', the warning should re-appear when the user
@@ -169,6 +217,8 @@
   EXPECT_TRUE(IsUIShowing());
   EXPECT_EQ(kNavigatedUrl,
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+
+  CheckPageInfoShowsSafetyTipInfo(browser());
 }
 
 // After the user closes the warning, they should still be on the same domain.
@@ -181,6 +231,8 @@
   EXPECT_FALSE(IsUIShowing());
   EXPECT_EQ(kNavigatedUrl,
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+
+  CheckPageInfoShowsSafetyTipInfo(browser());
 }
 
 // If the user closes the bubble, the warning should not re-appear when the user
@@ -197,4 +249,22 @@
   EXPECT_FALSE(IsUIShowing());
   EXPECT_EQ(kNavigatedUrl,
             browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+
+  CheckPageInfoDoesNotShowSafetyTipInfo(browser());
+}
+
+// Non main-frame navigations should be ignored.
+IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest,
+                       IgnoreIFrameNavigations) {
+  const GURL kNavigatedUrl =
+      embedded_test_server()->GetURL("a.com", "/iframe_cross_site.html");
+  const GURL kFrameUrl =
+      embedded_test_server()->GetURL("b.com", "/title1.html");
+  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
+  SetEngagementScore(browser(), kFrameUrl, kHighEngagement);
+
+  NavigateToURLSync(browser(), kNavigatedUrl);
+  EXPECT_TRUE(IsUIShowing());
+
+  CheckPageInfoShowsSafetyTipInfo(browser());
 }
diff --git a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
index f1a371eb..6071ca5 100644
--- a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
+++ b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
@@ -25,9 +25,8 @@
 PasswordAutoSignInView::PasswordAutoSignInView(
     content::WebContents* web_contents,
     views::View* anchor_view,
-    const gfx::Point& anchor_point,
     DisplayReason reason)
-    : PasswordBubbleViewBase(web_contents, anchor_view, anchor_point, reason) {
+    : PasswordBubbleViewBase(web_contents, anchor_view, reason) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
   const autofill::PasswordForm& form = model()->pending_password();
 
diff --git a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.h b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.h
index 8b603dd7..e2ea5d14 100644
--- a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.h
+++ b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.h
@@ -17,10 +17,9 @@
 class PasswordAutoSignInView : public PasswordBubbleViewBase,
                                public views::ButtonListener {
  public:
-  explicit PasswordAutoSignInView(content::WebContents* web_contents,
-                                  views::View* anchor_view,
-                                  const gfx::Point& anchor_point,
-                                  DisplayReason reason);
+  PasswordAutoSignInView(content::WebContents* web_contents,
+                         views::View* anchor_view,
+                         DisplayReason reason);
 
 #if defined(UNIT_TEST)
   static void set_auto_signin_toast_timeout(int seconds) {
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
index 463b433..da8ef01 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
@@ -44,7 +44,7 @@
   }
 
   PasswordBubbleViewBase* bubble =
-      CreateBubble(web_contents, anchor_view, gfx::Point(), reason);
+      CreateBubble(web_contents, anchor_view, reason);
   DCHECK(bubble);
   DCHECK(bubble == g_manage_passwords_bubble_);
 
@@ -82,25 +82,20 @@
 PasswordBubbleViewBase* PasswordBubbleViewBase::CreateBubble(
     content::WebContents* web_contents,
     views::View* anchor_view,
-    const gfx::Point& anchor_point,
     DisplayReason reason) {
   PasswordBubbleViewBase* view = nullptr;
   password_manager::ui::State model_state =
       PasswordsModelDelegateFromWebContents(web_contents)->GetState();
   if (model_state == password_manager::ui::MANAGE_STATE) {
-    view =
-        new PasswordItemsView(web_contents, anchor_view, anchor_point, reason);
+    view = new PasswordItemsView(web_contents, anchor_view, reason);
   } else if (model_state == password_manager::ui::AUTO_SIGNIN_STATE) {
-    view = new PasswordAutoSignInView(web_contents, anchor_view, anchor_point,
-                                      reason);
+    view = new PasswordAutoSignInView(web_contents, anchor_view, reason);
   } else if (model_state == password_manager::ui::CONFIRMATION_STATE) {
-    view = new PasswordSaveConfirmationView(web_contents, anchor_view,
-                                            anchor_point, reason);
+    view = new PasswordSaveConfirmationView(web_contents, anchor_view, reason);
   } else if (model_state ==
                  password_manager::ui::PENDING_PASSWORD_UPDATE_STATE ||
              model_state == password_manager::ui::PENDING_PASSWORD_STATE) {
-    view = new PasswordPendingView(web_contents, anchor_view, anchor_point,
-                                   reason);
+    view = new PasswordPendingView(web_contents, anchor_view, reason);
   } else {
     NOTREACHED();
   }
@@ -137,9 +132,8 @@
 PasswordBubbleViewBase::PasswordBubbleViewBase(
     content::WebContents* web_contents,
     views::View* anchor_view,
-    const gfx::Point& anchor_point,
     DisplayReason reason)
-    : LocationBarBubbleDelegateView(anchor_view, anchor_point, web_contents),
+    : LocationBarBubbleDelegateView(anchor_view, gfx::Point(), web_contents),
       model_(PasswordsModelDelegateFromWebContents(web_contents),
              reason == AUTOMATIC ? ManagePasswordsBubbleModel::AUTOMATIC
                                  : ManagePasswordsBubbleModel::USER_ACTION),
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.h b/chrome/browser/ui/views/passwords/password_bubble_view_base.h
index ab9e2ef..a0b5cc76 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_base.h
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.h
@@ -39,7 +39,6 @@
   static PasswordBubbleViewBase* CreateBubble(
       content::WebContents* web_contents,
       views::View* anchor_view,
-      const gfx::Point& anchor_point,
       DisplayReason reason);
 
   // Closes the existing bubble.
@@ -61,7 +60,6 @@
  protected:
   PasswordBubbleViewBase(content::WebContents* web_contents,
                          views::View* anchor_view,
-                         const gfx::Point& anchor_point,
                          DisplayReason reason);
 
   ~PasswordBubbleViewBase() override;
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc
index 215f27d..92c6c914 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -233,9 +233,8 @@
 
 PasswordItemsView::PasswordItemsView(content::WebContents* web_contents,
                                      views::View* anchor_view,
-                                     const gfx::Point& anchor_point,
                                      DisplayReason reason)
-    : PasswordBubbleViewBase(web_contents, anchor_view, anchor_point, reason) {
+    : PasswordBubbleViewBase(web_contents, anchor_view, reason) {
   DCHECK_EQ(password_manager::ui::MANAGE_STATE, model()->state());
 
   if (model()->local_credentials().empty()) {
diff --git a/chrome/browser/ui/views/passwords/password_items_view.h b/chrome/browser/ui/views/passwords/password_items_view.h
index d992a7f..42a92d1 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.h
+++ b/chrome/browser/ui/views/passwords/password_items_view.h
@@ -38,7 +38,6 @@
  public:
   PasswordItemsView(content::WebContents* web_contents,
                     views::View* anchor_view,
-                    const gfx::Point& anchor_point,
                     DisplayReason reason);
   ~PasswordItemsView() override;
 
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc
index 3b21bee..cdb0d29 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.cc
+++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -201,9 +201,8 @@
 
 PasswordPendingView::PasswordPendingView(content::WebContents* web_contents,
                                          views::View* anchor_view,
-                                         const gfx::Point& anchor_point,
                                          DisplayReason reason)
-    : PasswordBubbleViewBase(web_contents, anchor_view, anchor_point, reason),
+    : PasswordBubbleViewBase(web_contents, anchor_view, reason),
       is_update_bubble_(model()->state() ==
                         password_manager::ui::PENDING_PASSWORD_UPDATE_STATE),
       sign_in_promo_(nullptr),
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.h b/chrome/browser/ui/views/passwords/password_pending_view.h
index a2adc5d..3f649a2 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.h
+++ b/chrome/browser/ui/views/passwords/password_pending_view.h
@@ -26,7 +26,6 @@
  public:
   PasswordPendingView(content::WebContents* web_contents,
                       views::View* anchor_view,
-                      const gfx::Point& anchor_point,
                       DisplayReason reason);
 
   views::View* GetUsernameTextfieldForTest() const;
diff --git a/chrome/browser/ui/views/passwords/password_save_confirmation_view.cc b/chrome/browser/ui/views/passwords/password_save_confirmation_view.cc
index e319957d..52ce2fed 100644
--- a/chrome/browser/ui/views/passwords/password_save_confirmation_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_confirmation_view.cc
@@ -16,9 +16,8 @@
 PasswordSaveConfirmationView::PasswordSaveConfirmationView(
     content::WebContents* web_contents,
     views::View* anchor_view,
-    const gfx::Point& anchor_point,
     DisplayReason reason)
-    : PasswordBubbleViewBase(web_contents, anchor_view, anchor_point, reason) {
+    : PasswordBubbleViewBase(web_contents, anchor_view, reason) {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   auto label = std::make_unique<views::StyledLabel>(
diff --git a/chrome/browser/ui/views/passwords/password_save_confirmation_view.h b/chrome/browser/ui/views/passwords/password_save_confirmation_view.h
index 3ccbf97..0178a02d 100644
--- a/chrome/browser/ui/views/passwords/password_save_confirmation_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_confirmation_view.h
@@ -16,7 +16,6 @@
  public:
   explicit PasswordSaveConfirmationView(content::WebContents* web_contents,
                                         views::View* anchor_view,
-                                        const gfx::Point& anchor_point,
                                         DisplayReason reason);
   ~PasswordSaveConfirmationView() override;
 
diff --git a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_icon_view.cc b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_icon_view.cc
index a769308..a765e16 100644
--- a/chrome/browser/ui/views/sharing/click_to_call/click_to_call_icon_view.cc
+++ b/chrome/browser/ui/views/sharing/click_to_call/click_to_call_icon_view.cc
@@ -34,9 +34,8 @@
 
 views::BubbleDialogDelegateView* ClickToCallIconView::GetBubble() const {
   auto* controller = GetControllerFromWebContents(GetWebContents());
-  return controller
-             ? static_cast<ClickToCallDialogView*>(controller->GetDialog())
-             : nullptr;
+  return controller ? static_cast<ClickToCallDialogView*>(controller->dialog())
+                    : nullptr;
 }
 
 bool ClickToCallIconView::Update() {
diff --git a/chrome/browser/vr/ui_scene_constants.h b/chrome/browser/vr/ui_scene_constants.h
index 63f6670..ef202ef9 100644
--- a/chrome/browser/vr/ui_scene_constants.h
+++ b/chrome/browser/vr/ui_scene_constants.h
@@ -285,6 +285,8 @@
 static constexpr float kOverflowMenuItemXPadding = 0.024f;
 static constexpr float kOverflowMenuMaxSpan = 0.384f - kOverflowMenuYPadding;
 
+static constexpr const char* kCrashVrBrowserUrl = "chrome://crash-vr-browser";
+
 }  // namespace vr
 
 #endif  // CHROME_BROWSER_VR_UI_SCENE_CONSTANTS_H_
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index b6992b9..45afa74 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -1103,6 +1103,16 @@
   return kToastTimeoutSeconds;
 }
 
+NOINLINE void CrashIntentionally() {
+  LOG(ERROR) << "Crashing VR browser";
+
+  static int static_variable_to_make_this_function_unique = 0;
+  base::debug::Alias(&static_variable_to_make_this_function_unique);
+
+  volatile int* zero = nullptr;
+  *zero = 0;
+}
+
 }  // namespace
 
 UiSceneCreator::UiSceneCreator(UiBrowserInterface* browser,
@@ -2633,6 +2643,8 @@
   omnibox_text_field->set_input_committed_callback(base::BindRepeating(
       [](Model* model, UiBrowserInterface* browser, Ui* ui,
          const EditedText& text) {
+        if (text.current.text == base::UTF8ToUTF16(kCrashVrBrowserUrl))
+          CrashIntentionally();
         if (!model->omnibox_suggestions.empty()) {
           browser->Navigate(model->omnibox_suggestions.front().destination,
                             NavigationMethod::kOmniboxUrlEntry);
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 67f4d93b..348f63b 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -715,11 +715,6 @@
 // installed through policy.
 const char kDisableLoginScreenApps[] = "disable-login-screen-apps";
 
-// Provides the name of the mojo service running in a mash utility process.
-// NOTE: Used by the Chrome OS crash_reporter to identify mash processes. If you
-// change or remove the flag please update platform2/crash_reporter.
-const char kMashServiceName[] = "mash-service-name";
-
 // Use a short (1 second) timeout for merge session loader throttle testing.
 const char kShortMergeSessionTimeoutForTest[] =
     "short-merge-session-timeout-for-test";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index d1333f4..8958f38 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -211,7 +211,6 @@
 extern const char kCroshCommand[];
 extern const char kDisableLoggingRedirect[];
 extern const char kDisableLoginScreenApps[];
-extern const char kMashServiceName[];
 extern const char kShortMergeSessionTimeoutForTest[];
 extern const char kSchedulerConfiguration[];
 extern const char kSchedulerConfigurationConservative[];
diff --git a/chrome/test/android/test_trusted_web_activity/BUILD.gn b/chrome/test/android/test_trusted_web_activity/BUILD.gn
index 36dac01b..1201e70 100644
--- a/chrome/test/android/test_trusted_web_activity/BUILD.gn
+++ b/chrome/test/android/test_trusted_web_activity/BUILD.gn
@@ -14,6 +14,6 @@
     "src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java",
   ]
   deps = [
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java"
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
   ]
 }
diff --git a/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java b/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
index f660b53..5735160d 100644
--- a/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
+++ b/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
@@ -5,8 +5,7 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.app.Notification;
-
-import androidx.browser.trusted.TrustedWebActivityService;
+import android.support.customtabs.trusted.TrustedWebActivityService;
 
 /**
  * A TrustedWebActivityService to be used in TrustedWebActivityClientTest.
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 0cc3ae1c..9024439 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -15,9 +15,6 @@
     'SlowLoadingPageTest.testRefreshShouldBlockUntilPageLoads',
     'PageLoadingTest.testShouldTimeoutIfAPageTakesTooLongToRefresh',
 
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2854
-    'ChromeOptionsFunctionalTest.canSetAcceptInsecureCerts',
-
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2858
     'ElementFindingTest.testFindingMultipleElementsByInvalidClassNameShouldThrow',
     'ElementFindingTest.testFindingMultipleElementsByCompoundClassNameShouldThrow',
@@ -34,15 +31,30 @@
     'CombinedInputActionsTest.testChordControlCutAndPaste',
 
     #https://bugs.chromium.org/p/chromedriver/issues/detail?id=2971
+    'ChromeDevToolsProfilerTest.aSimpleStartStopAndGetProfilerTest',
+    'ChromeDevToolsSecurityTest.loadInsecureWebsite',
     'ChromeDevToolsProfilerTest.sampleProfileEvents',
+    'ChromeDevToolsProfilerTest.sampleSetStartPreciseCoverageTest',
 ]
 
 _READY_TO_RUN_FILTER = [
     # marked as not yet implemented with chrome but already works
-    'ProxySettingTest.canUsePACThatOnlyProxiesCertainHosts',
+    'AlertsTest.testIncludesAlertTextInUnhandledAlertException',
+    'BasicKeyboardInterfaceTest.testSelectionSelectBySymbol',
+    'BasicKeyboardInterfaceTest.testSelectionSelectByWord',
+    'BasicMouseInterfaceTest.testCanMoveOverAndOutOfAnElement',
+    'BasicMouseInterfaceTest.testMoveMouseByOffsetOverAndOutOfAnElement',
+    'BasicMouseInterfaceTest.testMovingMouseToRelativeElementOffset',
+    'BasicMouseInterfaceTest.testMovingMouseToRelativeZeroElementOffset',
+    'CombinedInputActionsTest.testClickAfterMoveToAnElementWithAnOffsetShouldUseLastMousePosition',
+    'ContentEditableTest.appendsTextToEndOfContentEditableWithMultipleTextNodes',
     'ContentEditableTest.testShouldAppendToTinyMCE',
     'ContentEditableTest.testShouldBeAbleToTypeIntoContentEditableElementWithExistingValue',
-    'ContentEditableTest.appendsTextToEndOfContentEditableWithMultipleTextNodes',
+    'CookieImplementationTest.canHandleHttpOnlyCookie',
+    'ExecutingJavascriptTest.testShouldBeAbleToReturnADateObject',
+    'PageLoadingTest.testEagerStrategyShouldNotWaitForResources',
+    'PageLoadingTest.testEagerStrategyShouldNotWaitForResourcesOnRefresh',
+    'PageLoadingTest.testEagerStrategyShouldWaitForDocumentToBeLoaded',
     'WindowSwitchingTest.canOpenANewWindow',
 ]
 
diff --git a/chrome/test/data/pdf/accessibility/hello-world-expected-auralinux.txt b/chrome/test/data/pdf/accessibility/hello-world-expected-auralinux.txt
index ee8c26c..31e73ffa 100644
--- a/chrome/test/data/pdf/accessibility/hello-world-expected-auralinux.txt
+++ b/chrome/test/data/pdf/accessibility/hello-world-expected-auralinux.txt
@@ -1,5 +1,5 @@
 [embedded component]
-++[panel]
+++[document frame]
 ++++[landmark] name='Page 1'
 ++++++[paragraph]
 ++++++++[text] name='Hello, world!'
diff --git a/chrome/test/data/pdf/accessibility/hello-world-expected-blink.txt b/chrome/test/data/pdf/accessibility/hello-world-expected-blink.txt
index 6fad2681..850b44f4 100644
--- a/chrome/test/data/pdf/accessibility/hello-world-expected-blink.txt
+++ b/chrome/test/data/pdf/accessibility/hello-world-expected-blink.txt
@@ -1,6 +1,6 @@
 embeddedObject
-++group restriction=readOnly
-++++region name='Page 1' restriction=readOnly
+++document restriction=readOnly
+++++region name='Page 1' restriction=readOnly isPageBreakingObject=true
 ++++++paragraph restriction=readOnly
 ++++++++staticText name='Hello, world!' restriction=readOnly
 ++++++++++inlineTextBox name='Hello, world!' restriction=readOnly
diff --git a/chrome/test/data/pdf/accessibility/hello-world-expected-uia-win.txt b/chrome/test/data/pdf/accessibility/hello-world-expected-uia-win.txt
index ebda394..be9ef8c 100644
--- a/chrome/test/data/pdf/accessibility/hello-world-expected-uia-win.txt
+++ b/chrome/test/data/pdf/accessibility/hello-world-expected-uia-win.txt
@@ -1,4 +1,5 @@
 group
-++group
+++document
 ++++region Name='Page 1'
 ++++++group
+++++++++description Name='Hello, world!'
diff --git a/chrome/test/data/pdf/accessibility/hello-world-expected-win.txt b/chrome/test/data/pdf/accessibility/hello-world-expected-win.txt
index c33fc42..4bfc3dab 100644
--- a/chrome/test/data/pdf/accessibility/hello-world-expected-win.txt
+++ b/chrome/test/data/pdf/accessibility/hello-world-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_GROUPING FOCUSABLE
-++ROLE_SYSTEM_GROUPING READONLY
+++ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++++IA2_ROLE_LANDMARK name='Page 1' READONLY
 ++++++IA2_ROLE_PARAGRAPH READONLY
 ++++++++ROLE_SYSTEM_STATICTEXT name='Hello, world!' READONLY
diff --git a/chrome/test/data/pdf/accessibility/multi-page-expected-auralinux.txt b/chrome/test/data/pdf/accessibility/multi-page-expected-auralinux.txt
new file mode 100644
index 0000000..f8013d8
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page-expected-auralinux.txt
@@ -0,0 +1,8 @@
+[embedded component]
+++[document frame]
+++++[landmark] name='Page 1'
+++++++[paragraph]
+++++++++[text] name='Page 1'
+++++[landmark] name='Page 2'
+++++++[paragraph]
+++++++++[text] name='Page 2'
diff --git a/chrome/test/data/pdf/accessibility/multi-page-expected-blink.txt b/chrome/test/data/pdf/accessibility/multi-page-expected-blink.txt
new file mode 100644
index 0000000..58d374e
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page-expected-blink.txt
@@ -0,0 +1,10 @@
+embeddedObject
+++document restriction=readOnly
+++++region name='Page 1' restriction=readOnly isPageBreakingObject=true
+++++++paragraph restriction=readOnly
+++++++++staticText name='Page 1' restriction=readOnly
+++++++++++inlineTextBox name='Page 1' restriction=readOnly
+++++region name='Page 2' restriction=readOnly isPageBreakingObject=true
+++++++paragraph restriction=readOnly
+++++++++staticText name='Page 2' restriction=readOnly
+++++++++++inlineTextBox name='Page 2' restriction=readOnly
diff --git a/chrome/test/data/pdf/accessibility/multi-page-expected-mac.txt b/chrome/test/data/pdf/accessibility/multi-page-expected-mac.txt
new file mode 100644
index 0000000..d7dd6639
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page-expected-mac.txt
@@ -0,0 +1,8 @@
+AXGroup AXDescription='Page 1 Page 2'
+++AXGroup
+++++AXGroup AXDescription='Page 1'
+++++++AXGroup
+++++++++AXStaticText AXValue='Page 1'
+++++AXGroup AXDescription='Page 2'
+++++++AXGroup
+++++++++AXStaticText AXValue='Page 2'
diff --git a/chrome/test/data/pdf/accessibility/multi-page-expected-uia-win.txt b/chrome/test/data/pdf/accessibility/multi-page-expected-uia-win.txt
new file mode 100644
index 0000000..4886f10f
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page-expected-uia-win.txt
@@ -0,0 +1,8 @@
+group
+++document
+++++region Name='Page 1'
+++++++group
+++++++++description Name='Page 1'
+++++region Name='Page 2'
+++++++group
+++++++++description Name='Page 2'
diff --git a/chrome/test/data/pdf/accessibility/multi-page-expected-win.txt b/chrome/test/data/pdf/accessibility/multi-page-expected-win.txt
new file mode 100644
index 0000000..f0eaa0e
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page-expected-win.txt
@@ -0,0 +1,8 @@
+ROLE_SYSTEM_GROUPING FOCUSABLE
+++ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++++IA2_ROLE_LANDMARK name='Page 1' READONLY
+++++++IA2_ROLE_PARAGRAPH READONLY
+++++++++ROLE_SYSTEM_STATICTEXT name='Page 1' READONLY
+++++IA2_ROLE_LANDMARK name='Page 2' READONLY
+++++++IA2_ROLE_PARAGRAPH READONLY
+++++++++ROLE_SYSTEM_STATICTEXT name='Page 2' READONLY
diff --git a/chrome/test/data/pdf/accessibility/multi-page.in b/chrome/test/data/pdf/accessibility/multi-page.in
new file mode 100644
index 0000000..406f338
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 250 ]
+  /Count 2
+  /Kids [ 3 0 R 6 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F1 16 Tf
+(Page 1) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F1 16 Tf
+(Page 2) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/chrome/test/data/pdf/accessibility/multi-page.pdf b/chrome/test/data/pdf/accessibility/multi-page.pdf
new file mode 100644
index 0000000..621b71c7
--- /dev/null
+++ b/chrome/test/data/pdf/accessibility/multi-page.pdf
@@ -0,0 +1,80 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 250 ]
+  /Count 2
+  /Kids [ 3 0 R 6 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+5 0 obj <<
+  /Length 38
+>>
+stream
+BT
+20 100 Td
+/F1 16 Tf
+(Page 1) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 7 0 R
+>>
+7 0 obj <<
+  /Length 38
+>>
+stream
+BT
+20 100 Td
+/F1 16 Tf
+(Page 2) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000167 00000 n 
+0000000293 00000 n 
+0000000369 00000 n 
+0000000458 00000 n 
+0000000577 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+666
+%%EOF
diff --git a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-auralinux.txt b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-auralinux.txt
index b1ff437..2822240a 100644
--- a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-auralinux.txt
+++ b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-auralinux.txt
@@ -1,5 +1,5 @@
 [embedded component]
-++[panel]
+++[document frame]
 ++++[landmark] name='Page 1'
 ++++++[heading]
 ++++++++[text] name='Heading<newline>'
diff --git a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-blink.txt b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-blink.txt
index 30b0e8ca..9b85331 100644
--- a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-blink.txt
+++ b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-blink.txt
@@ -1,6 +1,6 @@
 embeddedObject
-++group restriction=readOnly
-++++region name='Page 1' restriction=readOnly
+++document restriction=readOnly
+++++region name='Page 1' restriction=readOnly isPageBreakingObject=true
 ++++++heading hierarchicalLevel=2 restriction=readOnly
 ++++++++staticText name='Heading<newline>' restriction=readOnly
 ++++++++++inlineTextBox name='Heading<newline>' restriction=readOnly
diff --git a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-uia-win.txt b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-uia-win.txt
index 6aa78b76..817f686b 100644
--- a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-uia-win.txt
+++ b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-uia-win.txt
@@ -1,8 +1,12 @@
 group
-++group
+++document
 ++++region Name='Page 1'
 ++++++heading
 ++++++group
+++++++++description Name='This is a small pdf file:<newline>'
 ++++++group
+++++++++description Name='Lorem Ipsum is simply dummy text of the printing and typesetting industry.<newline>Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.<newline>'
 ++++++group
+++++++++description Name='It has survived not only five centuries, but also the leap into electronic typesetting,<newline>remaining essentially unchanged. It was popularised in the 1960s with the release of<newline>Letraset sheets containing Lorem Ipsum passages, and more recently with desktop<newline>publishing software like Aldus PageMaker including versions of Lorem Ipsum.<newline>'
 ++++++group
+++++++++description Name='Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots<newline>in a piece of classical Latin literature from 45 BC, making it over 2000 years old.'
diff --git a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-win.txt b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-win.txt
index 9a753255..18bfc0fd 100644
--- a/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-win.txt
+++ b/chrome/test/data/pdf/accessibility/paragraphs-and-heading-untagged-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_GROUPING FOCUSABLE
-++ROLE_SYSTEM_GROUPING READONLY
+++ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++++IA2_ROLE_LANDMARK name='Page 1' READONLY
 ++++++IA2_ROLE_HEADING READONLY
 ++++++++ROLE_SYSTEM_STATICTEXT name='Heading<newline>' READONLY
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
index ca000ed..7d3ac90 100644
--- a/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
+++ b/chrome/test/data/webui/bookmarks/bookmarks_browsertest.js
@@ -66,7 +66,7 @@
   __proto__: BookmarksBrowserTest.prototype,
 
   extraLibraries: BookmarksBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'command_manager_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
index f6c24ae..e8bd761 100644
--- a/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
+++ b/chrome/test/data/webui/bookmarks/bookmarks_focus_test.js
@@ -19,7 +19,7 @@
   extraLibraries: [
     ...PolymerInteractiveUITest.prototype.extraLibraries,
     '//ui/webui/resources/js/util.js',
-    '../settings/test_util.js',
+    '../test_util.js',
     '../test_store.js',
     'test_command_manager.js',
     'test_store.js',
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
index 7625733..8b52458 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -150,7 +150,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_drawer_tests.js',
   ]),
 };
@@ -173,7 +173,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_scrollable_behavior_tests.js',
   ]),
 };
@@ -393,7 +393,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_dialog_test.js',
   ]),
 };
@@ -416,7 +416,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_slider_test.js',
   ]),
 };
@@ -463,7 +463,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_toast_manager_test.js',
   ]),
 };
@@ -487,7 +487,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_radio_button_test.js',
   ]),
 };
@@ -511,7 +511,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_radio_group_test.js',
   ]),
 };
@@ -534,7 +534,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_button_tests.js',
   ]),
 };
@@ -558,7 +558,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_icon_button_tests.js',
   ]),
 };
@@ -628,7 +628,7 @@
 
   /** @override */
   extraLibraries: CrElementsBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_searchable_drop_down_tests.js',
   ]),
 };
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
index 5040264..4800367 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
@@ -23,7 +23,7 @@
       'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html',
 
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_action_menu_test.js',
   ]),
 };
@@ -65,7 +65,7 @@
 
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_toggle_test.js',
   ]),
 };
@@ -89,7 +89,7 @@
 
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_checkbox_test.js',
   ]),
 };
@@ -112,7 +112,7 @@
 
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_input_test.js',
   ]),
 };
@@ -161,7 +161,7 @@
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
     '//ui/webui/resources/js/util.js',
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_expand_button_focus_tests.js',
   ]),
 };
@@ -185,7 +185,7 @@
   /** @override */
   extraLibraries: CrElementsFocusTest.prototype.extraLibraries.concat([
     '//ui/webui/resources/js/util.js',
-    '../settings/test_util.js',
+    '../test_util.js',
     'cr_tabs_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
index a31e1980..fea56b29 100644
--- a/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
+++ b/chrome/test/data/webui/cr_focus_row_behavior_interactive_test.js
@@ -23,7 +23,7 @@
     ...PolymerTest.prototype.extraLibraries,
     '//ui/webui/resources/js/util.js',
     'cr_focus_row_behavior_test.js',
-    'settings/test_util.js',
+    'test_util.js',
   ],
 
   /** @override */
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index b1e1407cb..7d38e22 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -29,7 +29,7 @@
       '../../../../../ui/webui/resources/js/promise_resolver.js',
       '../../../../../ui/webui/resources/js/webui_resource_test.js',
       '../fake_chrome_event.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'test_service.js',
     ];
@@ -540,7 +540,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'manager_test.js',
     ]);
   }
@@ -594,7 +594,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'manager_test.js',
     ]);
   }
@@ -707,7 +707,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'pack_dialog_test.js',
     ]);
   }
@@ -761,7 +761,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'options_dialog_test.js',
     ]);
   }
@@ -892,7 +892,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'error_console_test.js',
     ]);
   }
diff --git a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
index d659f1a..6f0fc156 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
@@ -24,7 +24,7 @@
   get extraLibraries() {
     return [
       ...super.extraLibraries,
-      '../settings/test_util.js',
+      '../test_util.js',
     ];
   }
 
diff --git a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
index 24575db..67c6b705 100644
--- a/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
+++ b/chrome/test/data/webui/find_shortcut_behavior_browsertest.js
@@ -26,8 +26,7 @@
   extraLibraries: [
     ...PolymerTest.prototype.extraLibraries,
     '//ui/webui/resources/js/util.js',
-    'settings/test_util.js',
-    'settings/test_util.js',
+    'test_util.js',
     'find_shortcut_behavior_test.js',
   ],
 };
diff --git a/chrome/test/data/webui/history/history_browsertest.js b/chrome/test/data/webui/history/history_browsertest.js
index 7b378ef..2f325b97 100644
--- a/chrome/test/data/webui/history/history_browsertest.js
+++ b/chrome/test/data/webui/history/history_browsertest.js
@@ -188,7 +188,7 @@
   __proto__: HistoryBrowserTest.prototype,
 
   extraLibraries: HistoryBrowserTest.prototype.extraLibraries.concat([
-    '../settings/test_util.js',
+    '../test_util.js',
     'history_synced_tabs_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
index e272a7a..cc978e9 100644
--- a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
+++ b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
@@ -24,7 +24,7 @@
     '../test_browser_proxy.js',
     '../fake_chrome_event.js',  // Necessary for fake_quick_unlock_private.js
     '../settings/chromeos/fake_quick_unlock_private.js',
-    '../settings/test_util.js',
+    '../test_util.js',
     'integration_test.js',
     'setup_succeeded_page_test.js',
     'start_setup_page_test.js',
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
index 837e2018..e08136c 100644
--- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -56,7 +56,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '//chrome/test/data/webui/settings/test_util.js',
+      '//chrome/test/data/webui/test_util.js',
       'print_header_interactive_test.js',
     ]);
   }
@@ -84,7 +84,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '//chrome/test/data/webui/settings/test_util.js',
+      '//chrome/test/data/webui/test_util.js',
       'button_strip_interactive_test.js',
     ]);
   }
@@ -119,7 +119,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '//chrome/test/data/webui/settings/test_util.js',
+      '//chrome/test/data/webui/test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -167,7 +167,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'pages_settings_test.js',
     ]);
@@ -207,7 +207,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'number_settings_section_interactive_test.js',
     ]);
@@ -238,7 +238,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/util.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'scaling_settings_interactive_test.js',
     ]);
diff --git a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
index cca997c..0b8ce81 100644
--- a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
@@ -54,7 +54,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -88,7 +88,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -119,7 +119,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'pages_settings_test.js',
     ]);
@@ -157,7 +157,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -198,7 +198,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'settings_select_test.js',
     ]);
@@ -219,7 +219,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'select_behavior_test.js',
     ]);
   }
@@ -250,7 +250,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'number_settings_section_test.js',
     ]);
   }
@@ -313,7 +313,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'model_test.js',
     ]);
@@ -355,7 +355,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'model_settings_availability_test.js',
     ]);
@@ -377,7 +377,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'model_settings_policy_test.js',
     ]);
@@ -496,7 +496,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'link_container_test.js',
     ]);
@@ -538,7 +538,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -579,7 +579,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/cr/event_target.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -633,7 +633,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/web_ui_listener_behavior.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -733,7 +733,7 @@
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/web_ui_listener_behavior.js',
       '//ui/webui/resources/js/cr/event_target.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
@@ -774,7 +774,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'advanced_dialog_test.js',
     ]);
@@ -825,7 +825,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -858,7 +858,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'custom_margins_test.js',
     ]);
@@ -942,7 +942,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/web_ui_listener_behavior.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'print_preview_test_utils.js',
@@ -997,7 +997,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'header_test.js',
     ]);
   }
@@ -1042,7 +1042,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'header_new_test.js',
     ]);
   }
@@ -1083,7 +1083,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'button_strip_test.js',
     ]);
   }
@@ -1157,7 +1157,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'advanced_item_test.js',
     ]);
@@ -1203,7 +1203,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'destination_list_test.js',
     ]);
   }
@@ -1266,7 +1266,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'plugin_stub.js',
@@ -1331,7 +1331,7 @@
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/web_ui_listener_behavior.js',
       '../test_browser_proxy.js',
-      '../settings/test_util.js',
+      '../test_util.js',
       'cloud_print_interface_stub.js',
       'print_preview_test_utils.js',
       'native_layer_stub.js',
@@ -1420,7 +1420,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'scaling_settings_test.js',
     ]);
@@ -1460,7 +1460,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'copies_settings_test.js',
     ]);
@@ -1481,7 +1481,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'media_size_settings_test.js',
     ]);
@@ -1502,7 +1502,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'dpi_settings_test.js',
     ]);
@@ -1523,7 +1523,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'other_options_settings_test.js',
     ]);
@@ -1544,7 +1544,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'layout_settings_test.js',
     ]);
@@ -1565,7 +1565,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'color_settings_test.js',
     ]);
@@ -1586,7 +1586,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'margins_settings_test.js',
     ]);
@@ -1607,7 +1607,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'pages_per_sheet_settings_test.js',
     ]);
@@ -1628,7 +1628,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'duplex_settings_test.js',
     ]);
@@ -1650,7 +1650,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'print_preview_test_utils.js',
       'pin_settings_test.js',
     ]);
@@ -1672,7 +1672,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       '../test_browser_proxy.js',
       'cloud_print_interface_stub.js',
       'native_layer_stub.js',
diff --git a/chrome/test/data/webui/set_time_dialog_browsertest.js b/chrome/test/data/webui/set_time_dialog_browsertest.js
index 0ae1342..df58137e 100644
--- a/chrome/test/data/webui/set_time_dialog_browsertest.js
+++ b/chrome/test/data/webui/set_time_dialog_browsertest.js
@@ -187,6 +187,46 @@
             assertGT(timeInSeconds, todaySeconds);
           });
     });
+
+    suite('NullTimezone', () => {
+      suiteSetup(() => {
+        loadTimeData.overrideValues({
+          currentTimezoneId: '',
+          timezoneList: [],
+        });
+      });
+
+      test('SetDateNullTimezone', () => {
+        const dateInput = setTimeElement.$$('#dateInput');
+        assertTrue(!!dateInput);
+
+        assertEquals(null, setTimeElement.$$('#timezoneSelect'));
+
+        // Simulates the user changing the date picker backward by two days. We
+        // are changing the date to make the test simpler. Changing the time
+        // would require timezone manipulation and handling corner cases over
+        // midnight. valuesAsDate return the time in UTC, therefore the amount
+        // of days here must be bigger than one to avoid situations where the
+        // new time and old time are in the same day.
+        const today = dateInput.valueAsDate;
+        const twoDaysAgo = new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000);
+        dateInput.focus();
+        dateInput.valueAsDate = twoDaysAgo;
+        setTimeElement.$$('#doneButton').click();
+
+        // Verify the page sends a request to move time backward.
+        return testBrowserProxy.whenCalled('setTimeInSeconds')
+            .then(newTimeSeconds => {
+              const todaySeconds = today.getTime() / 1000;
+              // Check that the current time is bigger than the new time, which
+              // is supposed to be two days ago. The exact value isn't
+              // important, checking it is difficult because it depends on the
+              // current time, which is constantly updated, therefore we only
+              // assert that one is bigger than the other.
+              assertGT(todaySeconds, newTimeSeconds);
+            });
+      });
+    });
   });
 
   mocha.run();
diff --git a/chrome/test/data/webui/settings/a11y/sign_out_a11y_test.js b/chrome/test/data/webui/settings/a11y/sign_out_a11y_test.js
index ce00ee06..023acd0 100644
--- a/chrome/test/data/webui/settings/a11y/sign_out_a11y_test.js
+++ b/chrome/test/data/webui/settings/a11y/sign_out_a11y_test.js
@@ -26,8 +26,8 @@
   // Include files that define the mocha tests.
   extraLibraries: SettingsAccessibilityTest.prototype.extraLibraries.concat([
     '../../test_browser_proxy.js',
+    '../../test_util.js',
     '../sync_test_util.js',
-    '../test_util.js',
     '../test_sync_browser_proxy.js',
   ]),
 };
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index 586b3ef..a2831f2 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -483,13 +483,6 @@
           assertTrue(!!page.$.regulatoryInfo.hidden);
           assertTrue(!!page.$.crostiniLicense.hidden);
         });
-
-        test('detailed build info page', () => {
-          page.scroller = page.offsetParent;
-          assertTrue(!!page.$['detailed-build-info-trigger']);
-          page.$['detailed-build-info-trigger'].click();
-          assertTrue(!!page.$$('settings-detailed-build-info'));
-        });
       }
 
       if (!cr.isChromeOS) {
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
index 72671a8..34fa77c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -439,6 +439,13 @@
         await checkHasEndOfLife(false);
       });
 
+      test('detailed build info page', () => {
+        page.scroller = page.offsetParent;
+        assertTrue(!!page.$['detailed-build-info-trigger']);
+        page.$['detailed-build-info-trigger'].click();
+        assertTrue(!!page.$$('settings-detailed-build-info'));
+      });
+
       test('GetHelp', function() {
         assertTrue(!!page.$.help);
         page.$.help.click();
@@ -464,7 +471,7 @@
   }
 
   return {
-    // TODO(aee): move the detailed build info and channel switch dialog tests
+    // TODO(crbug.com/950007): Move the channel switch dialog tests to here
     // from the browser about page tests when those CrOS-specific parts are
     // removed from the browser about page.
     registerTests: registerAboutPageTests,
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index e744ed2..20f2760 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -57,7 +57,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'test_lifetime_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'test_about_page_browser_proxy.js',
@@ -104,7 +104,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'os_advanced_page_browsertest.js',
     ]);
   }
@@ -397,7 +397,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       'os_settings_main_test.js',
     ]);
@@ -414,7 +414,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'os_settings_menu_test.js',
     ]);
   }
@@ -503,7 +503,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'test_multidevice_browser_proxy.js',
       'multidevice_smartlock_subpage_test.js',
     ]);
@@ -611,7 +611,7 @@
     return super.extraLibraries.concat([
       BROWSER_SETTINGS_PATH + '../fake_chrome_event.js',
       BROWSER_SETTINGS_PATH + 'fake_settings_private.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'fake_quick_unlock_private.js',
       'fake_quick_unlock_uma.js',
       'quick_unlock_authenticate_browsertest_chromeos.js',
@@ -742,7 +742,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/assert.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       'cups_printer_entry_tests.js',
     ]);
@@ -766,7 +766,7 @@
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/assert.js',
       '//ui/webui/resources/js/promise_resolver.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       BROWSER_SETTINGS_PATH + '../fake_chrome_event.js',
       BROWSER_SETTINGS_PATH + '../chromeos/fake_networking_private.js',
@@ -798,7 +798,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/assert.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       'test_cups_printers_browser_proxy.js',
       'cups_printer_page_tests.js',
@@ -826,7 +826,7 @@
       BROWSER_SETTINGS_PATH + 'fake_language_settings_private.js',
       BROWSER_SETTINGS_PATH + 'test_languages_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'fake_settings_private.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'fake_input_method_private.js',
       'os_languages_page_tests.js',
     ]);
@@ -854,7 +854,7 @@
     return super.extraLibraries.concat([
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       BROWSER_SETTINGS_PATH + 'test_lifetime_browser_proxy.js',
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       'test_os_reset_browser_proxy.js',
       'os_reset_page_test.js',
     ]);
@@ -899,7 +899,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      BROWSER_SETTINGS_PATH + 'test_util.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
       'smb_shares_page_tests.js',
     ]);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
index a496156..d972a91c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
@@ -24,7 +24,8 @@
 
   /** @override */
   get extraLibraries() {
-    return super.extraLibraries.concat(BROWSER_SETTINGS_PATH + 'test_util.js');
+    return super.extraLibraries.concat(
+        BROWSER_SETTINGS_PATH + '../test_util.js');
   }
 };
 
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 36b804a..314373e 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -83,7 +83,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
     'settings_slider_tests.js',
   ]),
 };
@@ -207,7 +207,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'test_lifetime_browser_proxy.js',
     'test_about_page_browser_proxy.js',
@@ -305,8 +305,9 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'passwords_and_autofill_fake_data.js', 'test_util.js',
-    'autofill_section_test.js'
+    'passwords_and_autofill_fake_data.js',
+    '../test_util.js',
+    'autofill_section_test.js',
   ]),
 };
 
@@ -330,8 +331,9 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'passwords_and_autofill_fake_data.js', 'test_util.js',
-    'autofill_section_test.js'
+    'passwords_and_autofill_fake_data.js',
+    '../test_util.js',
+    'autofill_section_test.js',
   ]),
 };
 
@@ -364,7 +366,7 @@
     'passwords_and_autofill_fake_data.js',
     'passwords_section_test.js',
     'test_password_manager_proxy.js',
-    'test_util.js',
+    '../test_util.js',
   ]),
 };
 
@@ -422,7 +424,7 @@
     'passwords_and_autofill_fake_data.js',
     'sync_test_util.js',
     'test_sync_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'payments_section_test.js',
   ]),
 };
@@ -479,9 +481,12 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    '../fake_chrome_event.js', 'chromeos/fake_quick_unlock_private.js',
-    'fake_settings_private.js', 'chromeos/fake_quick_unlock_uma.js',
-    'chromeos/quick_unlock_authenticate_browsertest_chromeos.js', 'test_util.js'
+    '../fake_chrome_event.js',
+    'chromeos/fake_quick_unlock_private.js',
+    'fake_settings_private.js',
+    'chromeos/fake_quick_unlock_uma.js',
+    'chromeos/quick_unlock_authenticate_browsertest_chromeos.js',
+    '../test_util.js',
   ]),
 };
 
@@ -775,7 +780,7 @@
     '../test_browser_proxy.js',
     'sync_test_util.js',
     'test_sync_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'people_page_sync_page_test.js',
   ]),
 };
@@ -802,7 +807,7 @@
     '../test_browser_proxy.js',
     'test_lifetime_browser_proxy.js',
     'test_reset_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'reset_page_test.js',
   ]),
 };
@@ -1057,7 +1062,7 @@
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'test_extension_control_browser_proxy.js',
     'test_search_engines_browser_proxy.js',
     'search_engines_page_test.js',
@@ -1089,7 +1094,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'certificate_manager_test.js',
   ]),
@@ -1152,7 +1157,7 @@
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '//ui/webui/resources/js/promise_resolver.js',
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'test_privacy_page_browser_proxy.js',
     'test_sync_browser_proxy.js',
@@ -1236,9 +1241,10 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../cr_elements/cr_policy_strings.js',
     '../test_browser_proxy.js',
-    'chooser_exception_list_entry_tests.js',
-    'test_site_settings_prefs_browser_proxy.js',
+    '../test_util.js',
     'test_util.js',
+    'test_site_settings_prefs_browser_proxy.js',
+    'chooser_exception_list_entry_tests.js',
   ]),
 };
 
@@ -1422,6 +1428,7 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
     'test_util.js',
+    '../test_util.js',
     'test_site_settings_prefs_browser_proxy.js',
     'chromeos/test_multidevice_browser_proxy.js',
     'site_list_tests.js',
@@ -1457,9 +1464,10 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../cr_elements/cr_policy_strings.js',
     '../test_browser_proxy.js',
-    'site_list_entry_tests.js',
+    '../test_util.js',
     'test_util.js',
     'test_site_settings_prefs_browser_proxy.js',
+    'site_list_entry_tests.js',
   ]),
 };
 
@@ -1482,6 +1490,7 @@
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
+    '../test_util.js',
     'test_util.js',
     'test_site_settings_prefs_browser_proxy.js',
     'zoom_levels_tests.js',
@@ -1533,7 +1542,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'security_keys_subpage_test.js',
   ]),
@@ -1555,7 +1564,7 @@
   browsePreload: 'chrome://settings/site_settings/site_data.html',
 
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'test_local_data_browser_proxy.js',
     'site_data_test.js',
@@ -1842,7 +1851,7 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '//ui/webui/resources/js/promise_resolver.js',
     '../fake_chrome_event.js',
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'fake_language_settings_private.js',
     'fake_settings_private.js',
@@ -1871,7 +1880,7 @@
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../fake_chrome_event.js',
-    'test_util.js',
+    '../test_util.js',
     '../test_browser_proxy.js',
     'fake_settings_private.js',
     'fake_language_settings_private.js',
@@ -2012,7 +2021,7 @@
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'settings_main_test.js',
   ]),
 };
@@ -2235,7 +2244,7 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '../test_browser_proxy.js',
     'chromeos/test_multidevice_browser_proxy.js',
-    'test_util.js',
+    '../test_util.js',
     'chromeos/multidevice_smartlock_subpage_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/settings/settings_ui_browsertest.js b/chrome/test/data/webui/settings/settings_ui_browsertest.js
index ac0a2d8..b71f8f6 100644
--- a/chrome/test/data/webui/settings/settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_ui_browsertest.js
@@ -17,7 +17,7 @@
 
   /** @override */
   extraLibraries: SettingsPageBrowserTest.prototype.extraLibraries.concat([
-    'test_util.js',
+    '../test_util.js',
   ]),
 };
 
diff --git a/chrome/test/data/webui/settings/test_util.js b/chrome/test/data/webui/settings/test_util.js
index cfdce28..7c67808c9 100644
--- a/chrome/test/data/webui/settings/test_util.js
+++ b/chrome/test/data/webui/settings/test_util.js
@@ -3,72 +3,6 @@
 // found in the LICENSE file.
 
 cr.define('test_util', function() {
-  /**
-   * Observes an HTML attribute and fires a promise when it matches a given
-   * value.
-   * @param {!HTMLElement} target
-   * @param {string} attributeName
-   * @param {*} attributeValue
-   * @return {!Promise}
-   */
-  function whenAttributeIs(target, attributeName, attributeValue) {
-    function isDone() {
-      return target.getAttribute(attributeName) == attributeValue;
-    }
-
-    return isDone() ? Promise.resolve() : new Promise(function(resolve) {
-      new MutationObserver(function(mutations, observer) {
-        for (const mutation of mutations) {
-          assertEquals('attributes', mutation.type);
-          if (mutation.attributeName == attributeName && isDone()) {
-            observer.disconnect();
-            resolve();
-            return;
-          }
-        }
-      }).observe(target, {
-        attributes: true,
-        childList: false,
-        characterData: false
-      });
-    });
-  }
-
-  /**
-   * Converts an event occurrence to a promise.
-   * @param {string} eventType
-   * @param {!HTMLElement} target
-   * @return {!Promise} A promise firing once the event occurs.
-   */
-  function eventToPromise(eventType, target) {
-    return new Promise(function(resolve, reject) {
-      target.addEventListener(eventType, function f(e) {
-        target.removeEventListener(eventType, f);
-        resolve(e);
-      });
-    });
-  }
-
-  /**
-   * Data-binds two Polymer properties using the property-changed events and
-   * set/notifyPath API. Useful for testing components which would normally be
-   * used together.
-   * @param {!HTMLElement} el1
-   * @param {!HTMLElement} el2
-   * @param {string} property
-   */
-  function fakeDataBind(el1, el2, property) {
-    const forwardChange = function(el, event) {
-      if (event.detail.hasOwnProperty('path')) {
-        el.notifyPath(event.detail.path, event.detail.value);
-      } else {
-        el.set(property, event.detail.value);
-      }
-    };
-    // Add the listeners symmetrically. Polymer will prevent recursion.
-    el1.addEventListener(property + '-changed', forwardChange.bind(null, el2));
-    el2.addEventListener(property + '-changed', forwardChange.bind(null, el1));
-  }
 
   /**
    * Helper to create an object containing a ContentSettingsType key to array or
@@ -262,17 +196,6 @@
     }
   }
 
-  /**
-   * Converts beforeNextRender() API to promise-based.
-   * @param {!Element} element
-   * @return {!Promise}
-   */
-  function waitForRender(element) {
-    return new Promise(resolve => {
-      Polymer.RenderStatus.beforeNextRender(element, resolve);
-    });
-  }
-
   return {
     createContentSettingTypeToValuePair: createContentSettingTypeToValuePair,
     createDefaultContentSetting: createDefaultContentSetting,
@@ -281,11 +204,7 @@
     createRawSiteException: createRawSiteException,
     createSiteGroup: createSiteGroup,
     createSiteSettingsPrefs: createSiteSettingsPrefs,
-    eventToPromise: eventToPromise,
-    fakeDataBind: fakeDataBind,
     getContentSettingsTypeFromChooserType:
         getContentSettingsTypeFromChooserType,
-    waitForRender: waitForRender,
-    whenAttributeIs: whenAttributeIs,
   };
 });
diff --git a/chrome/test/data/webui/test_util.js b/chrome/test/data/webui/test_util.js
new file mode 100644
index 0000000..ac54ccca
--- /dev/null
+++ b/chrome/test/data/webui/test_util.js
@@ -0,0 +1,90 @@
+// 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.
+
+cr.define('test_util', function() {
+  /**
+   * Observes an HTML attribute and fires a promise when it matches a given
+   * value.
+   * @param {!HTMLElement} target
+   * @param {string} attributeName
+   * @param {*} attributeValue
+   * @return {!Promise}
+   */
+  function whenAttributeIs(target, attributeName, attributeValue) {
+    function isDone() {
+      return target.getAttribute(attributeName) == attributeValue;
+    }
+
+    return isDone() ? Promise.resolve() : new Promise(function(resolve) {
+      new MutationObserver(function(mutations, observer) {
+        for (const mutation of mutations) {
+          assertEquals('attributes', mutation.type);
+          if (mutation.attributeName == attributeName && isDone()) {
+            observer.disconnect();
+            resolve();
+            return;
+          }
+        }
+      }).observe(target, {
+        attributes: true,
+        childList: false,
+        characterData: false
+      });
+    });
+  }
+
+  /**
+   * Converts an event occurrence to a promise.
+   * @param {string} eventType
+   * @param {!HTMLElement} target
+   * @return {!Promise} A promise firing once the event occurs.
+   */
+  function eventToPromise(eventType, target) {
+    return new Promise(function(resolve, reject) {
+      target.addEventListener(eventType, function f(e) {
+        target.removeEventListener(eventType, f);
+        resolve(e);
+      });
+    });
+  }
+
+  /**
+   * Data-binds two Polymer properties using the property-changed events and
+   * set/notifyPath API. Useful for testing components which would normally be
+   * used together.
+   * @param {!HTMLElement} el1
+   * @param {!HTMLElement} el2
+   * @param {string} property
+   */
+  function fakeDataBind(el1, el2, property) {
+    const forwardChange = function(el, event) {
+      if (event.detail.hasOwnProperty('path')) {
+        el.notifyPath(event.detail.path, event.detail.value);
+      } else {
+        el.set(property, event.detail.value);
+      }
+    };
+    // Add the listeners symmetrically. Polymer will prevent recursion.
+    el1.addEventListener(property + '-changed', forwardChange.bind(null, el2));
+    el2.addEventListener(property + '-changed', forwardChange.bind(null, el1));
+  }
+
+  /**
+   * Converts beforeNextRender() API to promise-based.
+   * @param {!Element} element
+   * @return {!Promise}
+   */
+  function waitForRender(element) {
+    return new Promise(resolve => {
+      Polymer.RenderStatus.beforeNextRender(element, resolve);
+    });
+  }
+
+  return {
+    eventToPromise: eventToPromise,
+    fakeDataBind: fakeDataBind,
+    waitForRender: waitForRender,
+    whenAttributeIs: whenAttributeIs,
+  };
+});
diff --git a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
index 3d703cef..20e44a8 100644
--- a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
+++ b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
@@ -62,7 +62,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'welcome_app_test.js',
       'test_bookmark_proxy.js',
       'test_welcome_browser_proxy.js',
@@ -107,7 +107,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'navigation_behavior_test.js',
     ]);
   }
@@ -149,7 +149,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
-      '../settings/test_util.js',
+      '../test_util.js',
       'nux_set_as_default_test.js',
       'test_nux_set_as_default_proxy.js',
     ]);
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_gamepad_button.html b/chrome/test/data/xr/e2e_test_files/html/test_gamepad_button.html
index aeff2f4..e1f89fa7 100644
--- a/chrome/test/data/xr/e2e_test_files/html/test_gamepad_button.html
+++ b/chrome/test/data/xr/e2e_test_files/html/test_gamepad_button.html
@@ -42,7 +42,7 @@
         frameCounter++;
         var gp = navigator.getGamepads()[index];
         // This can happen if the controller has been briefly disconnected.
-        if (gp === null) return;
+        if (gp == null) return;
         if (!canStartTest) {
           if (gp.buttons[0].pressed != lastInputValue) {
             lastInputChangeFrame = frameCounter;
diff --git a/chromecast/media/audio/BUILD.gn b/chromecast/media/audio/BUILD.gn
index 788d1c4..b2aab9e2 100644
--- a/chromecast/media/audio/BUILD.gn
+++ b/chromecast/media/audio/BUILD.gn
@@ -102,6 +102,7 @@
     "MAXIMUM_OUTPUT_BUFFER_SIZE_IN_FRAMES=$maximum_output_buffer_size_in_frames",
     "DEFAULT_OUTPUT_BUFFER_SIZE_IN_FRAMES=$default_output_buffer_size_in_frames",
     "ENABLE_AUDIO_CAPTURE_SERVICE=$enable_audio_capture_service",
+    "USE_UNIX_SOCKETS=$use_unix_sockets",
   ]
 
   if (use_alsa) {
diff --git a/chromecast/media/audio/capture_service/BUILD.gn b/chromecast/media/audio/capture_service/BUILD.gn
index 672cd66e..f907f3f 100644
--- a/chromecast/media/audio/capture_service/BUILD.gn
+++ b/chromecast/media/audio/capture_service/BUILD.gn
@@ -7,19 +7,10 @@
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 
-buildflag_header("buildflags") {
-  header = "capture_service_buildflags.h"
-  flags = [ "USE_UNIX_SOCKETS=$use_unix_sockets" ]
-}
-
 cast_source_set("common") {
   sources = [
     "constants.h",
   ]
-
-  deps = [
-    ":buildflags",
-  ]
 }
 
 cast_source_set("receiver") {
@@ -35,9 +26,9 @@
   ]
 
   deps = [
-    ":buildflags",
     "//base",
     "//chromecast/base",
+    "//chromecast/media/audio:audio_buildflags",
     "//chromecast/net:small_message_socket",
     "//media",
     "//net",
diff --git a/chromecast/media/audio/capture_service/capture_service_receiver.cc b/chromecast/media/audio/capture_service/capture_service_receiver.cc
index 7891ca6..2c0422d 100644
--- a/chromecast/media/audio/capture_service/capture_service_receiver.cc
+++ b/chromecast/media/audio/capture_service/capture_service_receiver.cc
@@ -14,7 +14,7 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/timer/timer.h"
-#include "chromecast/media/audio/capture_service/capture_service_buildflags.h"
+#include "chromecast/media/audio/audio_buildflags.h"
 #include "chromecast/media/audio/capture_service/constants.h"
 #include "chromecast/media/audio/capture_service/message_parsing_util.h"
 #include "chromecast/net/small_message_socket.h"
diff --git a/chromecast/media/audio/capture_service/constants.h b/chromecast/media/audio/capture_service/constants.h
index 1b98eb5..52d6108 100644
--- a/chromecast/media/audio/capture_service/constants.h
+++ b/chromecast/media/audio/capture_service/constants.h
@@ -5,17 +5,12 @@
 #ifndef CHROMECAST_MEDIA_AUDIO_CAPTURE_SERVICE_CONSTANTS_H_
 #define CHROMECAST_MEDIA_AUDIO_CAPTURE_SERVICE_CONSTANTS_H_
 
-#include "chromecast/media/audio/capture_service/capture_service_buildflags.h"
-
 namespace chromecast {
 namespace media {
 namespace capture_service {
 
-#if BUILDFLAG(USE_UNIX_SOCKETS)
 constexpr char kDefaultUnixDomainSocketPath[] = "/tmp/capture-service";
-#else
 constexpr int kDefaultTcpPort = 12855;
-#endif
 
 enum SampleFormat {
   INTERLEAVED_INT16 = 0,
diff --git a/chromecast/media/audio/mixer_service/BUILD.gn b/chromecast/media/audio/mixer_service/BUILD.gn
index 85aef6b..6728697 100644
--- a/chromecast/media/audio/mixer_service/BUILD.gn
+++ b/chromecast/media/audio/mixer_service/BUILD.gn
@@ -13,12 +13,6 @@
   ]
 }
 
-buildflag_header("buildflags") {
-  header = "mixer_service_buildflags.h"
-
-  flags = [ "USE_UNIX_SOCKETS=$use_unix_sockets" ]
-}
-
 cast_source_set("common") {
   sources = [
     "constants.h",
@@ -27,7 +21,6 @@
   ]
 
   deps = [
-    ":buildflags",
     ":proto",
     "//base",
     "//chromecast/net:small_message_socket",
@@ -44,11 +37,11 @@
   ]
 
   deps = [
-    ":buildflags",
     ":common",
     ":proto",
     "//base",
     "//chromecast/base",
+    "//chromecast/media/audio:audio_buildflags",
     "//chromecast/net:small_message_socket",
     "//net",
   ]
diff --git a/chromecast/media/audio/mixer_service/constants.h b/chromecast/media/audio/mixer_service/constants.h
index ad73a49a..744add3f 100644
--- a/chromecast/media/audio/mixer_service/constants.h
+++ b/chromecast/media/audio/mixer_service/constants.h
@@ -7,17 +7,12 @@
 
 #include <stdint.h>
 
-#include "chromecast/media/audio/mixer_service/mixer_service_buildflags.h"
-
 namespace chromecast {
 namespace media {
 namespace mixer_service {
 
-#if BUILDFLAG(USE_UNIX_SOCKETS)
 constexpr char kDefaultUnixDomainSocketPath[] = "/tmp/mixer-service";
-#else
 constexpr int kDefaultTcpPort = 12854;
-#endif
 
 enum class MessageType : int16_t {
   kMetadata,
diff --git a/chromecast/media/audio/mixer_service/mixer_service_connection.cc b/chromecast/media/audio/mixer_service/mixer_service_connection.cc
index 71e945b..84dc8a7 100644
--- a/chromecast/media/audio/mixer_service/mixer_service_connection.cc
+++ b/chromecast/media/audio/mixer_service/mixer_service_connection.cc
@@ -16,9 +16,9 @@
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chromecast/base/chromecast_switches.h"
+#include "chromecast/media/audio/audio_buildflags.h"
 #include "chromecast/media/audio/mixer_service/constants.h"
 #include "chromecast/media/audio/mixer_service/mixer_service.pb.h"
-#include "chromecast/media/audio/mixer_service/mixer_service_buildflags.h"
 #include "chromecast/media/audio/mixer_service/proto_helpers.h"
 #include "chromecast/net/small_message_socket.h"
 #include "net/base/address_list.h"
diff --git a/chromecast/renderer/extensions/extension_hooks_delegate.cc b/chromecast/renderer/extensions/extension_hooks_delegate.cc
index 862a6260..3e68b62e 100644
--- a/chromecast/renderer/extensions/extension_hooks_delegate.cc
+++ b/chromecast/renderer/extensions/extension_hooks_delegate.cc
@@ -21,6 +21,7 @@
 #include "extensions/renderer/message_target.h"
 #include "extensions/renderer/messaging_util.h"
 #include "extensions/renderer/native_renderer_messaging_service.h"
+#include "extensions/renderer/runtime_hooks_delegate.h"
 #include "extensions/renderer/script_context.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
@@ -237,17 +238,10 @@
 RequestResult ExtensionHooksDelegate::HandleGetURL(
     ScriptContext* script_context,
     const std::vector<v8::Local<v8::Value>>& arguments) {
-  DCHECK_EQ(1u, arguments.size());
-  DCHECK(arguments[0]->IsString());
-  DCHECK(script_context->extension());
-
-  std::string path = gin::V8ToString(script_context->isolate(), arguments[0]);
-
-  RequestResult result(RequestResult::HANDLED);
-  result.return_value =
-      gin::StringToV8(script_context->isolate(),
-                      script_context->extension()->GetResourceURL(path).spec());
-  return result;
+  // We call a static implementation here rather using an alias due to not being
+  // able to remove the extension.json GetURL entry, as it is used for generated
+  // documentation and api feature lists some other methods refer to.
+  return RuntimeHooksDelegate::GetURL(script_context, arguments);
 }
 
 APIBindingHooks::RequestResult ExtensionHooksDelegate::HandleGetViews(
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl.cc b/chromeos/components/proximity_auth/unlock_manager_impl.cc
index 610ed3b..60a2cac 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl.cc
+++ b/chromeos/components/proximity_auth/unlock_manager_impl.cc
@@ -43,9 +43,9 @@
 // in case something goes wrong.
 constexpr base::TimeDelta kAuthAttemptTimeout = base::TimeDelta::FromSeconds(5);
 
-constexpr base::TimeDelta kMinGetUnlockableRemoteStatusDuration =
+constexpr base::TimeDelta kMinExtendedDuration =
     base::TimeDelta::FromMilliseconds(1);
-constexpr base::TimeDelta kMaxGetUnlockableRemoteStatusDuration =
+constexpr base::TimeDelta kMaxExtendedDuration =
     base::TimeDelta::FromSeconds(15);
 const int kNumDurationMetricBuckets = 100;
 
@@ -111,6 +111,19 @@
   }
 }
 
+void RecordExtendedDurationTimerMetric(const std::string& histogram_name,
+                                       base::TimeDelta duration) {
+  // Use a custom |max| to account for Smart Lock's timeout (larger than the
+  // default 10 seconds).
+  base::UmaHistogramCustomTimes(
+      histogram_name, duration, kMinExtendedDuration /* min */,
+      kMaxExtendedDuration /* max */, kNumDurationMetricBuckets /* buckets */);
+}
+
+std::string GetHistogramStatusSuffix(bool unlockable) {
+  return unlockable ? "Unlockable" : "Other";
+}
+
 }  // namespace
 
 UnlockManagerImpl::UnlockManagerImpl(
@@ -166,20 +179,24 @@
   if (life_cycle_) {
     life_cycle_->AddObserver(this);
 
-    attempt_secure_connection_start_time_ =
-        base::DefaultClock::GetInstance()->Now();
+    show_lock_screen_time_ = base::DefaultClock::GetInstance()->Now();
+    has_user_been_shown_first_status_ = false;
 
-    SetIsPerformingInitialScan(true /* is_performing_initial_scan */);
-    AttemptToStartRemoteDeviceLifecycle();
+    if (IsBluetoothPresentAndPowered()) {
+      SetIsPerformingInitialScan(true /* is_performing_initial_scan */);
+      AttemptToStartRemoteDeviceLifecycle();
+    } else {
+      SetIsPerformingInitialScan(false /* is_performing_initial_scan */);
+    }
   } else {
     ResetPerformanceMetricsTimestamps();
 
     if (proximity_monitor_)
       proximity_monitor_->RemoveObserver(this);
     proximity_monitor_.reset();
-  }
 
-  UpdateLockScreen();
+    UpdateLockScreen();
+  }
 }
 
 void UnlockManagerImpl::OnLifeCycleStateChanged(
@@ -238,6 +255,14 @@
   remote_screenlock_state_.reset(new RemoteScreenlockState(
       GetScreenlockStateFromRemoteUpdate(status_update)));
 
+  // Only record these metrics within the initial period of opening the laptop
+  // displaying the lock screen.
+  if (is_performing_initial_scan_) {
+    RecordFirstRemoteStatusReceived(
+        *remote_screenlock_state_ ==
+        RemoteScreenlockState::UNLOCKED /* unlockable */);
+  }
+
   // This also calls |UpdateLockScreen()|
   SetIsPerformingInitialScan(false /* is_performing_initial_scan */);
 }
@@ -311,12 +336,14 @@
 
 void UnlockManagerImpl::AdapterPresentChanged(device::BluetoothAdapter* adapter,
                                               bool present) {
-  UpdateLockScreen();
+  if (!IsBluetoothAdapterRecoveringFromSuspend())
+    OnBluetoothAdapterPresentAndPoweredChanged();
 }
 
 void UnlockManagerImpl::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
                                               bool powered) {
-  UpdateLockScreen();
+  if (!IsBluetoothAdapterRecoveringFromSuspend())
+    OnBluetoothAdapterPresentAndPoweredChanged();
 }
 
 void UnlockManagerImpl::SuspendImminent(
@@ -333,10 +360,11 @@
 void UnlockManagerImpl::SuspendDone(const base::TimeDelta& sleep_duration) {
   bluetooth_suspension_recovery_timer_->Start(
       FROM_HERE, kBluetoothAdapterResumeMaxDuration,
-      base::Bind(&UnlockManagerImpl::UpdateLockScreen,
+      base::Bind(&UnlockManagerImpl::OnBluetoothAdapterPresentAndPoweredChanged,
                  weak_ptr_factory_.GetWeakPtr()));
 
-  SetIsPerformingInitialScan(true /* is_performing_initial_scan */);
+  // The next scan after resuming is expected to be triggered by calling
+  // SetRemoteDeviceLifeCycle().
 }
 
 bool UnlockManagerImpl::IsBluetoothPresentAndPowered() const {
@@ -353,6 +381,18 @@
          bluetooth_adapter_->IsPowered();
 }
 
+void UnlockManagerImpl::OnBluetoothAdapterPresentAndPoweredChanged() {
+  DCHECK(!IsBluetoothAdapterRecoveringFromSuspend());
+
+  if (!IsBluetoothPresentAndPowered()) {
+    SetIsPerformingInitialScan(false /* is_performing_initial_scan */);
+    return;
+  }
+
+  if (!is_performing_initial_scan_)
+    SetIsPerformingInitialScan(true /* is_performing_initial_scan */);
+}
+
 bool UnlockManagerImpl::IsBluetoothAdapterRecoveringFromSuspend() const {
   return bluetooth_suspension_recovery_timer_->IsRunning();
 }
@@ -539,8 +579,11 @@
   PA_LOG(INFO) << "Updating screenlock state from " << screenlock_state_
                << " to " << new_state;
 
-  if (new_state == ScreenlockState::AUTHENTICATED)
-    RecordUnlockableRemoteStatusReceived();
+  if (new_state != ScreenlockState::INACTIVE &&
+      new_state != ScreenlockState::BLUETOOTH_CONNECTING) {
+    RecordFirstStatusShownToUser(
+        new_state == ScreenlockState::AUTHENTICATED /* unlockable */);
+  }
 
   proximity_auth_client_->UpdateScreenlockState(new_state);
   screenlock_state_ = new_state;
@@ -559,6 +602,9 @@
   // Clear the waking up state after a timeout.
   initial_scan_timeout_weak_ptr_factory_.InvalidateWeakPtrs();
   if (is_performing_initial_scan_) {
+    initial_scan_start_time_ = base::DefaultClock::GetInstance()->Now();
+    has_received_first_remote_status_ = false;
+
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(&UnlockManagerImpl::OnInitialScanTimeout,
@@ -658,39 +704,91 @@
   return life_cycle_->GetMessenger();
 }
 
-void UnlockManagerImpl::RecordUnlockableRemoteStatusReceived() {
-  if (attempt_secure_connection_start_time_.is_null() ||
+void UnlockManagerImpl::RecordFirstRemoteStatusReceived(bool unlockable) {
+  if (has_received_first_remote_status_)
+    return;
+  has_received_first_remote_status_ = true;
+
+  if (initial_scan_start_time_.is_null() ||
       attempt_get_remote_status_start_time_.is_null()) {
-    PA_LOG(WARNING) << "Attempted to RecordUnlockableRemoteStatusReceived() "
+    PA_LOG(WARNING) << "Attempted to RecordFirstRemoteStatusReceived() "
                        "without initial timestamps recorded.";
     NOTREACHED();
+    return;
   }
 
-  base::Time now = base::DefaultClock::GetInstance()->Now();
-  if (screenlock_type_ == ProximityAuthSystem::SESSION_LOCK) {
-    // Use a custom |max| to account for Smart Lock's timeout (larger than the
-    // default 10 seconds).
-    base::UmaHistogramCustomTimes(
-        "SmartLock.Performance.StartScanToReceiveUnlockableRemoteStatus."
-        "Duration.Unlock",
-        now - attempt_secure_connection_start_time_ /* sample */,
-        kMinGetUnlockableRemoteStatusDuration /* min */,
-        kMaxGetUnlockableRemoteStatusDuration /* max */,
-        kNumDurationMetricBuckets /* buckets */);
+  const std::string histogram_status_suffix =
+      GetHistogramStatusSuffix(unlockable);
 
+  base::Time now = base::DefaultClock::GetInstance()->Now();
+  base::TimeDelta start_scan_to_receive_first_remote_status_duration =
+      now - initial_scan_start_time_;
+  base::TimeDelta authentication_to_receive_first_remote_status_duration =
+      now - attempt_get_remote_status_start_time_;
+
+  if (screenlock_type_ == ProximityAuthSystem::SESSION_LOCK) {
+    RecordExtendedDurationTimerMetric(
+        "SmartLock.Performance.StartScanToReceiveFirstRemoteStatusDuration."
+        "Unlock",
+        start_scan_to_receive_first_remote_status_duration);
+    RecordExtendedDurationTimerMetric(
+        "SmartLock.Performance.StartScanToReceiveFirstRemoteStatusDuration."
+        "Unlock." +
+            histogram_status_suffix,
+        start_scan_to_receive_first_remote_status_duration);
+
+    // This should be much less than 10 seconds, so use UmaHistogramTimes.
     base::UmaHistogramTimes(
-        "SmartLock.Performance.AuthenticationToReceiveUnlockableRemoteStatus."
-        "Duration.Unlock",
-        now - attempt_get_remote_status_start_time_);
+        "SmartLock.Performance."
+        "AuthenticationToReceiveFirstRemoteStatusDuration.Unlock",
+        authentication_to_receive_first_remote_status_duration);
+    base::UmaHistogramTimes(
+        "SmartLock.Performance."
+        "AuthenticationToReceiveFirstRemoteStatusDuration.Unlock." +
+            histogram_status_suffix,
+        authentication_to_receive_first_remote_status_duration);
   }
 
   // TODO(crbug.com/905438): Implement similar SignIn metrics.
+}
 
-  ResetPerformanceMetricsTimestamps();
+void UnlockManagerImpl::RecordFirstStatusShownToUser(bool unlockable) {
+  if (has_user_been_shown_first_status_)
+    return;
+  has_user_been_shown_first_status_ = true;
+
+  if (show_lock_screen_time_.is_null()) {
+    PA_LOG(WARNING) << "Attempted to RecordFirstStatusShownToUser() "
+                       "without initial timestamp recorded.";
+    NOTREACHED();
+    return;
+  }
+
+  const std::string histogram_status_suffix =
+      GetHistogramStatusSuffix(unlockable);
+
+  base::Time now = base::DefaultClock::GetInstance()->Now();
+  base::TimeDelta show_lock_screen_to_show_first_status_to_user_duration =
+      now - show_lock_screen_time_;
+
+  if (screenlock_type_ == ProximityAuthSystem::SESSION_LOCK) {
+    RecordExtendedDurationTimerMetric(
+        "SmartLock.Performance.ShowLockScreenToShowFirstStatusToUserDuration."
+        "Unlock",
+        show_lock_screen_to_show_first_status_to_user_duration);
+    RecordExtendedDurationTimerMetric(
+        "SmartLock.Performance.ShowLockScreenToShowFirstStatusToUserDuration."
+        "Unlock." +
+            histogram_status_suffix,
+        show_lock_screen_to_show_first_status_to_user_duration);
+  }
+
+  // TODO(crbug.com/905438): Implement similar SignIn metrics.
 }
 
 void UnlockManagerImpl::ResetPerformanceMetricsTimestamps() {
-  attempt_secure_connection_start_time_ = base::Time();
+  show_lock_screen_time_ = base::Time();
+  initial_scan_start_time_ = base::Time();
   attempt_get_remote_status_start_time_ = base::Time();
 }
 
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl.h b/chromeos/components/proximity_auth/unlock_manager_impl.h
index fc52823..1ff97110 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl.h
+++ b/chromeos/components/proximity_auth/unlock_manager_impl.h
@@ -117,6 +117,11 @@
   // yet be trusted.
   bool IsBluetoothAdapterRecoveringFromSuspend() const;
 
+  // Called once BluetoothAdapter has recovered after resuming from suspend,
+  // meaning its presence and power values can be trusted again. This method
+  // checks if Bluetooth is enabled; if it is not, it cancels the initial scan.
+  void OnBluetoothAdapterPresentAndPoweredChanged();
+
   // If the RemoteDeviceLifeCycle is available, ensure it is started (but only
   // if Bluetooth is available).
   void AttemptToStartRemoteDeviceLifecycle();
@@ -170,9 +175,13 @@
   // yet authenticated.
   Messenger* GetMessenger();
 
-  // Records UMA performance metrics for the unlockable remote status being
-  // received.
-  void RecordUnlockableRemoteStatusReceived();
+  // Records UMA performance metrics for the first remote status (regardless of
+  // whether it's unlockable) being received.
+  void RecordFirstRemoteStatusReceived(bool unlockable);
+
+  // Records UMA performance metrics for the first status shown to the user
+  // (regardless of whether it's unlockable/green).
+  void RecordFirstStatusShownToUser(bool unlockable);
 
   // Clears the timers for beginning a scan and fetching remote status.
   void ResetPerformanceMetricsTimestamps();
@@ -231,13 +240,27 @@
   // is fixed.
   bool was_bluetooth_present_and_powered_before_last_suspend_ = false;
 
+  // True only if the remote device has responded with a remote status, either
+  // "unlockable" or otherwise.
+  bool has_received_first_remote_status_ = false;
+
+  // True only if the user has been shown a Smart Lock status and tooltip,
+  // either "unlockable" (green) or otherwise (yellow).
+  bool has_user_been_shown_first_status_ = false;
+
   // The state of the current screen lock UI.
   ScreenlockState screenlock_state_ = ScreenlockState::INACTIVE;
 
-  // The timestamp of when UnlockManager begins to try to establish a secure
-  // connection to the requested remote device of the provided
-  // RemoteDeviceLifeCycle.
-  base::Time attempt_secure_connection_start_time_;
+  // The timestamp of when the lock or login screen is shown to the user. Begins
+  // when the screen is locked, the system is rebooted, the clamshell lid is
+  // opened, or another user pod is switched to on the login screen.
+  base::Time show_lock_screen_time_;
+
+  // The timestamp of when UnlockManager begins to perform the initial scan for
+  // the requested remote device of the provided RemoteDeviceLifeCycle. Usually
+  // begins right after |show_lock_screen_time_|, unless Bluetooth is disabled.
+  // If Bluetooth is re-enabled, it also begins.
+  base::Time initial_scan_start_time_;
 
   // The timestamp of when UnlockManager successfully establishes a secure
   // connection to the requested remote device of the provided
diff --git a/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc b/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
index c06f7e1..e4a962b 100644
--- a/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
+++ b/chromeos/components/proximity_auth/unlock_manager_impl_unittest.cc
@@ -299,18 +299,6 @@
 }
 
 TEST_F(ProximityAuthUnlockManagerImplTest,
-       IsUnlockAllowed_SecureChannelNotEstablished) {
-  CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
-
-  life_cycle_.set_messenger(nullptr);
-  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
-  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::AUTHENTICATING);
-  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenUnlocked);
-
-  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
-}
-
-TEST_F(ProximityAuthUnlockManagerImplTest,
        IsUnlockAllowed_RemoteDeviceLifeCycleIsNull) {
   CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
 
@@ -628,7 +616,7 @@
 }
 
 TEST_F(ProximityAuthUnlockManagerImplTest,
-       BluetoothOffMessagePresentedImmediatelyIfBluetoothWasOffBeforeSuspend) {
+       BluetoothOffMessageShownImmediatelyIfBluetoothWasOffBeforeSuspend) {
   CreateUnlockManager(ProximityAuthSystem::SESSION_LOCK);
 
   ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index 3aa9075f..1fd598e0 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -178,7 +178,10 @@
                            ImportFormData_ImportCreditCardRecordType_NewCard);
   FRIEND_TEST_ALL_PREFIXES(
       FormDataImporterTest,
-      ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard);
+      ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard_EditableExpDateOff);
+  FRIEND_TEST_ALL_PREFIXES(
+      FormDataImporterTest,
+      ImportFormData_ImportCreditCardRecordType_NewCard_ExpiredCard_WithExpDateFixFlow);
   FRIEND_TEST_ALL_PREFIXES(
       FormDataImporterTest,
       ImportFormData_ImportCreditCardRecordType_NoCard_InvalidCardNumber);
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 113a581..a672b8a 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -1432,8 +1432,11 @@
   ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size());
 }
 
-// Tests that an invalid credit card expiration is not extracted.
+// Tests that an invalid credit card expiration is not extracted when the
+// expiration date fix flow experiment is disabled.
 TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillUpstreamEditableExpirationDate);
   FormData form;
   form.url = GURL("https://wwww.foo.com");
 
@@ -2364,8 +2367,11 @@
 }
 
 // Ensures that |imported_credit_card_record_type_| is set correctly.
-TEST_F(FormDataImporterTest,
-       ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard) {
+TEST_F(
+    FormDataImporterTest,
+    ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard_EditableExpDateOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillUpstreamEditableExpirationDate);
   // Simulate a form submission with an expired credit card.
   FormData form;
   form.url = GURL("https://wwww.foo.com");
@@ -2388,6 +2394,33 @@
 }
 
 // Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(
+    FormDataImporterTest,
+    ImportFormData_ImportCreditCardRecordType_NewCard_ExpiredCard_WithExpDateFixFlow) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillUpstreamEditableExpirationDate);
+  // Simulate a form submission with an expired credit card.
+  FormData form;
+  form.url = GURL("https://wwww.foo.com");
+
+  AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+                        "1999");
+
+  FormStructure form_structure(form);
+  form_structure.DetermineHeuristicTypes();
+  std::unique_ptr<CreditCard> imported_credit_card;
+  EXPECT_TRUE(form_data_importer_->ImportFormData(
+      form_structure, /*profile_autofill_enabled=*/true,
+      /*credit_card_autofill_enabled=*/true,
+      /*should_return_local_card=*/true, &imported_credit_card));
+  ASSERT_TRUE(imported_credit_card);
+  // |imported_credit_card_record_type_| should be NEW_CARD because card was
+  // successfully imported from the form via the expiration date fix flow.
+  ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+              FormDataImporter::ImportedCreditCardRecordType::NEW_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
 TEST_F(FormDataImporterTest,
        ImportFormData_ImportCreditCardRecordType_NoCard_NoCardOnForm) {
   // Simulate a form submission with no credit card on form.
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 69e6cb6..2a6a856e 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -2964,7 +2964,37 @@
 }
 
 TEST_F(CreditCardSaveManagerTest,
-       UploadCreditCard_RequestExpirationDateIfTestingExperimentOn) {
+       UploadCreditCard_DoNotRequestExpirationDate_EditableExpDateOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillUpstreamEditableExpirationDate);
+  // Create, fill and submit an address form in order to establish a recent
+  // profile which can be selected for the upload request.
+  FormData address_form;
+  test::CreateTestAddressFormData(&address_form);
+  FormsSeen(std::vector<FormData>(1, address_form));
+  ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions());
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+  credit_card_form.fields[2].value = ASCIIToUTF16("");
+  credit_card_form.fields[3].value = ASCIIToUTF16("");
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  base::HistogramTester histogram_tester;
+  FormSubmitted(credit_card_form);
+  EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled());
+  EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+}
+
+TEST_F(CreditCardSaveManagerTest,
+       UploadCreditCard_RequestExpirationDateViaExpDateFixFlow) {
   scoped_feature_list_.InitAndEnableFeature(
       features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
@@ -3180,7 +3210,7 @@
 }
 
 TEST_F(CreditCardSaveManagerTest,
-       UploadCreditCard_RequestCardholderNameIfTestingExperimentOn) {
+       UploadCreditCard_RequestCardholderNameViaNameFixFlow) {
   scoped_feature_list_.InitAndEnableFeature(
       features::kAutofillUpstreamAlwaysRequestCardholderName);
 
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index bd755582..ed1add6f 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -159,8 +159,13 @@
 };
 
 const base::Feature kAutofillUpstreamEditableExpirationDate{
-    "AutofillUpstreamEditableExpirationDate",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+  "AutofillUpstreamEditableExpirationDate",
+#if defined(OS_ANDROID)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 
 bool ShouldShowImprovedUserConsentForCreditCardSave() {
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
diff --git a/components/feed/core/feed_content_database.cc b/components/feed/core/feed_content_database.cc
index da4d432..5b452c56 100644
--- a/components/feed/core/feed_content_database.cc
+++ b/components/feed/core/feed_content_database.cc
@@ -22,9 +22,6 @@
 
 namespace {
 
-using StorageEntryVector =
-    leveldb_proto::ProtoDatabase<ContentStorageProto>::KeyEntryVector;
-
 const char kContentDatabaseFolder[] = "content";
 
 const size_t kDatabaseWriteBufferSizeBytes = 64 * 1024;                 // 64KB
@@ -51,19 +48,38 @@
 FeedContentDatabase::FeedContentDatabase(
     leveldb_proto::ProtoDatabaseProvider* proto_database_provider,
     const base::FilePath& database_folder)
-    : FeedContentDatabase(proto_database_provider->GetDB<ContentStorageProto>(
+    : database_status_(InitStatus::kNotInitialized),
+      task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
+      storage_database_(proto_database_provider->GetDB<ContentStorageProto>(
           leveldb_proto::ProtoDbType::FEED_CONTENT_DATABASE,
           database_folder.AppendASCII(kContentDatabaseFolder),
-          base::CreateSequencedTaskRunner(
-              {base::ThreadPool(), base::MayBlock(),
-               base::TaskPriority::BEST_EFFORT,
-               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+          task_runner_)) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&FeedContentDatabase::InitInternal,
+                                        weak_ptr_factory_.GetWeakPtr()));
+}
 
+// Used for testing.
 FeedContentDatabase::FeedContentDatabase(
     std::unique_ptr<leveldb_proto::ProtoDatabase<ContentStorageProto>>
-        storage_database)
+        storage_database,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
     : database_status_(InitStatus::kNotInitialized),
+      task_runner_(task_runner),
       storage_database_(std::move(storage_database)) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&FeedContentDatabase::InitInternal,
+                                        weak_ptr_factory_.GetWeakPtr()));
+}
+
+FeedContentDatabase::~FeedContentDatabase() = default;
+
+bool FeedContentDatabase::IsInitialized() const {
+  return database_status_ == InitStatus::kOK;
+}
+
+void FeedContentDatabase::InitInternal() {
   leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
   options.write_buffer_size = base::SysInfo::IsLowEndDevice()
                                   ? kDatabaseWriteBufferSizeBytesForLowEndDevice
@@ -74,41 +90,48 @@
                               weak_ptr_factory_.GetWeakPtr()));
 }
 
-FeedContentDatabase::~FeedContentDatabase() = default;
-
-bool FeedContentDatabase::IsInitialized() const {
-  return database_status_ == InitStatus::kOK;
-}
-
 void FeedContentDatabase::LoadContent(const std::vector<std::string>& keys,
                                       ContentLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   std::unordered_set<std::string> key_set(keys.begin(), keys.end());
 
-  storage_database_->LoadEntriesWithFilter(
-      base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
-      CreateReadOptions(), /* target_prefix */ "",
-      base::BindOnce(&FeedContentDatabase::OnLoadEntriesForLoadContent,
-                     weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
-                     std::move(callback)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedContentDatabase::LoadEntriesWithFilterInternal,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
+          std::move(callback)));
 }
 
 void FeedContentDatabase::LoadContentByPrefix(const std::string& prefix,
                                               ContentLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedContentDatabase::LoadEntriesWithFilterInternal,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindRepeating(&DatabasePrefixFilter, std::move(prefix)),
+          std::move(callback)));
+}
 
+void FeedContentDatabase::LoadEntriesWithFilterInternal(
+    const leveldb_proto::KeyFilter& key_filter,
+    ContentLoadCallback callback) {
   storage_database_->LoadEntriesWithFilter(
-      base::BindRepeating(&DatabasePrefixFilter, std::move(prefix)),
-      CreateReadOptions(), /* target_prefix */ "",
+      std::move(key_filter), CreateReadOptions(), /* target_prefix */ "",
       base::BindOnce(&FeedContentDatabase::OnLoadEntriesForLoadContent,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
                      std::move(callback)));
 }
 
 void FeedContentDatabase::LoadAllContentKeys(ContentKeyCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedContentDatabase::LoadKeysInternal,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
 
+void FeedContentDatabase::LoadKeysInternal(ContentKeyCallback callback) {
   storage_database_->LoadKeys(
       base::BindOnce(&FeedContentDatabase::OnLoadKeysForLoadAllContentKeys,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
@@ -118,7 +141,6 @@
 void FeedContentDatabase::CommitContentMutation(
     std::unique_ptr<ContentMutation> content_mutation,
     ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(content_mutation);
 
   UMA_HISTOGRAM_COUNTS_100(
@@ -126,8 +148,8 @@
       content_mutation->Size());
 
   if (content_mutation->Empty()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), true));
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(std::move(callback), true));
     return;
   }
 
@@ -164,8 +186,8 @@
       break;
     default:
       // Operation type is not supported, therefore failing immediately.
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), false));
+      task_runner_->PostTask(FROM_HERE,
+                             base::BindOnce(std::move(callback), false));
   }
 }
 
@@ -181,10 +203,12 @@
   proto.set_content_data(operation.value());
   contents_to_save->emplace_back(proto.key(), std::move(proto));
 
-  storage_database_->UpdateEntries(
-      std::move(contents_to_save), std::make_unique<std::vector<std::string>>(),
-      base::BindOnce(&FeedContentDatabase::OnOperationCommitted,
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedContentDatabase::UpdateEntriesInternal,
                      weak_ptr_factory_.GetWeakPtr(),
+                     std::move(contents_to_save),
+                     std::make_unique<std::vector<std::string>>(),
                      std::move(content_mutation), std::move(callback)));
 }
 
@@ -197,8 +221,22 @@
   auto content_to_delete = std::make_unique<std::vector<std::string>>(
       std::initializer_list<std::string>({operation.key()}));
 
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedContentDatabase::UpdateEntriesInternal,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::make_unique<StorageEntryVector>(),
+                     std::move(content_to_delete), std::move(content_mutation),
+                     std::move(callback)));
+}
+
+void FeedContentDatabase::UpdateEntriesInternal(
+    std::unique_ptr<StorageEntryVector> entries_to_save,
+    std::unique_ptr<std::vector<std::string>> keys_to_remove,
+    std::unique_ptr<ContentMutation> content_mutation,
+    ConfirmationCallback callback) {
   storage_database_->UpdateEntries(
-      std::make_unique<StorageEntryVector>(), std::move(content_to_delete),
+      std::move(entries_to_save), std::move(keys_to_remove),
       base::BindOnce(&FeedContentDatabase::OnOperationCommitted,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(content_mutation), std::move(callback)));
@@ -210,12 +248,13 @@
     ConfirmationCallback callback) {
   DCHECK_EQ(operation.type(), ContentOperation::CONTENT_DELETE_BY_PREFIX);
 
-  storage_database_->UpdateEntriesWithRemoveFilter(
-      std::make_unique<StorageEntryVector>(),
-      base::BindRepeating(&DatabasePrefixFilter, operation.prefix()),
-      base::BindOnce(&FeedContentDatabase::OnOperationCommitted,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(content_mutation), std::move(callback)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedContentDatabase::UpdateEntriesWithRemoveFilterInternal,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindRepeating(&DatabasePrefixFilter, operation.prefix()),
+          std::move(content_mutation), std::move(callback)));
 }
 
 void FeedContentDatabase::DeleteAllContent(
@@ -225,9 +264,21 @@
   DCHECK_EQ(operation.type(), ContentOperation::CONTENT_DELETE_ALL);
 
   std::string key_prefix = "";
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedContentDatabase::UpdateEntriesWithRemoveFilterInternal,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+          std::move(content_mutation), std::move(callback)));
+}
+
+void FeedContentDatabase::UpdateEntriesWithRemoveFilterInternal(
+    const leveldb_proto::KeyFilter& key_filter,
+    std::unique_ptr<ContentMutation> content_mutation,
+    ConfirmationCallback callback) {
   storage_database_->UpdateEntriesWithRemoveFilter(
-      std::make_unique<StorageEntryVector>(),
-      base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+      std::make_unique<StorageEntryVector>(), std::move(key_filter),
       base::BindOnce(&FeedContentDatabase::OnOperationCommitted,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(content_mutation), std::move(callback)));
diff --git a/components/feed/core/feed_content_database.h b/components/feed/core/feed_content_database.h
index 6223adf..0d951f3 100644
--- a/components/feed/core/feed_content_database.h
+++ b/components/feed/core/feed_content_database.h
@@ -27,7 +27,9 @@
 using InitStatus = leveldb_proto::Enums::InitStatus;
 
 // FeedContentDatabase is leveldb backend store for Feed's content storage data.
-// Feed's content data are key-value pairs.
+// Feed's content data are key-value pairs. In order to support callers from
+// different threads, this class posts all database operations to an owned
+// sequenced task runner.
 class FeedContentDatabase {
  public:
   using KeyAndData = std::pair<std::string, std::string>;
@@ -46,6 +48,9 @@
   // the entry's existence.
   using ConfirmationCallback = base::OnceCallback<void(bool)>;
 
+  using StorageEntryVector =
+      leveldb_proto::ProtoDatabase<ContentStorageProto>::KeyEntryVector;
+
   // Initializes the database with |proto_database_provider| and
   // |database_folder|.
   FeedContentDatabase(
@@ -56,7 +61,8 @@
   // Useful for testing.
   explicit FeedContentDatabase(
       std::unique_ptr<leveldb_proto::ProtoDatabase<ContentStorageProto>>
-          storage_database);
+          storage_database,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
 
   ~FeedContentDatabase();
 
@@ -102,6 +108,21 @@
                         std::unique_ptr<ContentMutation> content_mutation,
                         ConfirmationCallback callback);
 
+  // The following *Internal methods must be executed from |task_runner_|.
+  void InitInternal();
+  void LoadEntriesWithFilterInternal(const leveldb_proto::KeyFilter& key_filter,
+                                     ContentLoadCallback callback);
+  void LoadKeysInternal(ContentKeyCallback callback);
+  void UpdateEntriesInternal(
+      std::unique_ptr<StorageEntryVector> entries_to_save,
+      std::unique_ptr<std::vector<std::string>> keys_to_remove,
+      std::unique_ptr<ContentMutation> content_mutation,
+      ConfirmationCallback callback);
+  void UpdateEntriesWithRemoveFilterInternal(
+      const leveldb_proto::KeyFilter& key_filter,
+      std::unique_ptr<ContentMutation> content_mutation,
+      ConfirmationCallback callback);
+
   // Callback methods given to |storage_database_| for async responses.
   void OnDatabaseInitialized(InitStatus status);
   void OnLoadEntriesForLoadContent(
@@ -121,12 +142,13 @@
   // Status of the database initialization.
   InitStatus database_status_;
 
+  // Task runner on which to execute database calls.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // The database for storing content storage information.
   std::unique_ptr<leveldb_proto::ProtoDatabase<ContentStorageProto>>
       storage_database_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
-
   base::WeakPtrFactory<FeedContentDatabase> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(FeedContentDatabase);
diff --git a/components/feed/core/feed_content_database_unittest.cc b/components/feed/core/feed_content_database_unittest.cc
index a739f003..f154aace 100644
--- a/components/feed/core/feed_content_database_unittest.cc
+++ b/components/feed/core/feed_content_database_unittest.cc
@@ -57,10 +57,14 @@
     auto storage_db =
         std::make_unique<FakeDB<ContentStorageProto>>(&content_db_storage_);
 
+    task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+        {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+
     content_db_ = storage_db.get();
-    feed_db_ = std::make_unique<FeedContentDatabase>(std::move(storage_db));
+    feed_db_ = std::make_unique<FeedContentDatabase>(std::move(storage_db),
+                                                     task_runner_);
     if (init_database) {
-      content_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+      InitStatusCallback(content_db_, leveldb_proto::Enums::InitStatus::kOK);
       ASSERT_TRUE(db()->IsInitialized());
     }
   }
@@ -73,8 +77,55 @@
     content_db_storage_[key] = storage_proto;
   }
 
+  // Since the FakeDB implementation doesn't run callbacks on the same task
+  // runner as the original request was made (like the real ProtoDatabase impl
+  // does), we explicitly post all callbacks onto the DB task runner here.
+  void InitStatusCallback(FakeDB<ContentStorageProto>* storage_db,
+                          leveldb_proto::Enums::InitStatus status) {
+    task_runner()->PostTask(FROM_HERE,
+                            base::BindOnce(
+                                [](FakeDB<ContentStorageProto>* storage_db,
+                                   leveldb_proto::Enums::InitStatus status) {
+                                  storage_db->InitStatusCallback(status);
+                                },
+                                storage_db, status));
+    RunUntilIdle();
+  }
+  void LoadCallback(FakeDB<ContentStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce([](FakeDB<ContentStorageProto>* storage_db,
+                          bool success) { storage_db->LoadCallback(success); },
+                       storage_db, success));
+    RunUntilIdle();
+  }
+  void LoadKeysCallback(FakeDB<ContentStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](FakeDB<ContentStorageProto>* storage_db, bool success) {
+              storage_db->LoadKeysCallback(success);
+            },
+            storage_db, success));
+    RunUntilIdle();
+  }
+  void UpdateCallback(FakeDB<ContentStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](FakeDB<ContentStorageProto>* storage_db, bool success) {
+              storage_db->UpdateCallback(success);
+            },
+            storage_db, success));
+    RunUntilIdle();
+  }
+
   void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner() {
+    return task_runner_;
+  }
+
   FakeDB<ContentStorageProto>* storage_db() { return content_db_; }
 
   FeedContentDatabase* db() { return feed_db_.get(); }
@@ -91,6 +142,8 @@
 
   std::map<std::string, ContentStorageProto> content_db_storage_;
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // Owned by |feed_db_|.
   FakeDB<ContentStorageProto>* content_db_;
 
@@ -106,7 +159,8 @@
 
   CreateDatabase(/*init_database=*/false);
 
-  storage_db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  InitStatusCallback(storage_db(), leveldb_proto::Enums::InitStatus::kOK);
+
   EXPECT_TRUE(db()->IsInitialized());
 }
 
@@ -118,7 +172,7 @@
       {kContentKey1},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -144,7 +198,7 @@
       {kContentKey2, kContentKey3},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -172,7 +226,7 @@
       kContentKeyPrefix,
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -193,7 +247,7 @@
       });
   db()->LoadAllContentKeys(base::BindOnce(
       &FeedContentDatabaseTest::OnContentKeyReceived, base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
+  LoadKeysCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaSizeHistogramName,
                                 /*size=*/2, 1);
@@ -213,8 +267,8 @@
       std::move(content_mutation),
       base::BindOnce(&FeedContentDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure they're there.
   EXPECT_CALL(*this, OnContentEntriesReceived(_, _))
@@ -231,7 +285,7 @@
       {kContentKey1, kContentKey2},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/2, 1);
@@ -255,8 +309,8 @@
       std::move(content_mutation),
       base::BindOnce(&FeedContentDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure only |kContentKey2| got deleted.
   EXPECT_CALL(*this, OnContentEntriesReceived(_, _))
@@ -271,7 +325,7 @@
       {kContentKey1, kContentKey2},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/2, 1);
@@ -294,7 +348,7 @@
       std::move(content_mutation),
       base::BindOnce(&FeedContentDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure |kContentKey1| and |kContentKey2| got deleted.
   EXPECT_CALL(*this, OnContentEntriesReceived(_, _))
@@ -307,7 +361,7 @@
       {kContentKey1, kContentKey2},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/1, 1);
@@ -331,7 +385,7 @@
       std::move(content_mutation),
       base::BindOnce(&FeedContentDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure |kContentKey1| and |kContentKey2| got deleted.
   EXPECT_CALL(*this, OnContentEntriesReceived(_, _))
@@ -344,7 +398,7 @@
       {kContentKey1, kContentKey2},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/1, 1);
@@ -366,10 +420,10 @@
       std::move(content_mutation),
       base::BindOnce(&FeedContentDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
-  storage_db()->UpdateCallback(true);
-  storage_db()->UpdateCallback(true);
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure only |kContentKey2| got deleted.
   EXPECT_CALL(*this, OnContentEntriesReceived(_, _))
@@ -384,7 +438,7 @@
       {kContentKey1, kContentKey2},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(true);
+  LoadCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/4, 1);
@@ -408,7 +462,7 @@
       {kContentKey2, kContentKey3},
       base::BindOnce(&FeedContentDatabaseTest::OnContentEntriesReceived,
                      base::Unretained(this)));
-  storage_db()->LoadCallback(false);
+  LoadCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -426,7 +480,7 @@
       });
   db()->LoadAllContentKeys(base::BindOnce(
       &FeedContentDatabaseTest::OnContentKeyReceived, base::Unretained(this)));
-  storage_db()->LoadKeysCallback(false);
+  LoadKeysCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaSizeHistogramName, 0);
   histogram().ExpectTotalCount(kUmaLoadKeysTimeHistogramName, 1);
diff --git a/components/feed/core/feed_journal_database.cc b/components/feed/core/feed_journal_database.cc
index 0030fad..1903ee1 100644
--- a/components/feed/core/feed_journal_database.cc
+++ b/components/feed/core/feed_journal_database.cc
@@ -20,9 +20,6 @@
 
 namespace {
 
-using StorageEntryVector =
-    leveldb_proto::ProtoDatabase<JournalStorageProto>::KeyEntryVector;
-
 const char kJournalDatabaseFolder[] = "journal";
 
 const size_t kDatabaseWriteBufferSizeBytes = 64 * 1024;                 // 64KB
@@ -39,19 +36,38 @@
 FeedJournalDatabase::FeedJournalDatabase(
     leveldb_proto::ProtoDatabaseProvider* proto_database_provider,
     const base::FilePath& database_folder)
-    : FeedJournalDatabase(proto_database_provider->GetDB<JournalStorageProto>(
+    : database_status_(InitStatus::kNotInitialized),
+      task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
+      storage_database_(proto_database_provider->GetDB<JournalStorageProto>(
           leveldb_proto::ProtoDbType::FEED_JOURNAL_DATABASE,
           database_folder.AppendASCII(kJournalDatabaseFolder),
-          base::CreateSequencedTaskRunner(
-              {base::ThreadPool(), base::MayBlock(),
-               base::TaskPriority::BEST_EFFORT,
-               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+          task_runner_)) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&FeedJournalDatabase::InitInternal,
+                                        weak_ptr_factory_.GetWeakPtr()));
+}
 
+// Used for testing.
 FeedJournalDatabase::FeedJournalDatabase(
     std::unique_ptr<leveldb_proto::ProtoDatabase<JournalStorageProto>>
-        storage_database)
+        storage_database,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
     : database_status_(InitStatus::kNotInitialized),
+      task_runner_(task_runner),
       storage_database_(std::move(storage_database)) {
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&FeedJournalDatabase::InitInternal,
+                                        weak_ptr_factory_.GetWeakPtr()));
+}
+
+FeedJournalDatabase::~FeedJournalDatabase() = default;
+
+bool FeedJournalDatabase::IsInitialized() const {
+  return database_status_ == InitStatus::kOK;
+}
+
+void FeedJournalDatabase::InitInternal() {
   leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
   options.write_buffer_size = base::SysInfo::IsLowEndDevice()
                                   ? kDatabaseWriteBufferSizeBytesForLowEndDevice
@@ -62,36 +78,33 @@
                               weak_ptr_factory_.GetWeakPtr()));
 }
 
-FeedJournalDatabase::~FeedJournalDatabase() = default;
-
-bool FeedJournalDatabase::IsInitialized() const {
-  return database_status_ == InitStatus::kOK;
-}
-
 void FeedJournalDatabase::LoadJournal(const std::string& key,
                                       JournalLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      key, base::BindOnce(&FeedJournalDatabase::OnGetEntryForLoadJournal,
-                          weak_ptr_factory_.GetWeakPtr(),
-                          base::TimeTicks::Now(), std::move(callback)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedJournalDatabase::GetEntryInternal,
+          weak_ptr_factory_.GetWeakPtr(), std::move(key),
+          base::BindOnce(&FeedJournalDatabase::OnGetEntryForLoadJournal,
+                         weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
+                         std::move(callback))));
 }
 
 void FeedJournalDatabase::DoesJournalExist(const std::string& key,
                                            CheckExistingCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  storage_database_->GetEntry(
-      key, base::BindOnce(&FeedJournalDatabase::OnGetEntryForDoesJournalExist,
-                          weak_ptr_factory_.GetWeakPtr(),
-                          base::TimeTicks::Now(), std::move(callback)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedJournalDatabase::GetEntryInternal,
+          weak_ptr_factory_.GetWeakPtr(), std::move(key),
+          base::BindOnce(&FeedJournalDatabase::OnGetEntryForDoesJournalExist,
+                         weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
+                         std::move(callback))));
 }
 
 void FeedJournalDatabase::CommitJournalMutation(
     std::unique_ptr<JournalMutation> journal_mutation,
     ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(journal_mutation);
 
   UMA_HISTOGRAM_COUNTS_100(
@@ -99,15 +112,15 @@
       journal_mutation->Size());
 
   if (journal_mutation->Empty()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), true));
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(std::move(callback), true));
     return;
   }
 
   // Skip loading journal if the first operation is JOURNAL_DELETE.
   if (journal_mutation->FirstOperationType() ==
       JournalOperation::JOURNAL_DELETE) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&FeedJournalDatabase::PerformOperations,
                        weak_ptr_factory_.GetWeakPtr(), nullptr,
@@ -116,16 +129,32 @@
   }
 
   std::string journal_name = journal_mutation->journal_name();
-  storage_database_->GetEntry(
-      journal_name,
-      base::BindOnce(&FeedJournalDatabase::OnGetEntryForCommitJournalMutation,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(journal_mutation), std::move(callback)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &FeedJournalDatabase::GetEntryInternal,
+          weak_ptr_factory_.GetWeakPtr(), std::move(journal_name),
+          base::BindOnce(
+              &FeedJournalDatabase::OnGetEntryForCommitJournalMutation,
+              weak_ptr_factory_.GetWeakPtr(), std::move(journal_mutation),
+              std::move(callback))));
+}
+
+void FeedJournalDatabase::GetEntryInternal(
+    const std::string& key,
+    leveldb_proto::Callbacks::Internal<JournalStorageProto>::GetCallback
+        callback) {
+  storage_database_->GetEntry(key, std::move(callback));
 }
 
 void FeedJournalDatabase::LoadAllJournalKeys(JournalLoadCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedJournalDatabase::LoadKeysInternal,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
 
+void FeedJournalDatabase::LoadKeysInternal(JournalLoadCallback callback) {
   storage_database_->LoadKeys(
       base::BindOnce(&FeedJournalDatabase::OnLoadKeysForLoadAllJournalKeys,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
@@ -133,8 +162,14 @@
 }
 
 void FeedJournalDatabase::DeleteAllJournals(ConfirmationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedJournalDatabase::DeleteAllEntriesInternal,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
 
+void FeedJournalDatabase::DeleteAllEntriesInternal(
+    ConfirmationCallback callback) {
   // For deleting all, filter method always return true.
   storage_database_->UpdateEntriesWithRemoveFilter(
       std::make_unique<StorageEntryVector>(),
@@ -198,8 +233,21 @@
     journals_to_save->emplace_back(it->first, std::move(it->second));
   }
 
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FeedJournalDatabase::UpdateEntriesInternal,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(journals_to_save), std::move(journals_to_delete),
+                     std::move(start_time), std::move(callback)));
+}
+
+void FeedJournalDatabase::UpdateEntriesInternal(
+    std::unique_ptr<StorageEntryVector> entries_to_save,
+    std::unique_ptr<std::vector<std::string>> keys_to_remove,
+    base::TimeTicks start_time,
+    ConfirmationCallback callback) {
   storage_database_->UpdateEntries(
-      std::move(journals_to_save), std::move(journals_to_delete),
+      std::move(entries_to_save), std::move(keys_to_remove),
       base::BindOnce(&FeedJournalDatabase::OnOperationCommitted,
                      weak_ptr_factory_.GetWeakPtr(), std::move(start_time),
                      std::move(callback)));
diff --git a/components/feed/core/feed_journal_database.h b/components/feed/core/feed_journal_database.h
index 4ded367..f79cd06 100644
--- a/components/feed/core/feed_journal_database.h
+++ b/components/feed/core/feed_journal_database.h
@@ -29,7 +29,9 @@
 using InitStatus = leveldb_proto::Enums::InitStatus;
 
 // FeedJournalDatabase is leveldb backend store for Feed's journal storage data.
-// Feed's journal data are key-value pairs.
+// Feed's journal data are key-value pairs.  In order to support callers from
+// different threads, this class posts all database operations to an owned
+// sequenced task runner.
 class FeedJournalDatabase {
  public:
   // Returns the journal data as a vector of strings when calling loading data
@@ -47,6 +49,9 @@
 
   using JournalMap = base::flat_map<std::string, JournalStorageProto>;
 
+  using StorageEntryVector =
+      leveldb_proto::ProtoDatabase<JournalStorageProto>::KeyEntryVector;
+
   // Initializes the database with |proto_database_provider| and
   // |database_folder|.
   FeedJournalDatabase(
@@ -57,7 +62,8 @@
   // Useful for testing.
   explicit FeedJournalDatabase(
       std::unique_ptr<leveldb_proto::ProtoDatabase<JournalStorageProto>>
-          storage_database);
+          storage_database,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
 
   ~FeedJournalDatabase();
 
@@ -88,9 +94,9 @@
 
  private:
   // This method performs JournalOperation in the |journal_mutation|.
-  // If the first operation in |journal_mutation| is JOURNAL_DELETE, journal can
-  // be empty, otherwise we need to load |journal| from database and then pass
-  // to this method.
+  // If the first operation in |journal_mutation| is JOURNAL_DELETE, journal
+  // can be empty, otherwise we need to load |journal| from database and
+  // then pass to this method.
   void PerformOperations(std::unique_ptr<JournalStorageProto> journal,
                          std::unique_ptr<JournalMutation> journal_mutation,
                          ConfirmationCallback callback);
@@ -99,6 +105,20 @@
                         JournalMap copy_to_journal,
                         ConfirmationCallback callback);
 
+  // The following *Internal methods must be executed from |task_runner_|.
+  void InitInternal();
+  void GetEntryInternal(
+      const std::string& key,
+      leveldb_proto::Callbacks::Internal<JournalStorageProto>::GetCallback
+          callback);
+  void LoadKeysInternal(JournalLoadCallback callback);
+  void DeleteAllEntriesInternal(ConfirmationCallback callback);
+  void UpdateEntriesInternal(
+      std::unique_ptr<StorageEntryVector> entries_to_save,
+      std::unique_ptr<std::vector<std::string>> keys_to_remove,
+      base::TimeTicks start_time,
+      ConfirmationCallback callback);
+
   // Callback methods given to |storage_database_| for async responses.
   void OnDatabaseInitialized(InitStatus status);
   void OnGetEntryForLoadJournal(base::TimeTicks start_time,
@@ -130,12 +150,13 @@
   // Status of the database initialization.
   InitStatus database_status_;
 
+  // Task runner on which to execute database calls.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // The database for storing journal storage information.
   std::unique_ptr<leveldb_proto::ProtoDatabase<JournalStorageProto>>
       storage_database_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
-
   base::WeakPtrFactory<FeedJournalDatabase> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(FeedJournalDatabase);
diff --git a/components/feed/core/feed_journal_database_unittest.cc b/components/feed/core/feed_journal_database_unittest.cc
index 172d2d16..7bb4a06b 100644
--- a/components/feed/core/feed_journal_database_unittest.cc
+++ b/components/feed/core/feed_journal_database_unittest.cc
@@ -63,10 +63,14 @@
     auto storage_db =
         std::make_unique<FakeDB<JournalStorageProto>>(&journal_db_storage_);
 
+    task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+        {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+
     journal_db_ = storage_db.get();
-    feed_db_ = std::make_unique<FeedJournalDatabase>(std::move(storage_db));
+    feed_db_ = std::make_unique<FeedJournalDatabase>(std::move(storage_db),
+                                                     task_runner_);
     if (init_database) {
-      journal_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+      InitStatusCallback(journal_db_, leveldb_proto::Enums::InitStatus::kOK);
       ASSERT_TRUE(db()->IsInitialized());
     }
   }
@@ -81,8 +85,55 @@
     journal_db_storage_[key] = storage_proto;
   }
 
+  // Since the FakeDB implementation doesn't run callbacks on the same task
+  // runner as the original request was made (like the real ProtoDatabase impl
+  // does), we explicitly post all callbacks onto the DB task runner here.
+  void InitStatusCallback(FakeDB<JournalStorageProto>* storage_db,
+                          leveldb_proto::Enums::InitStatus status) {
+    task_runner()->PostTask(FROM_HERE,
+                            base::BindOnce(
+                                [](FakeDB<JournalStorageProto>* storage_db,
+                                   leveldb_proto::Enums::InitStatus status) {
+                                  storage_db->InitStatusCallback(status);
+                                },
+                                storage_db, status));
+    RunUntilIdle();
+  }
+  void GetCallback(FakeDB<JournalStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce([](FakeDB<JournalStorageProto>* storage_db,
+                          bool success) { storage_db->GetCallback(success); },
+                       storage_db, success));
+    RunUntilIdle();
+  }
+  void LoadKeysCallback(FakeDB<JournalStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](FakeDB<JournalStorageProto>* storage_db, bool success) {
+              storage_db->LoadKeysCallback(success);
+            },
+            storage_db, success));
+    RunUntilIdle();
+  }
+  void UpdateCallback(FakeDB<JournalStorageProto>* storage_db, bool success) {
+    task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](FakeDB<JournalStorageProto>* storage_db, bool success) {
+              storage_db->UpdateCallback(success);
+            },
+            storage_db, success));
+    RunUntilIdle();
+  }
+
   void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner() {
+    return task_runner_;
+  }
+
   FakeDB<JournalStorageProto>* storage_db() { return journal_db_; }
 
   FeedJournalDatabase* db() { return feed_db_.get(); }
@@ -98,6 +149,8 @@
 
   std::map<std::string, JournalStorageProto> journal_db_storage_;
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // Owned by |feed_db_|.
   FakeDB<JournalStorageProto>* journal_db_;
 
@@ -114,7 +167,7 @@
   CreateDatabase(/*init_database=*/false);
 
   EXPECT_FALSE(db()->IsInitialized());
-  storage_db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  InitStatusCallback(storage_db(), leveldb_proto::Enums::InitStatus::kOK);
   EXPECT_TRUE(db()->IsInitialized());
 }
 
@@ -140,7 +193,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -159,7 +212,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -176,8 +229,8 @@
       std::move(journal_mutation),
       base::BindOnce(&FeedJournalDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
+  GetCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   // Make sure they're there.
   EXPECT_CALL(*this, OnJournalEntryReceived(_, _))
@@ -191,7 +244,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/2, 1);
@@ -209,8 +262,8 @@
       std::move(journal_mutation),
       base::BindOnce(&FeedJournalDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
+  GetCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   // Check new instances are there.
   EXPECT_CALL(*this, OnJournalEntryReceived(_, _))
@@ -227,7 +280,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/3, 1);
@@ -251,8 +304,8 @@
       std::move(journal_mutation),
       base::BindOnce(&FeedJournalDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
-  storage_db()->UpdateCallback(true);
+  GetCallback(storage_db(), true);
+  UpdateCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/4, 1);
@@ -270,7 +323,7 @@
       kJournalKey2,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 
@@ -290,7 +343,7 @@
       kJournalKey3,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 2);
 
@@ -310,7 +363,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 3);
 }
@@ -332,7 +385,7 @@
       base::BindOnce(&FeedJournalDatabaseTest::OnStorageCommitted,
                      base::Unretained(this)));
   RunUntilIdle();
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaCommitMutationSizeHistogramName,
                                 /*operations=*/1, 1);
@@ -348,7 +401,7 @@
       kJournalKey2,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 
@@ -367,7 +420,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 2);
 }
@@ -388,7 +441,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnCheckJournalExistReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -403,7 +456,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnCheckJournalExistReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(true);
+  GetCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -428,7 +481,7 @@
   db()->LoadAllJournalKeys(
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
+  LoadKeysCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaSizeHistogramName,
                                 /*size=*/3, 1);
@@ -449,7 +502,7 @@
   EXPECT_CALL(*this, OnStorageCommitted(true));
   db()->DeleteAllJournals(base::BindOnce(
       &FeedJournalDatabaseTest::OnStorageCommitted, base::Unretained(this)));
-  storage_db()->UpdateCallback(true);
+  UpdateCallback(storage_db(), true);
 
   histogram().ExpectTotalCount(kUmaOperationCommitTimeHistogramName, 1);
 
@@ -462,7 +515,7 @@
   db()->LoadAllJournalKeys(
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->LoadKeysCallback(true);
+  LoadKeysCallback(storage_db(), true);
 
   histogram().ExpectBucketCount(kUmaSizeHistogramName,
                                 /*size=*/0, 1);
@@ -487,7 +540,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(false);
+  GetCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -505,7 +558,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(false);
+  GetCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -526,7 +579,7 @@
   db()->LoadAllJournalKeys(
       base::BindOnce(&FeedJournalDatabaseTest::OnJournalEntryReceived,
                      base::Unretained(this)));
-  storage_db()->LoadKeysCallback(false);
+  LoadKeysCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaSizeHistogramName, 0);
   histogram().ExpectTotalCount(kUmaLoadKeysTimeHistogramName, 1);
@@ -548,7 +601,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnCheckJournalExistReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(false);
+  GetCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
@@ -563,7 +616,7 @@
       kJournalKey1,
       base::BindOnce(&FeedJournalDatabaseTest::OnCheckJournalExistReceived,
                      base::Unretained(this)));
-  storage_db()->GetCallback(false);
+  GetCallback(storage_db(), false);
 
   histogram().ExpectTotalCount(kUmaLoadTimeHistogramName, 1);
 }
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css
index 98d2eeb..30c7851c 100644
--- a/components/flags_ui/resources/flags.css
+++ b/components/flags_ui/resources/flags.css
@@ -229,6 +229,12 @@
   text-transform: uppercase;
 }
 
+.screen-reader-only {
+  display: inline-block;
+  position: fixed;
+  clip: rect(0px,0px,0px,0px);
+}
+
 #promos {
   color: var(--secondary-color);
   font-size: .875rem;
diff --git a/components/flags_ui/resources/flags.html b/components/flags_ui/resources/flags.html
index 4777e01..d49d2c7 100644
--- a/components/flags_ui/resources/flags.html
+++ b/components/flags_ui/resources/flags.html
@@ -36,6 +36,8 @@
       <button id="experiment-reset-all" type="button" tabindex="3">
         Reset all to default
       </button>
+      <div class="screen-reader-only" id="reset-all-success-message"
+           role="status">Reset complete</div>
     </div>
   </div>
 </div>
diff --git a/components/flags_ui/resources/flags.js b/components/flags_ui/resources/flags.js
index e44879f..7c7b1815 100644
--- a/components/flags_ui/resources/flags.js
+++ b/components/flags_ui/resources/flags.js
@@ -143,6 +143,8 @@
 /** Reset all flags to their default values and refresh the UI. */
 function resetAllFlags() {
   chrome.send('resetAllFlags');
+  // Updating the message in order for it to get announced by screen readers.
+  $('reset-all-success-message').innerHTML += "!";
   showRestartToast(true);
   requestExperimentalFeaturesData();
 }
diff --git a/components/media_message_center/media_notification_controller.h b/components/media_message_center/media_notification_controller.h
index ae1ed4f1..3e6a629 100644
--- a/components/media_message_center/media_notification_controller.h
+++ b/components/media_message_center/media_notification_controller.h
@@ -9,6 +9,13 @@
 
 #include "base/component_export.h"
 
+template <typename T>
+class scoped_refptr;
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
 namespace media_message_center {
 
 // MediaNotificationController does the actual hiding and showing of the media
@@ -19,6 +26,14 @@
   // MediaNotificationItem when the notification should be shown/hidden.
   virtual void ShowNotification(const std::string& id) = 0;
   virtual void HideNotification(const std::string& id) = 0;
+
+  // Removes a notification item with the given request id. Called by
+  // MediaNotificationItem when it should be destroyed.
+  virtual void RemoveItem(const std::string& id) = 0;
+
+  // Returns a task runner that the MediaNotificationItem should use.
+  // It typically returns null except in tests.
+  virtual scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const = 0;
 };
 
 }  // namespace media_message_center
diff --git a/components/media_message_center/media_notification_item.cc b/components/media_message_center/media_notification_item.cc
index 00cd289..cb9a103 100644
--- a/components/media_message_center/media_notification_item.cc
+++ b/components/media_message_center/media_notification_item.cc
@@ -36,6 +36,10 @@
   return MediaNotificationItem::Source::kUnknown;
 }
 
+// How long to wait (in milliseconds) for a new media session to begin.
+constexpr base::TimeDelta kFreezeTimerDelay =
+    base::TimeDelta::FromMilliseconds(2500);
+
 }  // namespace
 
 // static
@@ -54,11 +58,154 @@
     media_session::mojom::MediaSessionInfoPtr session_info)
     : controller_(notification_controller),
       request_id_(request_id),
-      source_(GetSource(source_name)),
-      media_controller_ptr_(std::move(controller)),
-      session_info_(std::move(session_info)) {
+      source_(GetSource(source_name)) {
   DCHECK(controller_);
 
+  if (auto task_runner = notification_controller->GetTaskRunner())
+    freeze_timer_.SetTaskRunner(task_runner);
+
+  SetController(std::move(controller), std::move(session_info));
+}
+
+MediaNotificationItem::~MediaNotificationItem() {
+  controller_->HideNotification(request_id_);
+}
+
+void MediaNotificationItem::MediaSessionInfoChanged(
+    media_session::mojom::MediaSessionInfoPtr session_info) {
+  session_info_ = std::move(session_info);
+
+  MaybeUnfreeze();
+  MaybeHideOrShowNotification();
+
+  if (view_ && !frozen_)
+    view_->UpdateWithMediaSessionInfo(session_info_);
+}
+
+void MediaNotificationItem::MediaSessionMetadataChanged(
+    const base::Optional<media_session::MediaMetadata>& metadata) {
+  session_metadata_ = metadata.value_or(media_session::MediaMetadata());
+
+  view_needs_metadata_update_ = true;
+
+  MaybeUnfreeze();
+  MaybeHideOrShowNotification();
+
+  // |MaybeHideOrShowNotification()| can synchronously create a
+  // MediaNotificationView that calls |SetView()|. If that happens, then we
+  // don't want to call |view_->UpdateWithMediaMetadata()| below since |view_|
+  // will have already received the metadata when calling |SetView()|.
+  // |view_needs_metadata_update_| is set to false in |SetView()|.
+  if (view_ && view_needs_metadata_update_ && !frozen_)
+    view_->UpdateWithMediaMetadata(session_metadata_);
+
+  view_needs_metadata_update_ = false;
+}
+
+void MediaNotificationItem::MediaSessionActionsChanged(
+    const std::vector<media_session::mojom::MediaSessionAction>& actions) {
+  session_actions_ = std::set<media_session::mojom::MediaSessionAction>(
+      actions.begin(), actions.end());
+
+  if (view_ && !frozen_) {
+    DCHECK(view_);
+    view_->UpdateWithMediaActions(session_actions_);
+  }
+}
+
+void MediaNotificationItem::MediaControllerImageChanged(
+    media_session::mojom::MediaSessionImageType type,
+    const SkBitmap& bitmap) {
+  DCHECK_EQ(media_session::mojom::MediaSessionImageType::kArtwork, type);
+
+  session_artwork_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+
+  if (view_ && !frozen_)
+    view_->UpdateWithMediaArtwork(*session_artwork_);
+}
+
+void MediaNotificationItem::SetView(MediaNotificationView* view) {
+  DCHECK(view_ || view);
+
+  view_ = view;
+
+  if (view_) {
+    view_needs_metadata_update_ = false;
+    view_->UpdateWithMediaSessionInfo(session_info_);
+    view_->UpdateWithMediaMetadata(session_metadata_);
+    view_->UpdateWithMediaActions(session_actions_);
+
+    if (session_artwork_.has_value())
+      view_->UpdateWithMediaArtwork(*session_artwork_);
+  }
+}
+
+void MediaNotificationItem::FlushForTesting() {
+  media_controller_ptr_.FlushForTesting();
+}
+
+bool MediaNotificationItem::ShouldShowNotification() const {
+  // If the |is_controllable| bit is set in MediaSessionInfo then we should show
+  // a media notification.
+  if (!session_info_ || !session_info_->is_controllable)
+    return false;
+
+  // If we do not have a title and an artist then we should hide the
+  // notification.
+  if (session_metadata_.title.empty() || session_metadata_.artist.empty())
+    return false;
+
+  return true;
+}
+
+void MediaNotificationItem::OnFreezeTimerFired() {
+  DCHECK(frozen_);
+
+  if (is_bound_) {
+    controller_->HideNotification(request_id_);
+  } else {
+    controller_->RemoveItem(request_id_);
+  }
+}
+
+void MediaNotificationItem::MaybeHideOrShowNotification() {
+  if (frozen_)
+    return;
+
+  if (!ShouldShowNotification()) {
+    controller_->HideNotification(request_id_);
+    return;
+  }
+
+  // If we have an existing view, then we don't need to create a new one.
+  if (view_)
+    return;
+
+  controller_->ShowNotification(request_id_);
+
+  UMA_HISTOGRAM_ENUMERATION(kSourceHistogramName, source_);
+}
+
+void MediaNotificationItem::OnMediaSessionActionButtonPressed(
+    MediaSessionAction action) {
+  UMA_HISTOGRAM_ENUMERATION(kUserActionHistogramName, action);
+
+  if (frozen_)
+    return;
+
+  media_session::PerformMediaSessionAction(action, media_controller_ptr_);
+}
+
+void MediaNotificationItem::SetController(
+    media_session::mojom::MediaControllerPtr controller,
+    media_session::mojom::MediaSessionInfoPtr session_info) {
+  observer_receiver_.reset();
+  artwork_observer_receiver_.reset();
+
+  is_bound_ = true;
+  media_controller_ptr_ = std::move(controller);
+  session_info_ = std::move(session_info);
+
   if (media_controller_ptr_.is_bound()) {
     // Bind an observer to the associated media controller.
     media_controller_ptr_->AddObserver(
@@ -76,108 +223,27 @@
   MaybeHideOrShowNotification();
 }
 
-MediaNotificationItem::~MediaNotificationItem() {
-  controller_->HideNotification(request_id_);
-}
-
-void MediaNotificationItem::MediaSessionInfoChanged(
-    media_session::mojom::MediaSessionInfoPtr session_info) {
-  session_info_ = std::move(session_info);
-
-  MaybeHideOrShowNotification();
-
-  if (view_)
-    view_->UpdateWithMediaSessionInfo(session_info_);
-}
-
-void MediaNotificationItem::MediaSessionMetadataChanged(
-    const base::Optional<media_session::MediaMetadata>& metadata) {
-  session_metadata_ = metadata.value_or(media_session::MediaMetadata());
-
-  view_needs_metadata_update_ = true;
-
-  MaybeHideOrShowNotification();
-
-  // |MaybeHideOrShowNotification()| can synchronously create a
-  // MediaNotificationView that calls |SetView()|. If that happens, then we
-  // don't want to call |view_->UpdateWithMediaMetadata()| below since |view_|
-  // will have already received the metadata when calling |SetView()|.
-  // |view_needs_metadata_update_| is set to false in |SetView()|.
-  if (view_ && view_needs_metadata_update_)
-    view_->UpdateWithMediaMetadata(session_metadata_);
-
-  view_needs_metadata_update_ = false;
-}
-
-void MediaNotificationItem::MediaSessionActionsChanged(
-    const std::vector<media_session::mojom::MediaSessionAction>& actions) {
-  session_actions_ = std::set<media_session::mojom::MediaSessionAction>(
-      actions.begin(), actions.end());
-
-  if (view_)
-    view_->UpdateWithMediaActions(session_actions_);
-}
-
-void MediaNotificationItem::MediaControllerImageChanged(
-    media_session::mojom::MediaSessionImageType type,
-    const SkBitmap& bitmap) {
-  DCHECK_EQ(media_session::mojom::MediaSessionImageType::kArtwork, type);
-
-  session_artwork_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
-
-  if (view_)
-    view_->UpdateWithMediaArtwork(*session_artwork_);
-}
-
-void MediaNotificationItem::SetView(MediaNotificationView* view) {
-  DCHECK(view_ || view);
-
-  view_ = view;
-
-  if (view) {
-    view_needs_metadata_update_ = false;
-    view_->UpdateWithMediaSessionInfo(session_info_);
-    view_->UpdateWithMediaMetadata(session_metadata_);
-    view_->UpdateWithMediaActions(session_actions_);
-
-    if (session_artwork_.has_value())
-      view_->UpdateWithMediaArtwork(*session_artwork_);
-  }
-}
-
-void MediaNotificationItem::FlushForTesting() {
-  media_controller_ptr_.FlushForTesting();
-}
-
-void MediaNotificationItem::MaybeHideOrShowNotification() {
-  // If the |is_controllable| bit is set in MediaSessionInfo then we should show
-  // a media notification.
-  if (!session_info_ || !session_info_->is_controllable) {
-    controller_->HideNotification(request_id_);
-    return;
-  }
-
-  // If we do not have a title and an artist then we should hide the
-  // notification.
-  if (session_metadata_.title.empty() || session_metadata_.artist.empty()) {
-    controller_->HideNotification(request_id_);
-    return;
-  }
-
-  // If we have an existing view, then we don't need to create a new one.
-  if (view_)
+void MediaNotificationItem::Freeze() {
+  if (frozen_)
     return;
 
-  controller_->ShowNotification(request_id_);
+  frozen_ = true;
+  is_bound_ = false;
 
-  UMA_HISTOGRAM_ENUMERATION(kSourceHistogramName, source_);
+  freeze_timer_.Start(FROM_HERE, kFreezeTimerDelay,
+                      base::BindOnce(&MediaNotificationItem::OnFreezeTimerFired,
+                                     base::Unretained(this)));
 }
 
-void MediaNotificationItem::OnMediaSessionActionButtonPressed(
-    MediaSessionAction action) {
-  UMA_HISTOGRAM_ENUMERATION(kUserActionHistogramName, action);
+void MediaNotificationItem::MaybeUnfreeze() {
+  if (!frozen_)
+    return;
 
-  media_session::PerformMediaSessionAction(action, media_controller_ptr_);
+  if (!ShouldShowNotification() || !is_bound_)
+    return;
+
+  frozen_ = false;
+  freeze_timer_.Stop();
 }
 
 }  // namespace media_message_center
diff --git a/components/media_message_center/media_notification_item.h b/components/media_message_center/media_notification_item.h
index 08f817e6..08a3465 100644
--- a/components/media_message_center/media_notification_item.h
+++ b/components/media_message_center/media_notification_item.h
@@ -87,13 +87,30 @@
     media_controller_ptr_ = std::move(controller);
   }
 
+  void SetController(media_session::mojom::MediaControllerPtr controller,
+                     media_session::mojom::MediaSessionInfoPtr session_info);
+
+  // This will freeze the item and start a timer to destroy the item after
+  // some time has passed.
+  void Freeze();
+
+  bool frozen() const { return frozen_; }
+
  private:
+  bool ShouldShowNotification() const;
+
+  void MaybeUnfreeze();
+
+  void OnFreezeTimerFired();
+
   void MaybeHideOrShowNotification();
 
   void HideNotification();
 
   MediaNotificationController* controller_;
 
+  bool is_bound_ = true;
+
   // Weak reference to the view of the currently shown media notification.
   MediaNotificationView* view_ = nullptr;
 
@@ -118,6 +135,14 @@
   // updating |view_|'s metadata twice on a single change.
   bool view_needs_metadata_update_ = false;
 
+  // When the item is frozen the |view_| will not receive any updates to the
+  // data and no actions will be executed.
+  bool frozen_ = false;
+
+  // The timer that will notify the controller to destroy this item after it
+  // has been frozen for a certain period of time.
+  base::OneShotTimer freeze_timer_;
+
   mojo::Receiver<media_session::mojom::MediaControllerObserver>
       observer_receiver_{this};
 
diff --git a/components/media_message_center/media_notification_view_unittest.cc b/components/media_message_center/media_notification_view_unittest.cc
index e213c732..c4aa88c 100644
--- a/components/media_message_center/media_notification_view_unittest.cc
+++ b/components/media_message_center/media_notification_view_unittest.cc
@@ -67,6 +67,10 @@
   // MediaNotificationController implementation.
   MOCK_METHOD1(ShowNotification, void(const std::string& id));
   MOCK_METHOD1(HideNotification, void(const std::string& id));
+  MOCK_METHOD1(RemoveItem, void(const std::string& id));
+  scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
+    return nullptr;
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationController);
@@ -870,4 +874,72 @@
   EXPECT_EQ(base::ASCIIToUTF16("title - artist"), accessible_name());
 }
 
+TEST_F(MediaNotificationViewTest, Freezing_DoNotUpdateMetadata) {
+  media_session::MediaMetadata metadata;
+  metadata.title = base::ASCIIToUTF16("title2");
+  metadata.artist = base::ASCIIToUTF16("artist2");
+  metadata.album = base::ASCIIToUTF16("album");
+
+  GetItem()->Freeze();
+  GetItem()->MediaSessionMetadataChanged(metadata);
+
+  EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+  EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
+}
+
+TEST_F(MediaNotificationViewTest, Freezing_DoNotUpdateImage) {
+  SkBitmap image;
+  image.allocN32Pixels(10, 10);
+  image.eraseColor(SK_ColorMAGENTA);
+
+  GetItem()->Freeze();
+  GetItem()->MediaControllerImageChanged(
+      media_session::mojom::MediaSessionImageType::kArtwork, image);
+
+  EXPECT_TRUE(GetArtworkImage().isNull());
+}
+
+TEST_F(MediaNotificationViewTest, Freezing_DoNotUpdatePlaybackState) {
+  EnableAction(MediaSessionAction::kPlay);
+  EnableAction(MediaSessionAction::kPause);
+
+  GetItem()->Freeze();
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+
+  media_session::mojom::MediaSessionInfoPtr session_info(
+      media_session::mojom::MediaSessionInfo::New());
+  session_info->playback_state =
+      media_session::mojom::MediaPlaybackState::kPlaying;
+  GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+  EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+  EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+}
+
+TEST_F(MediaNotificationViewTest, Freezing_DoNotUpdateActions) {
+  EXPECT_FALSE(
+      GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
+
+  GetItem()->Freeze();
+  EnableAction(MediaSessionAction::kSeekForward);
+
+  EXPECT_FALSE(
+      GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
+}
+
+TEST_F(MediaNotificationViewTest, Freezing_DisableInteraction) {
+  EnableAllActions();
+
+  EXPECT_EQ(0, media_controller()->next_track_count());
+
+  GetItem()->Freeze();
+
+  SimulateButtonClick(MediaSessionAction::kNextTrack);
+  GetItem()->FlushForTesting();
+
+  EXPECT_EQ(0, media_controller()->next_track_count());
+}
+
 }  // namespace media_message_center
diff --git a/components/nacl/browser/nacl_browser_delegate.h b/components/nacl/browser/nacl_browser_delegate.h
index 9d73cf0..d8abebb 100644
--- a/components/nacl/browser/nacl_browser_delegate.h
+++ b/components/nacl/browser/nacl_browser_delegate.h
@@ -23,7 +23,8 @@
 }
 
 // Encapsulates the dependencies of NaCl code on chrome/, to avoid a direct
-// dependency on chrome/.
+// dependency on chrome/. All methods should be called on the IO thread unless
+// otherwise noted.
 class NaClBrowserDelegate {
  public:
   virtual ~NaClBrowserDelegate() {}
@@ -63,17 +64,20 @@
   // |use_blocking_api| to true, so calling blocking file API is allowed
   // otherwise non blocking API will be used (which only handles a subset of the
   // urls checking only the url scheme against kExtensionScheme).
-  virtual bool MapUrlToLocalFilePath(const GURL& url,
-                                     bool use_blocking_api,
-                                     const base::FilePath& profile_directory,
-                                     base::FilePath* file_path) = 0;
+  using MapUrlToLocalFilePathCallback = base::RepeatingCallback<
+      bool(const GURL& url, bool use_blocking_api, base::FilePath* file_path)>;
+  // Returns a MapUrlToLocalFilePathCallback that can be called on any thread.
+  // Must be called on the UI thread.
+  virtual MapUrlToLocalFilePathCallback GetMapUrlToLocalFilePathCallback(
+      const base::FilePath& profile_directory) = 0;
   // Set match patterns which will be checked before enabling debug stub.
   virtual void SetDebugPatterns(const std::string& debug_patterns) = 0;
 
   // Returns whether NaCl application with this manifest URL should be debugged.
   virtual bool URLMatchesDebugPatterns(const GURL& manifest_url) = 0;
 
-  // Returns whether Non-SFI mode is allowed for a given manifest URL.
+  // Returns whether Non-SFI mode is allowed for a given manifest URL. Must be
+  // called on the UI thread.
   virtual bool IsNonSfiModeAllowed(const base::FilePath& profile_directory,
                                    const GURL& manifest_url) = 0;
 };
diff --git a/components/nacl/browser/nacl_file_host.cc b/components/nacl/browser/nacl_file_host.cc
index c76d5a9..ff813f5e 100644
--- a/components/nacl/browser/nacl_file_host.cc
+++ b/components/nacl/browser/nacl_file_host.cc
@@ -126,13 +126,11 @@
     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
     const GURL& file_url,
     bool enable_validation_caching,
+    NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback,
     IPC::Message* reply_msg) {
   base::FilePath file_path;
-  if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
-          file_url,
-          true /* use_blocking_api */,
-          nacl_host_message_filter->profile_directory(),
-          &file_path)) {
+  if (!map_url_callback.Run(file_url, true /* use_blocking_api */,
+                            &file_path)) {
     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
     return;
   }
@@ -248,13 +246,18 @@
     return;
   }
 
+  auto map_url_callback =
+      nacl::NaClBrowser::GetDelegate()->GetMapUrlToLocalFilePathCallback(
+          nacl_host_message_filter->profile_directory());
+
   // The URL is part of the current app. Now query the extension system for the
   // file path and convert that to a file descriptor. This should be done on a
   // blocking pool thread.
-  base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
-                 base::BindOnce(&DoOpenNaClExecutableOnThreadPool,
-                                nacl_host_message_filter, file_url,
-                                enable_validation_caching, reply_msg));
+  base::PostTask(
+      FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+      base::BindOnce(&DoOpenNaClExecutableOnThreadPool,
+                     nacl_host_message_filter, file_url,
+                     enable_validation_caching, map_url_callback, reply_msg));
 }
 
 }  // namespace nacl_file_host
diff --git a/components/nacl/browser/nacl_host_message_filter.cc b/components/nacl/browser/nacl_host_message_filter.cc
index fd6f887..9b8823a 100644
--- a/components/nacl/browser/nacl_host_message_filter.cc
+++ b/components/nacl/browser/nacl_host_message_filter.cc
@@ -91,6 +91,15 @@
   pnacl::PnaclHost::GetInstance()->RendererClosing(render_process_id_);
 }
 
+void NaClHostMessageFilter::OverrideThreadForMessage(
+    const IPC::Message& message,
+    content::BrowserThread::ID* thread) {
+#if BUILDFLAG(ENABLE_NACL)
+  if (message.type() == NaClHostMsg_LaunchNaCl::ID)
+    *thread = content::BrowserThread::UI;
+#endif
+}
+
 bool NaClHostMessageFilter::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(NaClHostMessageFilter, message)
@@ -122,26 +131,42 @@
 void NaClHostMessageFilter::OnLaunchNaCl(
     const nacl::NaClLaunchParams& launch_params,
     IPC::Message* reply_msg) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  bool nonsfi_mode_allowed = false;
+#if defined(OS_CHROMEOS) && \
+    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
+  nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed(
+      profile_directory_, GURL(launch_params.manifest_url));
+#endif
+
+  auto map_url_callback =
+      nacl::NaClBrowser::GetDelegate()->GetMapUrlToLocalFilePathCallback(
+          profile_directory_);
+
   // If we're running llc or ld for the PNaCl translator, we don't need to look
   // up permissions, and we don't have the right browser state to look up some
   // of the whitelisting parameters anyway.
   if (launch_params.process_type == kPNaClTranslatorProcessType) {
     uint32_t perms = launch_params.permission_bits & ppapi::PERMISSION_DEV;
-    LaunchNaClContinuationOnIOThread(
-        launch_params,
-        reply_msg,
-        std::vector<NaClResourcePrefetchResult>(),
-        ppapi::PpapiPermissions(perms));
+    base::PostTask(
+        FROM_HERE, {content::BrowserThread::IO},
+        base::BindOnce(&NaClHostMessageFilter::LaunchNaClContinuationOnIOThread,
+                       this, launch_params, reply_msg,
+                       std::vector<NaClResourcePrefetchResult>(),
+                       ppapi::PpapiPermissions(perms), nonsfi_mode_allowed,
+                       map_url_callback));
     return;
   }
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(&NaClHostMessageFilter::LaunchNaClContinuation,
-                                this, launch_params, reply_msg));
+  LaunchNaClContinuation(launch_params, reply_msg, nonsfi_mode_allowed,
+                         map_url_callback);
 }
 
 void NaClHostMessageFilter::LaunchNaClContinuation(
     const nacl::NaClLaunchParams& launch_params,
-    IPC::Message* reply_msg) {
+    IPC::Message* reply_msg,
+    bool nonsfi_mode_allowed,
+    NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   ppapi::PpapiPermissions permissions =
@@ -185,24 +210,25 @@
       {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(&NaClHostMessageFilter::BatchOpenResourceFiles, this,
-                     safe_launch_params, reply_msg, permissions));
+                     safe_launch_params, reply_msg, permissions,
+                     nonsfi_mode_allowed, map_url_callback));
 }
 
 void NaClHostMessageFilter::BatchOpenResourceFiles(
     const nacl::NaClLaunchParams& launch_params,
     IPC::Message* reply_msg,
-    ppapi::PpapiPermissions permissions) {
+    ppapi::PpapiPermissions permissions,
+    bool nonsfi_mode_allowed,
+    NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback) {
   std::vector<NaClResourcePrefetchResult> prefetched_resource_files;
   const std::vector<NaClResourcePrefetchRequest>& request_list =
       launch_params.resource_prefetch_request_list;
   for (size_t i = 0; i < request_list.size(); ++i) {
     GURL gurl(request_list[i].resource_url);
     base::FilePath file_path_metadata;
-    if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
-            gurl,
-            true,  // use_blocking_api
-            profile_directory_,
-            &file_path_metadata)) {
+    if (!map_url_callback.Run(gurl,
+                              true,  // use_blocking_api
+                              &file_path_metadata)) {
       continue;
     }
     base::File file = nacl::OpenNaClReadExecImpl(
@@ -222,14 +248,16 @@
       FROM_HERE, {content::BrowserThread::IO},
       base::BindOnce(&NaClHostMessageFilter::LaunchNaClContinuationOnIOThread,
                      this, launch_params, reply_msg, prefetched_resource_files,
-                     permissions));
+                     permissions, nonsfi_mode_allowed, map_url_callback));
 }
 
 void NaClHostMessageFilter::LaunchNaClContinuationOnIOThread(
     const nacl::NaClLaunchParams& launch_params,
     IPC::Message* reply_msg,
     const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files,
-    ppapi::PpapiPermissions permissions) {
+    ppapi::PpapiPermissions permissions,
+    bool nonsfi_mode_allowed,
+    NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   NaClFileToken nexe_token = {
@@ -241,27 +269,18 @@
       IPC::PlatformFileForTransitToPlatformFile(launch_params.nexe_file);
 
   NaClProcessHost* host = new NaClProcessHost(
-      GURL(launch_params.manifest_url),
-      base::File(nexe_file),
-      nexe_token,
-      prefetched_resource_files,
-      permissions,
-      launch_params.render_view_id,
-      launch_params.permission_bits,
-      launch_params.uses_nonsfi_mode,
-      off_the_record_,
-      launch_params.process_type,
+      GURL(launch_params.manifest_url), base::File(nexe_file), nexe_token,
+      prefetched_resource_files, permissions, launch_params.render_view_id,
+      launch_params.permission_bits, launch_params.uses_nonsfi_mode,
+      nonsfi_mode_allowed, off_the_record_, launch_params.process_type,
       profile_directory_);
   GURL manifest_url(launch_params.manifest_url);
   base::FilePath manifest_path;
   // We're calling MapUrlToLocalFilePath with the non-blocking API
   // because we're running in the I/O thread. Ideally we'd use the other path,
   // which would cover more cases.
-  nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
-      manifest_url,
-      false /* use_blocking_api */,
-      profile_directory_,
-      &manifest_path);
+  map_url_callback.Run(manifest_url, false /* use_blocking_api */,
+                       &manifest_path);
   host->Launch(this, reply_msg, manifest_path);
 }
 
diff --git a/components/nacl/browser/nacl_host_message_filter.h b/components/nacl/browser/nacl_host_message_filter.h
index 474ac45..84d29717 100644
--- a/components/nacl/browser/nacl_host_message_filter.h
+++ b/components/nacl/browser/nacl_host_message_filter.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/nacl/browser/nacl_browser_delegate.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"
 
@@ -35,6 +36,8 @@
   // content::BrowserMessageFilter methods:
   bool OnMessageReceived(const IPC::Message& message) override;
   void OnChannelClosing() override;
+  void OverrideThreadForMessage(const IPC::Message& message,
+                                content::BrowserThread::ID* thread) override;
 
   int render_process_id() { return render_process_id_; }
   bool off_the_record() { return off_the_record_; }
@@ -48,17 +51,24 @@
 
   void OnLaunchNaCl(const NaClLaunchParams& launch_params,
                     IPC::Message* reply_msg);
-  void BatchOpenResourceFiles(const nacl::NaClLaunchParams& launch_params,
-                              IPC::Message* reply_msg,
-                              ppapi::PpapiPermissions permissions);
+  void BatchOpenResourceFiles(
+      const nacl::NaClLaunchParams& launch_params,
+      IPC::Message* reply_msg,
+      ppapi::PpapiPermissions permissions,
+      bool nonsfi_mode_allowed,
+      NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback);
   void LaunchNaClContinuation(
       const nacl::NaClLaunchParams& launch_params,
-      IPC::Message* reply_msg);
+      IPC::Message* reply_msg,
+      bool nonsfi_mode_allowed,
+      NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback);
   void LaunchNaClContinuationOnIOThread(
       const nacl::NaClLaunchParams& launch_params,
       IPC::Message* reply_msg,
       const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files,
-      ppapi::PpapiPermissions permissions);
+      ppapi::PpapiPermissions permissions,
+      bool nonsfi_mode_allowed,
+      NaClBrowserDelegate::MapUrlToLocalFilePathCallback map_url_callback);
   void OnGetReadonlyPnaclFd(const std::string& filename,
                             bool is_executable,
                             IPC::Message* reply_msg);
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc
index 3a124c5..66602a6e0 100644
--- a/components/nacl/browser/nacl_process_host.cc
+++ b/components/nacl/browser/nacl_process_host.cc
@@ -208,6 +208,7 @@
     int render_view_id,
     uint32_t permission_bits,
     bool uses_nonsfi_mode,
+    bool nonsfi_mode_allowed,
     bool off_the_record,
     NaClAppProcessType process_type,
     const base::FilePath& profile_directory)
@@ -224,6 +225,7 @@
       debug_exception_handler_requested_(false),
 #endif
       uses_nonsfi_mode_(uses_nonsfi_mode),
+      nonsfi_mode_allowed_(nonsfi_mode_allowed),
       enable_debug_stub_(false),
       enable_crash_throttling_(false),
       off_the_record_(off_the_record),
@@ -378,18 +380,12 @@
 
   if (uses_nonsfi_mode_) {
     bool nonsfi_mode_forced_by_command_line = false;
-    bool nonsfi_mode_allowed = false;
 #if defined(OS_LINUX)
     nonsfi_mode_forced_by_command_line =
         cmd->HasSwitch(switches::kEnableNaClNonSfiMode);
-#if defined(OS_CHROMEOS) && \
-    (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
-    nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed(
-        nacl_host_message_filter->profile_directory(), manifest_url_);
-#endif
 #endif
     bool nonsfi_mode_enabled =
-        nonsfi_mode_forced_by_command_line || nonsfi_mode_allowed;
+        nonsfi_mode_forced_by_command_line || nonsfi_mode_allowed_;
 
     if (!nonsfi_mode_enabled) {
       SendErrorToRenderer(
diff --git a/components/nacl/browser/nacl_process_host.h b/components/nacl/browser/nacl_process_host.h
index 82d72ec..6db9dfc 100644
--- a/components/nacl/browser/nacl_process_host.h
+++ b/components/nacl/browser/nacl_process_host.h
@@ -81,6 +81,7 @@
       int render_view_id,
       uint32_t permission_bits,
       bool uses_nonsfi_mode,
+      bool nonsfi_mode_allowed,
       bool off_the_record,
       NaClAppProcessType process_type,
       const base::FilePath& profile_directory);
@@ -230,6 +231,7 @@
   std::unique_ptr<content::BrowserChildProcessHost> process_;
 
   bool uses_nonsfi_mode_;
+  bool nonsfi_mode_allowed_;
 
   bool enable_debug_stub_;
   bool enable_crash_throttling_;
diff --git a/components/nacl/browser/test_nacl_browser_delegate.cc b/components/nacl/browser/test_nacl_browser_delegate.cc
index 81e925c1..7c07998 100644
--- a/components/nacl/browser/test_nacl_browser_delegate.cc
+++ b/components/nacl/browser/test_nacl_browser_delegate.cc
@@ -41,12 +41,11 @@
   return NULL;
 }
 
-bool TestNaClBrowserDelegate::MapUrlToLocalFilePath(
-    const GURL& url,
-    bool use_blocking_api,
-    const base::FilePath& profile_directory,
-    base::FilePath* file_path) {
-  return false;
+NaClBrowserDelegate::MapUrlToLocalFilePathCallback
+TestNaClBrowserDelegate::GetMapUrlToLocalFilePathCallback(
+    const base::FilePath& profile_directory) {
+  return base::BindRepeating([](const GURL& url, bool use_blocking_api,
+                                base::FilePath* file_path) { return false; });
 }
 
 void TestNaClBrowserDelegate::SetDebugPatterns(
diff --git a/components/nacl/browser/test_nacl_browser_delegate.h b/components/nacl/browser/test_nacl_browser_delegate.h
index 69dde4e..5ee3d72e 100644
--- a/components/nacl/browser/test_nacl_browser_delegate.h
+++ b/components/nacl/browser/test_nacl_browser_delegate.h
@@ -32,10 +32,8 @@
   std::string GetVersionString() const override;
   ppapi::host::HostFactory* CreatePpapiHostFactory(
       content::BrowserPpapiHost* ppapi_host) override;
-  bool MapUrlToLocalFilePath(const GURL& url,
-                             bool use_blocking_api,
-                             const base::FilePath& profile_directory,
-                             base::FilePath* file_path) override;
+  MapUrlToLocalFilePathCallback GetMapUrlToLocalFilePathCallback(
+      const base::FilePath& profile_directory) override;
   void SetDebugPatterns(const std::string& debug_patterns) override;
   bool URLMatchesDebugPatterns(const GURL& manifest_url) override;
   bool IsNonSfiModeAllowed(const base::FilePath& profile_directory,
diff --git a/components/optimization_guide/BUILD.gn b/components/optimization_guide/BUILD.gn
index a8b92a6d1..6e1e40a7 100644
--- a/components/optimization_guide/BUILD.gn
+++ b/components/optimization_guide/BUILD.gn
@@ -21,8 +21,8 @@
     "hints_fetcher.h",
     "hints_processing_util.cc",
     "hints_processing_util.h",
-    "host_filter.cc",
-    "host_filter.h",
+    "optimization_filter.cc",
+    "optimization_filter.h",
     "optimization_guide_constants.cc",
     "optimization_guide_constants.h",
     "optimization_guide_features.cc",
@@ -80,7 +80,7 @@
     "hints_component_util_unittest.cc",
     "hints_fetcher_unittest.cc",
     "hints_processing_util_unittest.cc",
-    "host_filter_unittest.cc",
+    "optimization_filter_unittest.cc",
     "optimization_guide_features_unittest.cc",
     "optimization_guide_service_unittest.cc",
     "optimization_guide_switches_unittest.cc",
diff --git a/components/optimization_guide/host_filter_unittest.cc b/components/optimization_guide/host_filter_unittest.cc
deleted file mode 100644
index 8c49bd1c..0000000
--- a/components/optimization_guide/host_filter_unittest.cc
+++ /dev/null
@@ -1,64 +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/optimization_guide/host_filter.h"
-
-#include "base/macros.h"
-#include "components/optimization_guide/bloom_filter.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace optimization_guide {
-
-namespace {
-
-std::unique_ptr<BloomFilter> CreateBloomFilter() {
-  std::unique_ptr<BloomFilter> filter = std::make_unique<BloomFilter>(
-      7 /* num_hash_functions */, 8191 /* num_bits */);
-  return filter;
-}
-
-TEST(HostFilterTest, TestContainsHostSuffix) {
-  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
-  bloom_filter->Add("fooco.co.uk");
-  HostFilter host_filter(std::move(bloom_filter));
-  EXPECT_TRUE(
-      host_filter.ContainsHostSuffix(GURL("http://shopping.fooco.co.uk")));
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(
-      GURL("https://shopping.fooco.co.uk/somepath")));
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(GURL("https://fooco.co.uk")));
-  EXPECT_FALSE(host_filter.ContainsHostSuffix(GURL("https://nonfooco.co.uk")));
-}
-
-TEST(HostFilterTest, TestContainsHostSuffixMaxSuffix) {
-  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
-  bloom_filter->Add("one.two.three.four.co.uk");
-  bloom_filter->Add("one.two.three.four.five.co.uk");
-  HostFilter host_filter(std::move(bloom_filter));
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(
-      GURL("http://host.one.two.three.four.co.uk")));
-  EXPECT_FALSE(host_filter.ContainsHostSuffix(
-      GURL("http://host.one.two.three.four.five.co.uk")));
-
-  // Note: full host will match even if more than 5 elements.
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(
-      GURL("http://one.two.three.four.five.co.uk")));
-}
-
-TEST(HostFilterTest, TestContainsHostSuffixMinSuffix) {
-  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
-  bloom_filter->Add("abc.tv");
-  bloom_filter->Add("xy.tv");
-  HostFilter host_filter(std::move(bloom_filter));
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(GURL("https://abc.tv")));
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(GURL("https://host.abc.tv")));
-  EXPECT_FALSE(host_filter.ContainsHostSuffix(GURL("https://host.xy.tv")));
-
-  // Note: full host will match even if less than min size.
-  EXPECT_TRUE(host_filter.ContainsHostSuffix(GURL("https://xy.tv")));
-}
-
-}  // namespace
-
-}  // namespace optimization_guide
diff --git a/components/optimization_guide/host_filter.cc b/components/optimization_guide/optimization_filter.cc
similarity index 84%
rename from components/optimization_guide/host_filter.cc
rename to components/optimization_guide/optimization_filter.cc
index 20636e7..701b5a3 100644
--- a/components/optimization_guide/host_filter.cc
+++ b/components/optimization_guide/optimization_filter.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/optimization_guide/host_filter.h"
-
 #include <string>
 
+#include "components/optimization_guide/optimization_filter.h"
+
 namespace optimization_guide {
 
 // Maximum number of suffixes to check per url.
@@ -14,18 +14,19 @@
 // Realistic minimum length of a host suffix.
 const int kMinHostSuffix = 6;  // eg., abc.tv
 
-HostFilter::HostFilter(std::unique_ptr<BloomFilter> bloom_filter)
+OptimizationFilter::OptimizationFilter(
+    std::unique_ptr<BloomFilter> bloom_filter)
     : bloom_filter_(std::move(bloom_filter)) {
   // May be created on one thread but used on another. The first call to
   // CalledOnValidSequence() will re-bind it.
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
-HostFilter::~HostFilter() {
+OptimizationFilter::~OptimizationFilter() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-bool HostFilter::ContainsHostSuffix(const GURL& url) const {
+bool OptimizationFilter::ContainsHostSuffix(const GURL& url) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // First check full host name.
diff --git a/components/optimization_guide/host_filter.h b/components/optimization_guide/optimization_filter.h
similarity index 66%
rename from components/optimization_guide/host_filter.h
rename to components/optimization_guide/optimization_filter.h
index b74da202..aa0bd858 100644
--- a/components/optimization_guide/host_filter.h
+++ b/components/optimization_guide/optimization_filter.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_OPTIMIZATION_GUIDE_HOST_FILTER_H_
-#define COMPONENTS_OPTIMIZATION_GUIDE_HOST_FILTER_H_
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_FILTER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_FILTER_H_
 
 #include "base/macros.h"
 #include "base/sequence_checker.h"
@@ -12,16 +12,18 @@
 
 namespace optimization_guide {
 
-// HostFilter is a simple Host filter for keeping track of a set of strings
-// that are represented by a Bloom filter.
+// OptimizationFilter is a simple filter for keeping track of a set of strings
+// that are represented by a Bloom filter. This class has a 1:1 mapping with an
+// OptimizationFilter protobuf message where this is the logical implementation
+// of the proto data.
 //
 // TODO(dougarnett): consider moving this and BloomFilter under
 // components/blacklist/.
-class HostFilter {
+class OptimizationFilter {
  public:
-  explicit HostFilter(std::unique_ptr<BloomFilter> bloom_filter);
+  explicit OptimizationFilter(std::unique_ptr<BloomFilter> bloom_filter);
 
-  virtual ~HostFilter();
+  virtual ~OptimizationFilter();
 
   // Returns whether this filter contains a host suffix for the host part
   // of |url|. It will check at most 5 host suffixes and it may ignore simple
@@ -43,9 +45,9 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(HostFilter);
+  DISALLOW_COPY_AND_ASSIGN(OptimizationFilter);
 };
 
 }  // namespace optimization_guide
 
-#endif  // COMPONENTS_OPTIMIZATION_GUIDE_HOST_FILTER_H_
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_FILTER_H_
diff --git a/components/optimization_guide/optimization_filter_unittest.cc b/components/optimization_guide/optimization_filter_unittest.cc
new file mode 100644
index 0000000..79a01442
--- /dev/null
+++ b/components/optimization_guide/optimization_filter_unittest.cc
@@ -0,0 +1,63 @@
+// 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/optimization_guide/optimization_filter.h"
+#include "base/macros.h"
+#include "components/optimization_guide/bloom_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace optimization_guide {
+
+namespace {
+
+std::unique_ptr<BloomFilter> CreateBloomFilter() {
+  std::unique_ptr<BloomFilter> filter = std::make_unique<BloomFilter>(
+      7 /* num_hash_functions */, 8191 /* num_bits */);
+  return filter;
+}
+
+TEST(OptimizationFilterTest, TestContainsHostSuffix) {
+  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
+  bloom_filter->Add("fooco.co.uk");
+  OptimizationFilter opt_filter(std::move(bloom_filter));
+  EXPECT_TRUE(
+      opt_filter.ContainsHostSuffix(GURL("http://shopping.fooco.co.uk")));
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(
+      GURL("https://shopping.fooco.co.uk/somepath")));
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(GURL("https://fooco.co.uk")));
+  EXPECT_FALSE(opt_filter.ContainsHostSuffix(GURL("https://nonfooco.co.uk")));
+}
+
+TEST(OptimizationFilterTest, TestContainsHostSuffixMaxSuffix) {
+  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
+  bloom_filter->Add("one.two.three.four.co.uk");
+  bloom_filter->Add("one.two.three.four.five.co.uk");
+  OptimizationFilter opt_filter(std::move(bloom_filter));
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(
+      GURL("http://host.one.two.three.four.co.uk")));
+  EXPECT_FALSE(opt_filter.ContainsHostSuffix(
+      GURL("http://host.one.two.three.four.five.co.uk")));
+
+  // Note: full host will match even if more than 5 elements.
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(
+      GURL("http://one.two.three.four.five.co.uk")));
+}
+
+TEST(OptimizationFilterTest, TestContainsHostSuffixMinSuffix) {
+  std::unique_ptr<BloomFilter> bloom_filter(CreateBloomFilter());
+  bloom_filter->Add("abc.tv");
+  bloom_filter->Add("xy.tv");
+  OptimizationFilter opt_filter(std::move(bloom_filter));
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(GURL("https://abc.tv")));
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(GURL("https://host.abc.tv")));
+  EXPECT_FALSE(opt_filter.ContainsHostSuffix(GURL("https://host.xy.tv")));
+
+  // Note: full host will match even if less than min size.
+  EXPECT_TRUE(opt_filter.ContainsHostSuffix(GURL("https://xy.tv")));
+}
+
+}  // namespace
+
+}  // namespace optimization_guide
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 532169e3..528271f 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -45,6 +45,9 @@
   <message name="IDS_PAGE_INFO_SAFETY_TIP_LEAVE_BUTTON" desc="Text of button to leave a suspicious page shown on the safety tip page info bubble.">
     Leave this site
   </message>
+  <message name="IDS_PAGE_INFO_SAFETY_TIP_SUMMARY" desc="Message to display in the page info bubble when the page you are on triggered a safety tip.">
+    Suspicious site
+  </message>
 
   <!-- Viewing file strings -->
   <message name="IDS_PAGE_INFO_FILE_PAGE" desc="Message to display in the page info bubble when the page the user has navigated to is a file:// page">
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1
new file mode 100644
index 0000000..f5f4107
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1
@@ -0,0 +1 @@
+a529530d0ddcd81921d7f17316a5ddb453b3aef6
\ No newline at end of file
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 3dc120a0..72ad166d 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -94,7 +94,7 @@
     return;
 
   doc_info_ = doc_info;
-  doc_node_ = CreateNode(ax::mojom::Role::kGroup);
+  doc_node_ = CreateNode(ax::mojom::Role::kDocument);
 
   // Because all of the coordinates are expressed relative to the
   // doc's coordinates, the origin of the doc must be (0, 0). Its
@@ -119,6 +119,8 @@
   page_node->AddStringAttribute(
       ax::mojom::StringAttribute::kName,
       l10n_util::GetPluralStringFUTF8(IDS_PDF_PAGE_INDEX, page_index + 1));
+  page_node->AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
+                              true);
 
   gfx::RectF page_bounds = ToRectF(page_info.bounds);
   page_node->relative_bounds.bounds = page_bounds;
@@ -479,7 +481,7 @@
 }
 
 bool PdfAccessibilityTree::IsIgnored(const ui::AXNode* node) const {
-  return node->data().HasState(ax::mojom::State::kIgnored);
+  return node->IsIgnored();
 }
 
 bool PdfAccessibilityTree::IsValid(const ui::AXNode* node) const {
diff --git a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
index e262962..7cc80a7 100644
--- a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
@@ -131,7 +131,7 @@
   pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
                                                   chars_);
 
-  EXPECT_EQ(ax::mojom::Role::kGroup,
+  EXPECT_EQ(ax::mojom::Role::kDocument,
             pdf_accessibility_tree.GetRoot()->data().role);
 }
 
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index cf05ae4..9c6648d 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -409,6 +409,7 @@
     "//components/account_id",
     "//components/policy:generated",
     "//components/prefs:test_support",
+    "//components/version_info:version_info",
     "//extensions/buildflags",
     "//google_apis",
     "//net:test_support",
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 1460e6f..31d8df2a 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -555,7 +555,7 @@
   request_jobs_.push_back(service_->CreateJob(std::move(config)));
 }
 
-void CloudPolicyClient::UploadRealtimeReport(base::Value event,
+void CloudPolicyClient::UploadRealtimeReport(base::Value report,
                                              const StatusCallback& callback) {
   CHECK(is_registered());
   std::unique_ptr<RealtimeReportingJobConfiguration> config =
@@ -565,7 +565,7 @@
               &CloudPolicyClient::OnRealtimeReportUploadCompleted,
               weak_ptr_factory_.GetWeakPtr(), callback));
 
-  config->AddEvent(std::move(event));
+  config->AddReport(std::move(report));
 
   request_jobs_.push_back(service_->CreateJob(std::move(config)));
 }
diff --git a/components/policy/core/common/cloud/cloud_policy_client.h b/components/policy/core/common/cloud/cloud_policy_client.h
index ebefbc9..a77d9dfc 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.h
+++ b/components/policy/core/common/cloud/cloud_policy_client.h
@@ -262,10 +262,10 @@
           chrome_desktop_report,
       const StatusCallback& callback);
 
-  // Uploads |event| using the real-time reporting API.  As above, the client
+  // Uploads |report| using the real-time reporting API.  As above, the client
   // must be in a registered state.  The |callback| will be called when the
   // operation completes.
-  virtual void UploadRealtimeReport(base::Value event,
+  virtual void UploadRealtimeReport(base::Value report,
                                     const StatusCallback& callback);
 
   // Uploads a report on the status of app push-installs. The client must be in
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index 3b7fa0b7..7819492 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
+#include "base/json/json_reader.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
@@ -25,7 +26,9 @@
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
 #include "components/policy/core/common/cloud/mock_signing_service.h"
+#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "components/version_info/version_info.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -464,6 +467,7 @@
     EXPECT_CALL(service_, StartJob(_))
         .WillOnce(DoAll(service_.CaptureJobType(&job_type_),
                         service_.CaptureQueryParams(&query_params_),
+                        service_.CapturePayload(&job_payload_),
                         service_.StartJobAsync(
                             net::OK, DeviceManagementService::kSuccess, "{}")));
   }
@@ -586,6 +590,7 @@
   DeviceManagementService::JobConfiguration::JobType job_type_;
   DeviceManagementService::JobConfiguration::ParameterMap query_params_;
   em::DeviceManagementRequest job_request_;
+  std::string job_payload_;
   std::string client_id_;
   std::string policy_type_;
   MockDeviceManagementService service_;
@@ -1419,6 +1424,8 @@
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 }
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || \
+    defined(OS_LINUX) && !defined(OS_CHROMEOS)
 TEST_F(CloudPolicyClientTest, UploadRealtimeReport) {
   Register();
 
@@ -1427,18 +1434,47 @@
   CloudPolicyClient::StatusCallback callback =
       base::Bind(&MockStatusCallbackObserver::OnCallbackComplete,
                  base::Unretained(&callback_observer_));
-  base::Value event(base::Value::Type::DICTIONARY);
-  event.SetStringKey("eventId", "123");
-  event.SetStringKey("time", "2019-05-22T13:01:45Z");
-  event.SetStringPath("event.foo", "bar");
+  base::Value report(base::Value::Type::DICTIONARY);
+  report.SetStringPath("context.gaiaEmail", "name@gmail.com");
+  report.SetStringPath("context.userAgent", "User-Agent");
+  report.SetStringPath("context.profileName", "Profile 1");
+  report.SetStringPath("context.profilePath", "C:\\User Data\\Profile 1");
+  report.SetStringPath("event.time", "2019-05-22T13:01:45Z");
+  report.SetStringPath("event.foo.prop1", "value1");
+  report.SetStringPath("event.foo.prop2", "value2");
+  report.SetStringPath("event.foo.prop3", "value3");
 
-  client_->UploadRealtimeReport(std::move(event), callback);
+  client_->UploadRealtimeReport(std::move(report), callback);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(
       DeviceManagementService::JobConfiguration::TYPE_UPLOAD_REAL_TIME_REPORT,
       job_type_);
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+
+  base::Optional<base::Value> payload = base::JSONReader::Read(job_payload_);
+  ASSERT_TRUE(payload);
+
+  EXPECT_EQ(kDMToken, *payload->FindStringPath(
+                          RealtimeReportingJobConfiguration::kDmTokenKey));
+  EXPECT_EQ(client_id_, *payload->FindStringPath(
+                            RealtimeReportingJobConfiguration::kClientIdKey));
+  EXPECT_EQ(policy::GetOSUsername(),
+            *payload->FindStringPath(
+                RealtimeReportingJobConfiguration::kMachineUserKey));
+  EXPECT_EQ(version_info::GetVersionNumber(),
+            *payload->FindStringPath(
+                RealtimeReportingJobConfiguration::kChromeVersionKey));
+  EXPECT_EQ(policy::GetOSVersion(),
+            *payload->FindStringPath(
+                RealtimeReportingJobConfiguration::kOsVersionKey));
+
+  base::Value* events =
+      payload->FindPath(RealtimeReportingJobConfiguration::kEventsKey);
+  EXPECT_EQ(base::Value::Type::LIST, events->type());
+  const base::Value::ListStorage& list = events->GetList();
+  EXPECT_EQ(1u, list.size());
 }
+#endif
 
 TEST_F(CloudPolicyClientTest, MultipleActiveRequests) {
   Register();
diff --git a/components/policy/core/common/cloud/mock_device_management_service.cc b/components/policy/core/common/cloud/mock_device_management_service.cc
index 8d82273..06f34bb 100644
--- a/components/policy/core/common/cloud/mock_device_management_service.cc
+++ b/components/policy/core/common/cloud/mock_device_management_service.cc
@@ -76,6 +76,10 @@
   CHECK(request->ParseFromString(payload));
 }
 
+ACTION_P(CreateCapturePayloadAction, payload) {
+  *payload = arg0->GetConfiguration()->GetPayload();
+}
+
 MockDeviceManagementServiceConfiguration::
     MockDeviceManagementServiceConfiguration()
     : server_url_(kServerUrl) {}
@@ -180,6 +184,11 @@
   return CreateCaptureRequestAction(request);
 }
 
+testing::Action<MockDeviceManagementService::StartJobFunction>
+MockDeviceManagementService::CapturePayload(std::string* payload) {
+  return CreateCapturePayloadAction(payload);
+}
+
 // Call after using StartJobFullControl() to respond to the network request.
 void MockDeviceManagementService::DoURLCompletion(
     JobControl** job,
diff --git a/components/policy/core/common/cloud/mock_device_management_service.h b/components/policy/core/common/cloud/mock_device_management_service.h
index 0ff32159..37ee882 100644
--- a/components/policy/core/common/cloud/mock_device_management_service.h
+++ b/components/policy/core/common/cloud/mock_device_management_service.h
@@ -111,6 +111,11 @@
   testing::Action<StartJobFunction> CaptureRequest(
       enterprise_management::DeviceManagementRequest* request);
 
+  // Can be used as an action when mocking the StartJob method.
+  // Makes a copy of the payload of the JobConfiguration
+  // of the Job passed to StartJob.
+  testing::Action<StartJobFunction> CapturePayload(std::string* payload);
+
   // Call after using StartJobFullControl() to respond to the network request.
   // If the job completed successfully, |*job| will be nulled to prevent callers
   // from using the pointer beyond its lifetime.  If the job was retried, then
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc b/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
index 84455537..8de14966 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -6,8 +6,12 @@
 
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/optional.h"
+#include "base/path_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
+#include "components/policy/core/common/cloud/cloud_policy_util.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
+#include "components/version_info/version_info.h"
 #include "google_apis/google_api_keys.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -15,9 +19,21 @@
 
 namespace policy {
 
-const char RealtimeReportingJobConfiguration::kDmTokenKey[] = "dmToken";
-const char RealtimeReportingJobConfiguration::kClientIdKey[] = "clientId";
+const char RealtimeReportingJobConfiguration::kContextKey[] = "context";
+const char RealtimeReportingJobConfiguration::kEventKey[] = "event";
+
+const char RealtimeReportingJobConfiguration::kBrowserIdKey[] =
+    "browser.browserId";
+const char RealtimeReportingJobConfiguration::kChromeVersionKey[] =
+    "browser.chromeVersion";
+const char RealtimeReportingJobConfiguration::kClientIdKey[] =
+    "device.clientId";
+const char RealtimeReportingJobConfiguration::kDmTokenKey[] = "device.dmToken";
 const char RealtimeReportingJobConfiguration::kEventsKey[] = "events";
+const char RealtimeReportingJobConfiguration::kMachineUserKey[] =
+    "browser.machineUser";
+const char RealtimeReportingJobConfiguration::kOsVersionKey[] =
+    "device.osVersion";
 
 RealtimeReportingJobConfiguration::RealtimeReportingJobConfiguration(
     CloudPolicyClient* client,
@@ -33,17 +49,46 @@
   DCHECK(GetAuth().has_dm_token());
 
   AddParameter("key", google_apis::GetAPIKey());
-
-  payload_.SetStringKey(kDmTokenKey, GetAuth().dm_token());
-  payload_.SetStringKey(kClientIdKey, client->client_id());
-  payload_.SetKey(kEventsKey, base::Value(base::Value::Type::LIST));
+  InitializePayload(client);
 }
 
 RealtimeReportingJobConfiguration::~RealtimeReportingJobConfiguration() {}
 
-void RealtimeReportingJobConfiguration::AddEvent(base::Value event) {
+bool RealtimeReportingJobConfiguration::AddReport(base::Value report) {
+  if (!report.is_dict())
+    return false;
+
+  base::Optional<base::Value> context = report.ExtractKey(kContextKey);
+  base::Optional<base::Value> event = report.ExtractKey(kEventKey);
+  if (!context || !event)
+    return false;
+
+  // Move context keys to the payload.  It is possible to add multiple reports
+  // to the payload in which case the context values are the same.  Only add
+  // any new values not already present.
+  for (const auto& item : context->DictItems()) {
+    if (!payload_.FindKey(item.first))
+      payload_.SetKey(item.first, item.second.Clone());
+  }
+
+  // Append event to the list.
   base::Value::ListStorage& list = payload_.FindListKey(kEventsKey)->GetList();
-  list.push_back(std::move(event));
+  list.push_back(std::move(*event));
+  return true;
+}
+
+void RealtimeReportingJobConfiguration::InitializePayload(
+    CloudPolicyClient* client) {
+  base::FilePath browser_id;
+  if (base::PathService::Get(base::DIR_EXE, &browser_id))
+    payload_.SetStringPath(kBrowserIdKey, browser_id.value());
+
+  payload_.SetStringPath(kDmTokenKey, GetAuth().dm_token());
+  payload_.SetStringPath(kClientIdKey, client->client_id());
+  payload_.SetStringPath(kMachineUserKey, GetOSUsername());
+  payload_.SetStringPath(kChromeVersionKey, version_info::GetVersionNumber());
+  payload_.SetStringPath(kOsVersionKey, GetOSVersion());
+  payload_.SetPath(kEventsKey, base::Value(base::Value::Type::LIST));
 }
 
 std::string RealtimeReportingJobConfiguration::GetPayload() {
diff --git a/components/policy/core/common/cloud/realtime_reporting_job_configuration.h b/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
index 68c0675f..26b06ad 100644
--- a/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
+++ b/components/policy/core/common/cloud/realtime_reporting_job_configuration.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -22,6 +22,19 @@
 class POLICY_EXPORT RealtimeReportingJobConfiguration
     : public JobConfigurationBase {
  public:
+  // Keys used in report dictionary.
+  static const char kContextKey[];
+  static const char kEventKey[];
+
+  // Keys used in request payload dictionary.  Public for testing.
+  static const char kBrowserIdKey[];
+  static const char kChromeVersionKey[];
+  static const char kClientIdKey[];
+  static const char kDmTokenKey[];
+  static const char kEventsKey[];
+  static const char kMachineUserKey[];
+  static const char kOsVersionKey[];
+
   typedef base::OnceCallback<void(DeviceManagementService::Job* job,
                                   DeviceManagementStatus code,
                                   int net_error,
@@ -34,16 +47,21 @@
 
   ~RealtimeReportingJobConfiguration() override;
 
-  // Add a new event to the payload.  This methods takes ownership of the event
-  // value.  Events are dictionaries defined by the Event message described at
+  // Add a new report to the payload.  A report is a dictionary that contains
+  // two keys: "event" and "context".  The first key is a dictionary defined by
+  // the Event message described at
   // google/internal/chrome/reporting/v1/chromereporting.proto.
-  void AddEvent(base::Value event);
+  //
+  // The second is context information about this instance of chrome that
+  // is not specific to the event.
+  //
+  // Returns true if the report was added successfully.
+  bool AddReport(base::Value report);
 
  private:
-  // Keys used in request payload dictionary.
-  static const char kDmTokenKey[];
-  static const char kClientIdKey[];
-  static const char kEventsKey[];
+  // Does one time initialization of the payload when the configuration is
+  // created.
+  void InitializePayload(CloudPolicyClient* client);
 
   // DeviceManagementService::JobConfiguration.
   std::string GetPayload() override;
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index adfe31e..7ea0939e 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -266,7 +266,7 @@
               bloom_filter_proto.num_hash_functions(),
               bloom_filter_proto.num_bits(), bloom_filter_proto.data());
       lite_page_redirect_blacklist_ =
-          std::make_unique<optimization_guide::HostFilter>(
+          std::make_unique<optimization_guide::OptimizationFilter>(
               std::move(bloom_filter));
       RecordOptimizationFilterStatus(
           blacklist.optimization_type(),
diff --git a/components/previews/content/previews_hints.h b/components/previews/content/previews_hints.h
index aa01f64a..fcf7bf8 100644
--- a/components/previews/content/previews_hints.h
+++ b/components/previews/content/previews_hints.h
@@ -13,7 +13,7 @@
 #include "base/sequence_checker.h"
 #include "components/optimization_guide/hint_cache.h"
 #include "components/optimization_guide/hints_processing_util.h"
-#include "components/optimization_guide/host_filter.h"
+#include "components/optimization_guide/optimization_filter.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/previews/content/previews_user_data.h"
 #include "net/nqe/effective_connection_type.h"
@@ -114,7 +114,8 @@
   std::unique_ptr<optimization_guide::HintUpdateData> component_update_data_;
 
   // Blacklist of host suffixes for LITE_PAGE_REDIRECT Previews.
-  std::unique_ptr<optimization_guide::HostFilter> lite_page_redirect_blacklist_;
+  std::unique_ptr<optimization_guide::OptimizationFilter>
+      lite_page_redirect_blacklist_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc
index 06bce379..2c02021 100644
--- a/components/previews/content/previews_hints_unittest.cc
+++ b/components/previews/content/previews_hints_unittest.cc
@@ -20,7 +20,6 @@
 #include "components/optimization_guide/hint_update_data.h"
 #include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/hints_component_util.h"
-#include "components/optimization_guide/host_filter.h"
 #include "components/optimization_guide/optimization_guide_features.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/optimization_guide/proto_database_provider_test_base.h"
@@ -61,19 +60,6 @@
 
 }  // namespace
 
-class TestHostFilter : public optimization_guide::HostFilter {
- public:
-  explicit TestHostFilter(std::string single_host_match)
-      : HostFilter(nullptr), single_host_match_(single_host_match) {}
-
-  bool ContainsHostSuffix(const GURL& url) const override {
-    return single_host_match_ == url.host();
-  }
-
- private:
-  std::string single_host_match_;
-};
-
 class PreviewsHintsTest
     : public optimization_guide::ProtoDatabaseProviderTestBase {
  public:
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index aa9fb9f..d05fdb1 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -93,6 +93,21 @@
   security_style_explanations->insecure_explanations.push_back(explanation);
 }
 
+void ExplainSafetyTips(
+    const security_state::VisibleSecurityState& visible_security_state,
+    content::SecurityStyleExplanations* security_style_explanations) {
+  DCHECK_NE(visible_security_state.safety_tip_status, SAFETY_TIP_STATUS_NONE);
+
+  security_style_explanations->summary =
+      l10n_util::GetStringUTF8(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY);
+  // Add a bullet describing the issue.
+  content::SecurityStyleExplanation explanation(
+      l10n_util::GetStringUTF8(IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE),
+      l10n_util::GetStringUTF8(
+          IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION));
+  security_style_explanations->insecure_explanations.push_back(explanation);
+}
+
 void ExplainCertificateSecurity(
     const security_state::VisibleSecurityState& visible_security_state,
     content::SecurityStyleExplanations* security_style_explanations) {
@@ -434,6 +449,9 @@
     // certificate, connection, or content that needs to be explained, e.g. in
     // the case of a net error, so we can early return.
     return security_style;
+  } else if (visible_security_state.safety_tip_status !=
+             security_state::SAFETY_TIP_STATUS_NONE) {
+    ExplainSafetyTips(visible_security_state, security_style_explanations);
   } else {
     ExplainHTTPSecurity(security_level, visible_security_state,
                         security_style_explanations);
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index b26d575..8abc28b 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -194,6 +194,7 @@
 
 VisibleSecurityState::VisibleSecurityState()
     : malicious_content_status(MALICIOUS_CONTENT_STATUS_NONE),
+      safety_tip_status(SAFETY_TIP_STATUS_NONE),
       connection_info_initialized(false),
       cert_status(0),
       connection_status(0),
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 955724e..2d95607c 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -96,6 +96,13 @@
   MALICIOUS_CONTENT_STATUS_BILLING,
 };
 
+// Describes whether the page triggers any safety tips or reputation
+// warnings.
+enum SafetyTipStatus {
+  SAFETY_TIP_STATUS_NONE,
+  SAFETY_TIP_STATUS_BAD_REPUTATION,
+};
+
 // Contains the security state relevant to computing the SecurityLevel
 // for a page. This is the input to GetSecurityLevel().
 struct VisibleSecurityState {
@@ -105,6 +112,8 @@
 
   MaliciousContentStatus malicious_content_status;
 
+  SafetyTipStatus safety_tip_status;
+
   // CONNECTION SECURITY FIELDS
   // Whether the connection security fields are initialized.
   bool connection_info_initialized;
diff --git a/components/sessions/BUILD.gn b/components/sessions/BUILD.gn
index 3c722ba..652e153 100644
--- a/components/sessions/BUILD.gn
+++ b/components/sessions/BUILD.gn
@@ -111,6 +111,7 @@
 
   public_deps = [
     "//components/keyed_service/core",
+    "//skia",
   ]
 
   deps = [
diff --git a/components/sessions/core/DEPS b/components/sessions/core/DEPS
index f2ecd68b..6c0a29a 100644
--- a/components/sessions/core/DEPS
+++ b/components/sessions/core/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
   "+components/keyed_service/core",
   "+components/prefs",
+
+  # SkColor is referenced in a struct in session_types.h
+  "+third_party/skia/include/core/SkColor.h",
 ]
diff --git a/components/sessions/core/session_command.cc b/components/sessions/core/session_command.cc
index 0f38cf7..7062332 100644
--- a/components/sessions/core/session_command.cc
+++ b/components/sessions/core/session_command.cc
@@ -28,8 +28,8 @@
   return true;
 }
 
-base::Pickle* SessionCommand::PayloadAsPickle() const {
-  return new base::Pickle(contents(), static_cast<int>(size()));
+std::unique_ptr<base::Pickle> SessionCommand::PayloadAsPickle() const {
+  return std::make_unique<base::Pickle>(contents(), static_cast<int>(size()));
 }
 
 }  // namespace sessions
diff --git a/components/sessions/core/session_command.h b/components/sessions/core/session_command.h
index 54d19e4..e5f7663 100644
--- a/components/sessions/core/session_command.h
+++ b/components/sessions/core/session_command.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
@@ -60,11 +61,10 @@
   // count is not equal to the size of data this command contains.
   bool GetPayload(void* dest, size_t count) const;
 
-  // Returns the contents as a pickle. It is up to the caller to delete the
-  // returned Pickle. The returned Pickle references the underlying data of
-  // this SessionCommand. If you need it to outlive the command, copy the
-  // pickle.
-  base::Pickle* PayloadAsPickle() const;
+  // Returns the contents as a pickle. The returned Pickle references the
+  // underlying data of this SessionCommand. If you need it to outlive the
+  // command, copy the pickle.
+  std::unique_ptr<base::Pickle> PayloadAsPickle() const;
 
  private:
   const id_type id_;
diff --git a/components/sessions/core/session_service_commands.cc b/components/sessions/core/session_service_commands.cc
index 99a3301..e232d5c6e 100644
--- a/components/sessions/core/session_service_commands.cc
+++ b/components/sessions/core/session_service_commands.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/flat_set.h"
 #include "base/memory/ptr_util.h"
 #include "base/pickle.h"
 #include "base/token.h"
@@ -58,6 +59,7 @@
 static const SessionCommand::id_type kCommandSetWindowWorkspace2 = 23;
 static const SessionCommand::id_type kCommandTabNavigationPathPruned = 24;
 static const SessionCommand::id_type kCommandSetTabGroup = 25;
+static const SessionCommand::id_type kCommandSetTabGroupMetadata = 26;
 
 namespace {
 
@@ -112,7 +114,7 @@
   int32_t count;
 };
 
-struct SerializedTabGroupId {
+struct SerializedToken {
   // These fields correspond to the high and low fields of |base::Token|.
   uint64_t id_high;
   uint64_t id_low;
@@ -120,7 +122,7 @@
 
 struct TabGroupPayload {
   SessionID::id_type tab_id;
-  SerializedTabGroupId maybe_group;
+  SerializedToken maybe_group;
   bool has_group;
 };
 
@@ -148,9 +150,6 @@
   PERSISTED_SHOW_STATE_END = 8,
 };
 
-using IdToSessionTab = std::map<SessionID, std::unique_ptr<SessionTab>>;
-using IdToSessionWindow = std::map<SessionID, std::unique_ptr<SessionWindow>>;
-
 // Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState
 // is changed.
 static_assert(ui::SHOW_STATE_END ==
@@ -217,6 +216,11 @@
   }
 }
 
+using IdToSessionTab = std::map<SessionID, std::unique_ptr<SessionTab>>;
+using IdToSessionWindow = std::map<SessionID, std::unique_ptr<SessionWindow>>;
+using TokenToSessionTabGroup =
+    std::map<base::Token, std::unique_ptr<SessionTabGroup>>;
+
 // Returns the window in windows with the specified id. If a window does
 // not exist, one is created.
 SessionWindow* GetWindow(SessionID window_id, IdToSessionWindow* windows) {
@@ -244,6 +248,17 @@
   return i->second.get();
 }
 
+SessionTabGroup* GetTabGroup(base::Token group_id,
+                             TokenToSessionTabGroup* groups) {
+  DCHECK(groups);
+  // For |group_id|, insert a corresponding group entry or get the existing one.
+  auto result = groups->emplace(group_id, nullptr);
+  TokenToSessionTabGroup::iterator it = result.first;
+  if (result.second)
+    it->second = std::make_unique<SessionTabGroup>(group_id);
+  return it->second.get();
+}
+
 // Returns an iterator into navigations pointing to the navigation whose
 // index matches |index|. If no navigation index matches |index|, the first
 // navigation with an index > |index| is returned.
@@ -312,9 +327,13 @@
 
 // Adds tabs to their parent window based on the tab's window_id. This
 // ignores tabs with no navigations.
-void AddTabsToWindows(IdToSessionTab* tabs, IdToSessionWindow* windows) {
+void AddTabsToWindows(IdToSessionTab* tabs,
+                      TokenToSessionTabGroup* tab_groups,
+                      IdToSessionWindow* windows) {
   DVLOG(1) << "AddTabsToWindows";
-  DVLOG(1) << "Tabs " << tabs->size() << ", windows " << windows->size();
+  DVLOG(1) << "Tabs " << tabs->size() << ", groups " << tab_groups->size()
+           << ", windows " << windows->size();
+
   for (auto& tab_pair : *tabs) {
     std::unique_ptr<SessionTab> tab = std::move(tab_pair.second);
     if (!tab->window_id.id() || tab->navigations.empty())
@@ -339,6 +358,35 @@
   // There are no more pointers left in |tabs|, just empty husks from the
   // move, so clear it out.
   tabs->clear();
+
+  // For each window, collect all the tab groups present. We rely on the fact
+  // that tab groups can't be split between windows.
+  for (auto& window_pair : *windows) {
+    SessionWindow* window = window_pair.second.get();
+
+    base::flat_set<base::Token> groups_in_current_window;
+    for (const auto& tab : window->tabs) {
+      if (tab->group.has_value())
+        groups_in_current_window.insert(tab->group.value());
+    }
+
+    // Move corresponding SessionTabGroup entries into SessionWindow.
+    for (const base::Token& group_id : groups_in_current_window) {
+      auto it = tab_groups->find(group_id);
+      if (it == tab_groups->end()) {
+        window->tab_groups.push_back(
+            std::make_unique<SessionTabGroup>(group_id));
+        continue;
+      }
+      window->tab_groups.push_back(std::move(it->second));
+      tab_groups->erase(it);
+    }
+  }
+
+  // We may have extraneous tab group entries. Since we don't have explicit
+  // commands for opening and closing tab groups, there may be dangling
+  // SessionTabGroup entries after all tabs in a group are closed.
+  tab_groups->clear();
 }
 
 void ProcessTabNavigationPathPrunedCommand(
@@ -376,6 +424,7 @@
 bool CreateTabsAndWindows(
     const std::vector<std::unique_ptr<SessionCommand>>& data,
     IdToSessionTab* tabs,
+    TokenToSessionTabGroup* tab_groups,
     IdToSessionWindow* windows,
     SessionID* active_window_id) {
   // If the file is corrupt (command with wrong size, or unknown command), we
@@ -573,6 +622,24 @@
         break;
       }
 
+      case kCommandSetTabGroupMetadata: {
+        std::unique_ptr<base::Pickle> pickle = command->PayloadAsPickle();
+        base::PickleIterator iter(*pickle);
+
+        base::Optional<base::Token> group_id = ReadTokenFromPickle(&iter);
+        if (!group_id.has_value())
+          return true;
+
+        SessionTabGroup* group = GetTabGroup(group_id.value(), tab_groups);
+
+        if (!iter.ReadString16(&group->title))
+          return true;
+
+        if (!iter.ReadUInt32(&group->color))
+          return true;
+        break;
+      }
+
       case kCommandSetPinnedState: {
         PinnedStatePayload payload;
         if (!command->GetPayload(&payload, sizeof(payload))) {
@@ -788,6 +855,17 @@
   return CreateSessionCommandForPayload(kCommandSetTabGroup, payload);
 }
 
+std::unique_ptr<SessionCommand> CreateTabGroupMetadataUpdateCommand(
+    const base::Token& group,
+    const base::string16& title,
+    SkColor color) {
+  base::Pickle pickle;
+  WriteTokenToPickle(&pickle, group);
+  pickle.WriteString16(title);
+  pickle.WriteUInt32(color);
+  return std::make_unique<SessionCommand>(kCommandSetTabGroupMetadata, pickle);
+}
+
 std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
     const SessionID& tab_id,
     bool is_pinned) {
@@ -939,16 +1017,19 @@
     std::vector<std::unique_ptr<SessionWindow>>* valid_windows,
     SessionID* active_window_id) {
   IdToSessionTab tabs;
+  TokenToSessionTabGroup tab_groups;
   IdToSessionWindow windows;
 
   DVLOG(1) << "RestoreSessionFromCommands " << commands.size();
-  if (CreateTabsAndWindows(commands, &tabs, &windows, active_window_id)) {
-    AddTabsToWindows(&tabs, &windows);
+  if (CreateTabsAndWindows(commands, &tabs, &tab_groups, &windows,
+                           active_window_id)) {
+    AddTabsToWindows(&tabs, &tab_groups, &windows);
     SortTabsBasedOnVisualOrderAndClear(&windows, valid_windows);
     UpdateSelectedTabIndex(valid_windows);
   }
-  // AddTabsToWindows should have processed all the tabs.
+  // AddTabsToWindows should have processed all the tabs and groups.
   DCHECK_EQ(0u, tabs.size());
+  DCHECK_EQ(0u, tab_groups.size());
   // SortTabsBasedOnVisualOrderAndClear should have processed all the windows.
   DCHECK_EQ(0u, windows.size());
 }
diff --git a/components/sessions/core/session_service_commands.h b/components/sessions/core/session_service_commands.h
index ce0a0c6..5715d6e 100644
--- a/components/sessions/core/session_service_commands.h
+++ b/components/sessions/core/session_service_commands.h
@@ -49,6 +49,10 @@
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateTabGroupCommand(
     const SessionID& tab_id,
     base::Optional<base::Token> group);
+SESSIONS_EXPORT std::unique_ptr<SessionCommand>
+CreateTabGroupMetadataUpdateCommand(const base::Token& group,
+                                    const base::string16& title,
+                                    SkColor color);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreatePinnedStateCommand(
     const SessionID& tab_id,
     bool is_pinned);
diff --git a/components/sessions/core/session_types.cc b/components/sessions/core/session_types.cc
index 066099d..3c83c4f 100644
--- a/components/sessions/core/session_types.cc
+++ b/components/sessions/core/session_types.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include "components/sessions/core/session_command.h"
+#include "ui/gfx/color_palette.h"
 
 namespace sessions {
 
@@ -22,6 +23,13 @@
 SessionTab::~SessionTab() {
 }
 
+// SessionTab -----------------------------------------------------------------
+
+SessionTabGroup::SessionTabGroup(base::Token group_id)
+    : group_id(group_id), color(gfx::kPlaceholderColor) {}
+
+SessionTabGroup::~SessionTabGroup() {}
+
 // SessionWindow ---------------------------------------------------------------
 
 SessionWindow::SessionWindow()
diff --git a/components/sessions/core/session_types.h b/components/sessions/core/session_types.h
index b160f2e9..14864bf 100644
--- a/components/sessions/core/session_types.h
+++ b/components/sessions/core/session_types.h
@@ -19,6 +19,7 @@
 #include "components/sessions/core/session_id.h"
 #include "components/sessions/core/sessions_export.h"
 #include "components/variations/variations_associated_data.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
@@ -97,6 +98,30 @@
   DISALLOW_COPY_AND_ASSIGN(SessionTab);
 };
 
+// SessionTabGroup -----------------------------------------------------------
+
+// Describes a tab group referenced by some SessionTab entry in its group
+// field. By default, this is initialized with placeholder values that are
+// visually obvious.
+struct SESSIONS_EXPORT SessionTabGroup {
+  explicit SessionTabGroup(base::Token group);
+  ~SessionTabGroup();
+
+  // Uniquely identifies this group. Initialized to zero and must be set be
+  // user. Unlike SessionID this should be globally unique, even across
+  // different sessions.
+  base::Token group_id;
+
+  // A human-readable title for the group.
+  base::string16 title;
+
+  // An accent color used when displaying the group.
+  SkColor color;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SessionTabGroup);
+};
+
 // SessionWindow -------------------------------------------------------------
 
 // Describes a saved window.
@@ -145,6 +170,10 @@
   // The tabs, ordered by visual order.
   std::vector<std::unique_ptr<SessionTab>> tabs;
 
+  // Tab groups in no particular order. For each group in |tab_groups|, there
+  // should be at least one tab in |tabs| in the group.
+  std::vector<std::unique_ptr<SessionTabGroup>> tab_groups;
+
   // Is the window maximized, minimized, or normal?
   ui::WindowShowState show_state;
 
diff --git a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
index f8e7348..7a7b07d 100644
--- a/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
+++ b/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc
@@ -22,10 +22,12 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "components/prefs/pref_service.h"
 #include "components/signin/internal/identity_manager/account_tracker_service.h"
 #include "components/signin/internal/identity_manager/oauth_multilogin_helper.h"
 #include "components/signin/internal/identity_manager/ubertoken_fetcher_impl.h"
 #include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
@@ -438,13 +440,34 @@
       fetcher_retries_(0),
       cookie_listener_binding_(this),
       external_cc_result_fetched_(false),
-      list_accounts_stale_(true) {}
+      list_accounts_stale_(true) {
+  std::string gaia_cookie_last_list_accounts_data =
+      signin_client_->GetPrefs()->GetString(
+          prefs::kGaiaCookieLastListAccountsData);
+
+  if (!gaia_cookie_last_list_accounts_data.empty()) {
+    if (!gaia::ParseListAccountsData(gaia_cookie_last_list_accounts_data,
+                                     &listed_accounts_,
+                                     &signed_out_accounts_)) {
+      DLOG(WARNING) << "GaiaCookieManagerService::ListAccounts: Failed to "
+                       "parse list accounts data from pref.";
+      listed_accounts_.clear();
+      signed_out_accounts_.clear();
+    }
+  }
+}
 
 GaiaCookieManagerService::~GaiaCookieManagerService() {
   CancelAll();
   DCHECK(requests_.empty());
 }
 
+// static
+void GaiaCookieManagerService::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(prefs::kGaiaCookieLastListAccountsData,
+                               std::string());
+}
+
 void GaiaCookieManagerService::InitCookieListener() {
   DCHECK(!cookie_listener_binding_);
   network::mojom::CookieManager* cookie_manager =
@@ -810,6 +833,8 @@
                                    &signed_out_accounts_)) {
     listed_accounts_.clear();
     signed_out_accounts_.clear();
+    signin_client_->GetPrefs()->ClearPref(
+        prefs::kGaiaCookieLastListAccountsData);
     GoogleServiceAuthError error(
         GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
     RecordListAccountsFailure(error.state());
@@ -817,6 +842,8 @@
     return;
   }
 
+  signin_client_->GetPrefs()->SetString(prefs::kGaiaCookieLastListAccountsData,
+                                        data);
   RecordListAccountsFailure(GoogleServiceAuthError::NONE);
   RecordListAccountsRetryResult(GoogleServiceAuthError::AuthErrorNone(),
                                 fetcher_retries_);
diff --git a/components/signin/internal/identity_manager/gaia_cookie_manager_service.h b/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
index c24edc3..193631a3 100644
--- a/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
+++ b/components/signin/internal/identity_manager/gaia_cookie_manager_service.h
@@ -18,6 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h"
 #include "components/signin/public/base/signin_client.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
@@ -291,6 +292,9 @@
   // Final call in the Setting accounts in cookie procedure. Public for testing.
   void OnSetAccountsFinished(signin::SetAccountsInCookieResult result);
 
+  // Registers prefs used by this class.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
  private:
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
 
diff --git a/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc b/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
index 492ee78..c3c1d3b 100644
--- a/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
+++ b/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc
@@ -121,6 +121,7 @@
         error_(GoogleServiceAuthError::SERVICE_ERROR),
         canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
+    GaiaCookieManagerService::RegisterPrefs(pref_service_.registry());
     signin_client_ = std::make_unique<TestSigninClient>(&pref_service_);
     token_service_ =
         std::make_unique<FakeProfileOAuth2TokenService>(&pref_service_);
@@ -650,6 +651,10 @@
   ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
   ASSERT_TRUE(list_accounts.empty());
   ASSERT_TRUE(signed_out_accounts.empty());
+  ASSERT_TRUE(signin_client()
+                  ->GetPrefs()
+                  ->GetString(prefs::kGaiaCookieLastListAccountsData)
+                  .empty());
 }
 
 TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsOneAccount) {
@@ -675,9 +680,12 @@
 
   ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
 
-  SimulateListAccountsSuccess(
-      &helper,
-      "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]");
+  std::string data =
+      "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]";
+  SimulateListAccountsSuccess(&helper, data);
+  EXPECT_EQ(signin_client()->GetPrefs()->GetString(
+                prefs::kGaiaCookieLastListAccountsData),
+            data);
 }
 
 TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsSignedOutAccounts) {
@@ -709,12 +717,15 @@
 
   ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
 
-  SimulateListAccountsSuccess(
-      &helper,
+  std::string data =
       "[\"f\","
       "[[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"],"
       " [\"b\", 0, \"n\", \"c@d.com\", \"p\", 0, 0, 0, 0, 1, \"9\","
-      "null,null,null,1]]]");
+      "null,null,null,1]]]";
+  SimulateListAccountsSuccess(&helper, data);
+  EXPECT_EQ(signin_client()->GetPrefs()->GetString(
+                prefs::kGaiaCookieLastListAccountsData),
+            data);
 }
 
 TEST_F(GaiaCookieManagerServiceTest, ListAccountsAcceptsNull) {
@@ -723,12 +734,12 @@
 
   ASSERT_FALSE(helper.ListAccounts(nullptr, nullptr));
 
-  SimulateListAccountsSuccess(
-      &helper,
+  std::string data =
       "[\"f\","
       "[[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"],"
       " [\"b\", 0, \"n\", \"c@d.com\", \"p\", 0, 0, 0, 0, 1, \"9\","
-      "null,null,null,1]]]");
+      "null,null,null,1]]]";
+  SimulateListAccountsSuccess(&helper, data);
 
   std::vector<gaia::ListedAccount> signed_out_accounts;
   ASSERT_TRUE(helper.ListAccounts(nullptr, &signed_out_accounts));
@@ -737,6 +748,10 @@
   std::vector<gaia::ListedAccount> accounts;
   ASSERT_TRUE(helper.ListAccounts(&accounts, nullptr));
   ASSERT_EQ(1u, accounts.size());
+
+  EXPECT_EQ(signin_client()->GetPrefs()->GetString(
+                prefs::kGaiaCookieLastListAccountsData),
+            data);
 }
 
 TEST_F(GaiaCookieManagerServiceTest, ListAccountsAfterOnCookieChange) {
@@ -764,14 +779,18 @@
   ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
   ASSERT_TRUE(list_accounts.empty());
   ASSERT_TRUE(signed_out_accounts.empty());
-  SimulateListAccountsSuccess(
-      &helper,
-      "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]");
+
+  std::string data =
+      "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]";
+  SimulateListAccountsSuccess(&helper, data);
 
   // Sanity-check that ListAccounts returns the cached data.
   ASSERT_TRUE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
   ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts));
   ASSERT_TRUE(signed_out_accounts.empty());
+  EXPECT_EQ(signin_client()->GetPrefs()->GetString(
+                prefs::kGaiaCookieLastListAccountsData),
+            data);
 
   EXPECT_CALL(helper, StartFetchingListAccounts());
   EXPECT_CALL(observer,
@@ -789,7 +808,117 @@
   ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
   ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts));
   ASSERT_TRUE(signed_out_accounts.empty());
-  SimulateListAccountsSuccess(&helper, "[\"f\",[]]");
+  data = "[\"f\",[]]";
+  SimulateListAccountsSuccess(&helper, data);
+  EXPECT_EQ(signin_client()->GetPrefs()->GetString(
+                prefs::kGaiaCookieLastListAccountsData),
+            data);
+}
+
+TEST_F(GaiaCookieManagerServiceTest, GaiaCookieLastListAccountsDataSaved) {
+  std::string data =
+      "[\"f\","
+      "[[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"],"
+      " [\"b\", 0, \"n\", \"c@d.com\", \"p\", 0, 0, 0, 0, 1, \"9\","
+      "null,null,null,1]]]";
+  std::vector<gaia::ListedAccount> expected_accounts;
+  gaia::ListedAccount listed_account;
+  listed_account.email = "a@b.com";
+  listed_account.raw_email = "a@b.com";
+  listed_account.gaia_id = "8";
+  expected_accounts.push_back(listed_account);
+
+  std::vector<gaia::ListedAccount> expected_signed_out_accounts;
+  gaia::ListedAccount signed_out_account;
+  signed_out_account.email = "c@d.com";
+  signed_out_account.raw_email = "c@d.com";
+  signed_out_account.gaia_id = "9";
+  signed_out_account.signed_out = true;
+  expected_signed_out_accounts.push_back(signed_out_account);
+  std::vector<gaia::ListedAccount> list_accounts;
+  std::vector<gaia::ListedAccount> signed_out_accounts;
+
+  {
+    InstrumentedGaiaCookieManagerService helper(token_service(),
+                                                signin_client());
+    MockObserver observer(&helper);
+
+    EXPECT_CALL(helper, StartFetchingListAccounts());
+    EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated(
+                              ListedAccountEquals(expected_accounts),
+                              ListedAccountEquals(expected_signed_out_accounts),
+                              no_error()));
+
+    ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
+    // |kGaiaCookieLastListAccountsData| is empty.
+    EXPECT_TRUE(list_accounts.empty());
+    EXPECT_TRUE(signed_out_accounts.empty());
+    SimulateListAccountsSuccess(&helper, data);
+    // |kGaiaCookieLastListAccountsData| is set.
+    ASSERT_EQ(signin_client()->GetPrefs()->GetString(
+                  prefs::kGaiaCookieLastListAccountsData),
+              data);
+    // List accounts is not stale.
+    ASSERT_TRUE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
+  }
+
+  // Now that the list accounts data is saved to the pref service, test that
+  // starting a new Gaia Service Manager gives synchronous answers to list
+  // accounts.
+  {
+    InstrumentedGaiaCookieManagerService helper(token_service(),
+                                                signin_client());
+    MockObserver observer(&helper);
+    EXPECT_CALL(helper, StartFetchingListAccounts()).Times(3);
+
+    // Though |SimulateListAccountsSuccess| is not yet called, we are able to
+    // retrieve last |list_accounts| and  |expected_accounts| from the pref,
+    // but mark them as stale. A |StartFetchingListAccounts| is triggered.
+    EXPECT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
+    EXPECT_TRUE(AreAccountListsEqual(list_accounts, expected_accounts));
+    EXPECT_TRUE(AreAccountListsEqual(signed_out_accounts,
+                                     expected_signed_out_accounts));
+
+    // |SimulateListAccountsSuccess| and assert list accounts is not stale
+    // anymore.
+    EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated(
+                              ListedAccountEquals(expected_accounts),
+                              ListedAccountEquals(expected_signed_out_accounts),
+                              no_error()));
+    SimulateListAccountsSuccess(&helper, data);
+    ASSERT_TRUE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
+
+    // Change list account state to be stale, which will trigger list accounts
+    // request.
+    helper.ForceOnCookieChangeProcessing();
+
+    // Receive an unexpected response from the server. Listed accounts as well
+    // as the pref should be cleared.
+    expected_accounts.clear();
+    expected_signed_out_accounts.clear();
+    GoogleServiceAuthError error(
+        GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
+    EXPECT_CALL(observer,
+                OnGaiaAccountsInCookieUpdated(
+                    ListedAccountEquals(expected_accounts),
+                    ListedAccountEquals(expected_signed_out_accounts), error));
+    SimulateListAccountsSuccess(&helper, "[]");
+    EXPECT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts));
+    // |kGaiaCookieLastListAccountsData| is cleared.
+    EXPECT_TRUE(signin_client()
+                    ->GetPrefs()
+                    ->GetString(prefs::kGaiaCookieLastListAccountsData)
+                    .empty());
+  }
+
+  {
+    // On next startup, |kGaiaCookieLastListAccountsData| contains last list
+    // accounts data.
+    EXPECT_TRUE(signin_client()
+                    ->GetPrefs()
+                    ->GetString(prefs::kGaiaCookieLastListAccountsData)
+                    .empty());
+  }
 }
 
 TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcher) {
diff --git a/components/signin/public/base/signin_pref_names.cc b/components/signin/public/base/signin_pref_names.cc
index 494ec235..0fe30da 100644
--- a/components/signin/public/base/signin_pref_names.cc
+++ b/components/signin/public/base/signin_pref_names.cc
@@ -111,4 +111,8 @@
 const char kTokenServiceExcludedSecondaryAccounts[] =
     "token_service.excluded_secondary_accounts";
 
+// Contains last |ListAccounts| data which corresponds to Gaia cookies.
+const char kGaiaCookieLastListAccountsData[] =
+    "gaia_cookie.last_list_accounts_data";
+
 }  // namespace prefs
diff --git a/components/signin/public/base/signin_pref_names.h b/components/signin/public/base/signin_pref_names.h
index 01d8bab..26f1c460d 100644
--- a/components/signin/public/base/signin_pref_names.h
+++ b/components/signin/public/base/signin_pref_names.h
@@ -30,6 +30,7 @@
 extern const char kTokenServiceDiceCompatible[];
 extern const char kTokenServiceExcludeAllSecondaryAccounts[];
 extern const char kTokenServiceExcludedSecondaryAccounts[];
+extern const char kGaiaCookieLastListAccountsData[];
 
 }  // namespace prefs
 
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index 0574b8c..9c482d9a 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -418,6 +418,7 @@
   PrimaryAccountManager::RegisterProfilePrefs(registry);
   AccountFetcherService::RegisterPrefs(registry);
   AccountTrackerService::RegisterPrefs(registry);
+  GaiaCookieManagerService::RegisterPrefs(registry);
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs(registry);
 #endif
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index 4b07091..d40f10a 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -67,6 +67,8 @@
     "async_document_subresource_filter_test_utils.h",
     "fake_safe_browsing_database_manager.cc",
     "fake_safe_browsing_database_manager.h",
+    "subframe_navigation_test_utils.cc",
+    "subframe_navigation_test_utils.h",
     "subresource_filter_observer_test_utils.cc",
     "subresource_filter_observer_test_utils.h",
   ]
@@ -75,6 +77,8 @@
     "//base/test:test_support",
     "//components/subresource_filter/core/common",
     "//content/public/browser",
+    "//content/test:test_support",
+    "//net",
     "//testing/gtest:gtest",
     "//url",
   ]
diff --git a/components/subresource_filter/content/browser/DEPS b/components/subresource_filter/content/browser/DEPS
index 14df806e..bebcdf4 100644
--- a/components/subresource_filter/content/browser/DEPS
+++ b/components/subresource_filter/content/browser/DEPS
@@ -2,6 +2,7 @@
   "+components/safe_browsing/db",
   "+components/ukm",
   "+content/public/browser",
+  "+content/public/test",
   "+net/base",
   "+services/metrics/public/cpp",
   "+ui/base/page_transition_types.h",
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index 51653d7..3a841a5 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
+#include "components/subresource_filter/content/browser/subframe_navigation_test_utils.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
@@ -223,13 +224,16 @@
 
   void CreateTestNavigation(const GURL& url,
                             content::RenderFrameHost* render_frame_host) {
-    DCHECK(!navigation_simulator_);
     DCHECK(render_frame_host);
     navigation_simulator_ =
         content::NavigationSimulator::CreateRendererInitiated(
             url, render_frame_host);
   }
 
+  content::NavigationSimulator* navigation_simulator() {
+    return navigation_simulator_.get();
+  }
+
   content::RenderFrameHost* CreateSubframeWithTestNavigation(
       const GURL& url,
       content::RenderFrameHost* parent) {
@@ -240,58 +244,12 @@
     return subframe;
   }
 
-  void SimulateStartAndExpectResult(
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Start();
-    content::NavigationThrottle::ThrottleCheckResult result =
-        navigation_simulator_->GetLastThrottleCheckResult();
-    EXPECT_EQ(expect_result, result);
-    if (result.action() != content::NavigationThrottle::PROCEED)
-      navigation_simulator_.reset();
-  }
-
-  void SimulateRedirectAndExpectResult(
-      const GURL& new_url,
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Redirect(new_url);
-    content::NavigationThrottle::ThrottleCheckResult result =
-        navigation_simulator_->GetLastThrottleCheckResult();
-    EXPECT_EQ(expect_result, result);
-    if (result.action() != content::NavigationThrottle::PROCEED)
-      navigation_simulator_.reset();
-  }
-
-  // Returns the RenderFrameHost that the navigation commit in.
-  content::RenderFrameHost* SimulateCommitAndExpectResult(
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Commit();
-    content::NavigationThrottle::ThrottleCheckResult result =
-        navigation_simulator_->GetLastThrottleCheckResult();
-    EXPECT_EQ(expect_result, result);
-
-    auto scoped_simulator = std::move(navigation_simulator_);
-    if (result.action() == content::NavigationThrottle::PROCEED)
-      return scoped_simulator->GetFinalRenderFrameHost();
-    return nullptr;
-  }
-
-  void SimulateSameDocumentCommit() {
-    navigation_simulator_->CommitSameDocument();
-    navigation_simulator_.reset();
-  }
-
-  void SimulateFailedNavigation(net::Error error) {
-    navigation_simulator_->Fail(error);
-    if (error != net::ERR_ABORTED) {
-      navigation_simulator_->CommitErrorPage();
-    }
-    navigation_simulator_.reset();
-  }
-
   void NavigateAndCommitMainFrame(const GURL& url) {
     CreateTestNavigation(url, main_rfh());
-    SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-    SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+    EXPECT_EQ(content::NavigationThrottle::PROCEED,
+              SimulateStartAndGetResult(navigation_simulator()));
+    EXPECT_EQ(content::NavigationThrottle::PROCEED,
+              SimulateCommitAndGetResult(navigation_simulator()));
   }
 
   bool ManagerHasRulesetHandle() {
@@ -387,8 +345,8 @@
   // A disallowed subframe navigation should be successfully filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -406,8 +364,8 @@
   // A disallowed subframe navigation should not be filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
-
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   EXPECT_EQ(0, disallowed_notification_count());
 }
 
@@ -420,9 +378,12 @@
   // A disallowed subframe navigation should not be filtered in dry-run mode.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   // But it should still be activated.
   ExpectActivationSignalForFrame(child, true /* expect_activation */,
                                  true /* is_ad_subframe */);
@@ -440,12 +401,12 @@
   // filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/before-redirect.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  content::NavigationThrottle::ThrottleAction expected_action =
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE;
-  SimulateRedirectAndExpectResult(
-      GURL("https://www.example.com/disallowed.html"), expected_action);
-
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://www.example.com/disallowed.html")));
   EXPECT_EQ(1, disallowed_notification_count());
 }
 
@@ -458,11 +419,16 @@
   // An allowed subframe navigation should complete successfully.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/allowed1.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  SimulateRedirectAndExpectResult(GURL("https://www.example.com/allowed2.html"),
-                                  content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://www.example.com/allowed2.html")));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(child, true /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -479,15 +445,15 @@
   // A disallowed subframe navigation should be successfully filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/1/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/2/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -501,8 +467,8 @@
   // A disallowed subframe navigation should be successfully filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/1/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 
@@ -512,8 +478,8 @@
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/2/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(2, disallowed_notification_count());
 }
@@ -529,21 +495,21 @@
   // A disallowed subframe navigation should be successfully filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/1/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 
   // Commit another navigation that triggers page level activation.
   GURL url2 = GURL(base::StringPrintf("%s#ref", kTestURLWithActivation));
   CreateTestNavigation(url2, main_rfh());
-  SimulateSameDocumentCommit();
+  navigation_simulator()->CommitSameDocument();
   ExpectActivationSignalForFrame(main_rfh(), false /* expect_activation */);
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/2/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -560,9 +526,12 @@
   // A subframe navigation should complete successfully.
   CreateSubframeWithTestNavigation(GURL("https://www.example.com/allowed.html"),
                                    main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(child, false /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -576,8 +545,8 @@
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 
@@ -592,8 +561,8 @@
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(2, disallowed_notification_count());
 }
@@ -618,9 +587,12 @@
   // A subframe navigation should complete successfully.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(child, false /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -636,15 +608,15 @@
       GURL(base::StringPrintf("%sinactive.html", kTestURLWithActivation));
 
   CreateTestNavigation(same_site_inactive_url, main_rfh());
-  SimulateFailedNavigation(net::ERR_ABORTED);
+  SimulateFailedNavigation(navigation_simulator(), net::ERR_ABORTED);
   EXPECT_TRUE(ManagerHasRulesetHandle());
   ExpectActivationSignalForFrame(main_rfh(), false /* expect_activation */);
 
   // A subframe navigation fail.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -659,15 +631,18 @@
       GURL(base::StringPrintf("%sinactive.html", kTestURLWithActivation));
 
   CreateTestNavigation(same_site_inactive_url, main_rfh());
-  SimulateFailedNavigation(net::ERR_FAILED);
+  SimulateFailedNavigation(navigation_simulator(), net::ERR_FAILED);
   EXPECT_FALSE(ManagerHasRulesetHandle());
   ExpectActivationSignalForFrame(main_rfh(), false /* expect_activation */);
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(child, false /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -683,25 +658,31 @@
   // filtering for this subframe document should still be activated.
   CreateSubframeWithTestNavigation(GURL("https://www.a.com/allowed.html"),
                                    main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe1 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe1, true /* expect_activation */);
 
   // Navigate a sub-subframe to a URL that is not itself disallowed. Subresource
   // filtering for this subframe document should still be activated.
   CreateSubframeWithTestNavigation(GURL("https://www.b.com/allowed.html"),
                                    subframe1);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe2 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe2, true /* expect_activation */);
 
   // A final, nested subframe navigation is filtered.
   CreateSubframeWithTestNavigation(GURL("https://www.c.com/disallowed.html"),
                                    subframe2);
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -713,17 +694,23 @@
 
   // Navigate a subframe that is not filtered, but should still activate.
   CreateSubframeWithTestNavigation(GURL("https://whitelist.com"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe1 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe1, true /* expect_activation */);
 
   // Navigate a sub-subframe that is not filtered due to the whitelist.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), subframe1);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe2 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe2, true /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -731,16 +718,19 @@
   // An identical series of events that don't match whitelist rules cause
   // filtering.
   CreateSubframeWithTestNavigation(GURL("https://average-joe.com"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe3 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe3, true /* expect_activation */);
 
   // Navigate a sub-subframe that is not filtered due to the whitelist.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), subframe3);
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -763,9 +753,12 @@
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(child, false /* expect_activation */);
 
   EXPECT_EQ(0, disallowed_notification_count());
@@ -796,9 +789,12 @@
 
   // Navigate a subframe that is not filtered, but should still activate.
   CreateSubframeWithTestNavigation(GURL("https://whitelist.com"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* subframe1 =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe1, true /* expect_activation */);
 
   tester.ExpectTotalCount(kActivationStateHistogram, 3);
@@ -823,9 +819,8 @@
   // A disallowed subframe navigation should be successfully filtered.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 
   EXPECT_EQ(1, disallowed_notification_count());
 }
@@ -845,9 +840,11 @@
   throttle_manager()->OnFrameIsAdSubframe(subframe);
   EXPECT_TRUE(throttle_manager()->IsFrameTaggedAsAd(subframe));
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  subframe =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  subframe = navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(subframe);
   ExpectActivationSignalForFrame(subframe, true /* expect_activation */,
                                  true /* is_ad_subframe */);
@@ -855,9 +852,9 @@
   // A non-ad navigation for the same frame should be considered an ad
   // subframe as well.
   CreateTestNavigation(GURL("https://example.com/allowed2.html"), subframe);
-  subframe =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
-  EXPECT_TRUE(subframe);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  subframe = navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe, true /* expect_activation */,
                                  true /* is_ad_subframe */);
 }
@@ -882,10 +879,13 @@
   throttle_manager()->OnFrameIsAdSubframe(initial_subframe);
   EXPECT_TRUE(throttle_manager()->IsFrameTaggedAsAd(initial_subframe));
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
 
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* final_subframe =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(final_subframe);
   EXPECT_NE(initial_subframe, final_subframe);
 
@@ -910,19 +910,22 @@
   // Simulate the render process telling the manager that the frame is an ad.
   throttle_manager()->OnFrameIsAdSubframe(subframe);
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-
-  subframe =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  subframe = navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(subframe, true /* expect_activation */,
                                  true /* is_ad_subframe */);
 
   // Create a grandchild frame that is marked as an ad because its parent is.
   content::RenderFrameHost* grandchild_frame = CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/foo/allowed.html"), subframe);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  grandchild_frame =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+  grandchild_frame = navigation_simulator()->GetFinalRenderFrameHost();
   ExpectActivationSignalForFrame(grandchild_frame, true /* expect_activation */,
                                  true /* is_ad_subframe */);
   EXPECT_TRUE(throttle_manager()->IsFrameTaggedAsAd(grandchild_frame));
@@ -942,9 +945,12 @@
   // A disallowed subframe navigation should not be filtered in dry-run mode.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/disallowed.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(child);
 
   // But it should still be activated.
@@ -956,9 +962,12 @@
   // tagged as ad because of its parent.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/allowed_by_ruleset.html"), child);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* grandchild =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(grandchild);
   ExpectActivationSignalForFrame(grandchild, true /* expect_activation */,
                                  true /* is_ad_subframe */);
@@ -968,9 +977,12 @@
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/great_grandchild_allowed_by_ruleset.html"),
       child);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* greatGrandchild =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(greatGrandchild);
   ExpectActivationSignalForFrame(greatGrandchild, true /* expect_activation */,
                                  true /* is_ad_subframe */);
@@ -986,9 +998,12 @@
 
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/allowed_by_ruleset.html"), main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* child =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(child);
   ExpectActivationSignalForFrame(child, true /* expect_activation */,
                                  false /* is_ad_subframe */);
@@ -998,9 +1013,12 @@
   // as ad because its parent is not tagged as well.
   CreateSubframeWithTestNavigation(
       GURL("https://www.example.com/also_allowed_by_ruleset.html"), child);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
   content::RenderFrameHost* grandchild =
-      SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+      navigation_simulator()->GetFinalRenderFrameHost();
   EXPECT_TRUE(grandchild);
   ExpectActivationSignalForFrame(grandchild, true /* expect_activation */,
                                  false /* is_ad_subframe */);
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
index 49b40db..513f720f 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
+#include "components/subresource_filter/content/browser/subframe_navigation_test_utils.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
@@ -127,32 +128,6 @@
                                                               render_frame);
   }
 
-  void SimulateStartAndExpectResult(
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Start();
-    EXPECT_EQ(expect_result,
-              navigation_simulator_->GetLastThrottleCheckResult());
-  }
-
-  void SimulateRedirectAndExpectResult(
-      const GURL& new_url,
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Redirect(new_url);
-    EXPECT_EQ(expect_result,
-              navigation_simulator_->GetLastThrottleCheckResult());
-  }
-
-  void SimulateCommitAndExpectResult(
-      content::NavigationThrottle::ThrottleAction expect_result) {
-    navigation_simulator_->Commit();
-    EXPECT_EQ(expect_result,
-              navigation_simulator_->GetLastThrottleCheckResult());
-  }
-
-  void SimulateCommitErrorPage() {
-    navigation_simulator_->CommitErrorPage();
-  }
-
   const std::vector<std::string>& GetConsoleMessages() {
     return content::RenderFrameHostTester::For(main_rfh())
         ->GetConsoleMessages();
@@ -182,8 +157,8 @@
   InitializeDocumentSubresourceFilter(GURL("https://example.test"));
   const GURL url("https://example.test/disallowed.html");
   CreateTestSubframeAndInitNavigation(url, main_rfh());
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
   EXPECT_TRUE(
       base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
 }
@@ -193,11 +168,12 @@
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  content::NavigationThrottle::ThrottleAction expected_result =
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE;
-  SimulateRedirectAndExpectResult(GURL("https://example.test/disallowed.html"),
-                                  expected_result);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://example.test/disallowed.html")));
 }
 
 TEST_F(SubframeNavigationFilteringThrottleTest, DryRunOnStart) {
@@ -206,7 +182,8 @@
   const GURL url("https://example.test/disallowed.html");
   CreateTestSubframeAndInitNavigation(url, main_rfh());
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
   EXPECT_FALSE(
       base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
 }
@@ -217,9 +194,12 @@
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  SimulateRedirectAndExpectResult(GURL("https://example.test/disallowed.html"),
-                                  content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://example.test/disallowed.html")));
 }
 
 TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnSecondRedirect) {
@@ -227,13 +207,16 @@
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  SimulateRedirectAndExpectResult(GURL("https://example.test/allowed2.html"),
-                                  content::NavigationThrottle::PROCEED);
-  content::NavigationThrottle::ThrottleAction expected_result =
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE;
-  SimulateRedirectAndExpectResult(GURL("https://example.test/disallowed.html"),
-                                  expected_result);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(
+      content::NavigationThrottle::PROCEED,
+      SimulateRedirectAndGetResult(navigation_simulator(),
+                                   GURL("https://example.test/allowed2.html")));
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://example.test/disallowed.html")));
 }
 
 TEST_F(SubframeNavigationFilteringThrottleTest, NeverFilterNonMatchingRule) {
@@ -241,10 +224,14 @@
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
 
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  SimulateRedirectAndExpectResult(GURL("https://example.test/allowed2.html"),
-                                  content::NavigationThrottle::PROCEED);
-  SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(
+      content::NavigationThrottle::PROCEED,
+      SimulateRedirectAndGetResult(navigation_simulator(),
+                                   GURL("https://example.test/allowed2.html")));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
 }
 
 TEST_F(SubframeNavigationFilteringThrottleTest, FilterSubsubframe) {
@@ -261,8 +248,8 @@
 
   CreateTestSubframeAndInitNavigation(
       GURL("https://example.test/disallowed.html"), parent_subframe);
-  SimulateStartAndExpectResult(
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
 }
 
 TEST_F(SubframeNavigationFilteringThrottleTest, DelayMetrics) {
@@ -271,12 +258,14 @@
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
   navigation_simulator()->SetTransition(ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  content::NavigationThrottle::ThrottleAction expected_result =
-      content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE;
-  SimulateRedirectAndExpectResult(GURL("https://example.test/disallowed.html"),
-                                  expected_result);
-  SimulateCommitErrorPage();
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateRedirectAndGetResult(
+                navigation_simulator(),
+                GURL("https://example.test/disallowed.html")));
+
+  navigation_simulator()->CommitErrorPage();
 
   const char kFilterDelayDisallowed[] =
       "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed";
@@ -287,8 +276,11 @@
 
   CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
                                       main_rfh());
-  SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
-  SimulateCommitAndExpectResult(content::NavigationThrottle::PROCEED);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateStartAndGetResult(navigation_simulator()));
+  EXPECT_EQ(content::NavigationThrottle::PROCEED,
+            SimulateCommitAndGetResult(navigation_simulator()));
+
   histogram_tester.ExpectTotalCount(kFilterDelayDisallowed, 1);
   histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 1);
 }
diff --git a/components/subresource_filter/content/browser/subframe_navigation_test_utils.cc b/components/subresource_filter/content/browser/subframe_navigation_test_utils.cc
new file mode 100644
index 0000000..1549ba6
--- /dev/null
+++ b/components/subresource_filter/content/browser/subframe_navigation_test_utils.cc
@@ -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.
+
+#include "components/subresource_filter/content/browser/subframe_navigation_test_utils.h"
+
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/test/navigation_simulator.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace subresource_filter {
+
+content::NavigationThrottle::ThrottleCheckResult SimulateStartAndGetResult(
+    content::NavigationSimulator* navigation_simulator) {
+  navigation_simulator->Start();
+  return navigation_simulator->GetLastThrottleCheckResult();
+}
+
+content::NavigationThrottle::ThrottleCheckResult SimulateRedirectAndGetResult(
+    content::NavigationSimulator* navigation_simulator,
+    const GURL& new_url) {
+  navigation_simulator->Redirect(new_url);
+  return navigation_simulator->GetLastThrottleCheckResult();
+}
+
+content::NavigationThrottle::ThrottleCheckResult SimulateCommitAndGetResult(
+    content::NavigationSimulator* navigation_simulator) {
+  navigation_simulator->Commit();
+  return navigation_simulator->GetLastThrottleCheckResult();
+}
+
+void SimulateFailedNavigation(
+    content::NavigationSimulator* navigation_simulator,
+    net::Error error) {
+  navigation_simulator->Fail(error);
+  if (error != net::ERR_ABORTED) {
+    navigation_simulator->CommitErrorPage();
+  }
+}
+
+}  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subframe_navigation_test_utils.h b/components/subresource_filter/content/browser/subframe_navigation_test_utils.h
new file mode 100644
index 0000000..c8ca5bf
--- /dev/null
+++ b/components/subresource_filter/content/browser/subframe_navigation_test_utils.h
@@ -0,0 +1,34 @@
+// 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_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBFRAME_NAVIGATION_TEST_UTILS_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBFRAME_NAVIGATION_TEST_UTILS_H_
+
+#include "content/public/browser/navigation_throttle.h"
+#include "net/base/net_errors.h"
+#include "url/gurl.h"
+
+namespace content {
+class NavigationSimulator;
+}
+
+namespace subresource_filter {
+
+content::NavigationThrottle::ThrottleCheckResult SimulateStartAndGetResult(
+    content::NavigationSimulator* navigation_simulator);
+
+content::NavigationThrottle::ThrottleCheckResult SimulateRedirectAndGetResult(
+    content::NavigationSimulator* navigation_simulator,
+    const GURL& new_url);
+
+content::NavigationThrottle::ThrottleCheckResult SimulateCommitAndGetResult(
+    content::NavigationSimulator* navigation_simulator);
+
+void SimulateFailedNavigation(
+    content::NavigationSimulator* navigation_simulator,
+    net::Error error);
+
+}  // namespace subresource_filter
+
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBFRAME_NAVIGATION_TEST_UTILS_H_
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h
index d2fca1b8..c7339084 100644
--- a/components/sync/driver/data_type_controller.h
+++ b/components/sync/driver/data_type_controller.h
@@ -117,6 +117,8 @@
   // DataTypeManager before downloading initial data. Non-blocking types need to
   // pass activation context containing progress marker to sync backend and use
   // |set_downloaded| to inform the manager whether their initial sync is done.
+  // TODO(treib): Make this synchronous and possibly merge with
+  // ActivateManuallyForNigori().
   virtual void RegisterWithBackend(
       base::OnceCallback<void(bool)> set_downloaded,
       ModelTypeConfigurer* configurer) = 0;
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index 702c5a8d..aa4ac89 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -209,6 +209,11 @@
           base::Bind(&DataTypeManagerImpl::SetTypeDownloaded,
                      base::Unretained(this), dtc->type()),
           configurer_);
+      // This assumes SetTypeDownloaded() is called synchronously, which is
+      // the case.
+      if (force_redownload_types_.Has(type)) {
+        downloaded_types_.Remove(type);
+      }
     }
   }
 }
@@ -391,7 +396,15 @@
           data_type_status_table_.ResetDataTypePolicyErrorFor(type);
       const bool unready_status_changed =
           data_type_status_table_.ResetUnreadyErrorFor(type);
-      return data_type_policy_error_changed || unready_status_changed;
+      if (!data_type_policy_error_changed && !unready_status_changed) {
+        // Nothing changed.
+        return false;
+      }
+      // If preconditions are newly met, the datatype should be immediately
+      // redownloaded as part of the datatype configuration (most relevant for
+      // the UNREADY_ERROR case which usually won't clear sync metadata).
+      force_redownload_types_.Put(type);
+      return true;
     }
 
     case DataTypeController::PreconditionState::kMustStopAndClearData: {
@@ -581,6 +594,8 @@
   if (!types_to_download.Empty())
     types_to_download.Put(NIGORI);
 
+  force_redownload_types_.RemoveAll(types_to_download);
+
   // TODO(sync): crbug.com/137550.
   // It's dangerous to configure types that have progress markers. Types with
   // progress markers can trigger a MIGRATION_DONE response. We are not
diff --git a/components/sync/driver/data_type_manager_impl.h b/components/sync/driver/data_type_manager_impl.h
index 99d4291..705abeee 100644
--- a/components/sync/driver/data_type_manager_impl.h
+++ b/components/sync/driver/data_type_manager_impl.h
@@ -206,6 +206,13 @@
   // |model_association_manager_| was last attempted.
   ModelTypeSet last_enabled_types_;
 
+  // A set of types that should be redownloaded even if initial sync is
+  // completed for them.
+  // TODO(crbug.com/967677): Once all datatypes are in USS, we should redesign
+  // this class and for example compute |downloaded_types_|'s initial value
+  // only after all datatypes have loaded for the first time.
+  ModelTypeSet force_redownload_types_;
+
   // Whether an attempt to reconfigure was made while we were busy configuring.
   // The |last_requested_types_| will reflect the newest set of requested types.
   bool needs_reconfigure_;
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc
index 00663fa9..4535273 100644
--- a/components/sync/engine_impl/model_type_registry.cc
+++ b/components/sync/engine_impl/model_type_registry.cc
@@ -260,16 +260,6 @@
   return result;
 }
 
-ModelTypeSet ModelTypeRegistry::GetInitialSyncDoneNonBlockingTypes() const {
-  ModelTypeSet types;
-  for (const auto& worker : model_type_workers_) {
-    if (worker->IsInitialSyncEnded()) {
-      types.Put(worker->GetModelType());
-    }
-  }
-  return types;
-}
-
 const UpdateHandler* ModelTypeRegistry::GetUpdateHandler(ModelType type) const {
   auto it = update_handler_map_.find(type);
   return it == update_handler_map_.end() ? nullptr : it->second;
diff --git a/components/sync/engine_impl/model_type_registry.h b/components/sync/engine_impl/model_type_registry.h
index 174e8cb..b04821e 100644
--- a/components/sync/engine_impl/model_type_registry.h
+++ b/components/sync/engine_impl/model_type_registry.h
@@ -94,9 +94,6 @@
   // applied.
   ModelTypeSet GetInitialSyncEndedTypes() const;
 
-  // Returns the set of non-blocking types with initial sync done.
-  ModelTypeSet GetInitialSyncDoneNonBlockingTypes() const;
-
   // Returns the update handler for |type|.
   const UpdateHandler* GetUpdateHandler(ModelType type) const;
 
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index 14fcd0e..fb01ab2 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -378,12 +378,6 @@
 
 void ModelTypeWorker::PassiveApplyUpdates(StatusController* status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // This should only be called at the end of the very first download cycle,
-  // except for NIGORI, which can be downloaded in multiple configuration sync
-  // cycles and hence repeatedly ends up in PassiveApplyUpdates().
-  DCHECK(type_ == NIGORI || !model_type_state_.initial_sync_done())
-      << "PassiveApplyUpdates() called after initial sync has been done for "
-      << ModelTypeToString(type_);
   // Indicate to the processor that the initial download is done. The initial
   // sync technically isn't done yet but by the time this value is persisted to
   // disk on the model thread it will be.
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index cd300d3..49d3a4cb 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -242,14 +242,6 @@
   DCHECK(!ready_task.is_null());
   DCHECK(initialized_);
 
-  // Don't download non-blocking types that have already completed initial sync.
-  ModelTypeSet to_remove =
-      model_type_registry_->GetInitialSyncDoneNonBlockingTypes();
-  // NIGORI is the exception, because we may want to download it several times
-  // during configuration (see DataTypeManagerImpl::PrepareConfigureParams()).
-  to_remove.Remove(NIGORI);
-  to_download.RemoveAll(to_remove);
-
   DVLOG(1) << "Configuring -"
            << "\n\t"
            << "types to download: " << ModelTypeSetToString(to_download);
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index b9f2c53..27d4d3b 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -75,9 +75,42 @@
 
 }  // namespace
 
-std::string SurfaceAggregator::ClipData::ToString() const {
-  return is_clipped ? "clip " + rect.ToString() : "no clip";
-}
+struct SurfaceAggregator::ClipData {
+  std::string ToString() const {
+    return is_clipped ? "clip " + rect.ToString() : "no clip";
+  }
+
+  bool is_clipped = false;
+  gfx::Rect rect;
+};
+
+struct SurfaceAggregator::PrewalkResult {
+  // This is the set of Surfaces that were referenced by another Surface, but
+  // not included in a SurfaceDrawQuad.
+  base::flat_set<SurfaceId> undrawn_surfaces;
+  bool may_contain_video = false;
+};
+
+struct SurfaceAggregator::RoundedCornerInfo {
+  RoundedCornerInfo() = default;
+
+  // |target_transform| is the transform that maps |bounds_arg| from its current
+  // space into the desired target space. It must be a scale+translation matrix.
+  RoundedCornerInfo(const gfx::RRectF& bounds_arg,
+                    bool is_fast_rounded_corner,
+                    const gfx::Transform target_transform)
+      : bounds(bounds_arg), is_fast_rounded_corner(is_fast_rounded_corner) {
+    if (bounds.IsEmpty())
+      return;
+    DCHECK(target_transform.Preserves2dAxisAlignment());
+    SkMatrix matrix = target_transform.matrix();
+    bounds.Scale(matrix.getScaleX(), matrix.getScaleY());
+    bounds.Offset(target_transform.To2dTranslation());
+  }
+
+  gfx::RRectF bounds;
+  bool is_fast_rounded_corner;
+};
 
 SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
                                      DisplayResourceProvider* provider,
@@ -99,10 +132,6 @@
   ProcessAddedAndRemovedSurfaces();
 }
 
-SurfaceAggregator::PrewalkResult::PrewalkResult() {}
-
-SurfaceAggregator::PrewalkResult::~PrewalkResult() {}
-
 // Create a clip rect for an aggregated quad from the original clip rect and
 // the clip rect from the surface it's on.
 SurfaceAggregator::ClipData SurfaceAggregator::CalculateClipRect(
@@ -487,9 +516,9 @@
 
     CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
                     surface->GetActiveFrame().device_scale_factor(),
-                    child_to_parent_map, gfx::Transform(), ClipData(),
-                    copy_pass.get(), surface_id, RoundedCornerInfo(),
-                    occluding_damage_rect, occluding_damage_rect_valid);
+                    child_to_parent_map, gfx::Transform(), {}, copy_pass.get(),
+                    surface_id, RoundedCornerInfo(), occluding_damage_rect,
+                    occluding_damage_rect_valid);
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
@@ -528,9 +557,9 @@
 
     // Intersect the transformed visible rect and the clip rect to create a
     // smaller cliprect for the quad.
-    ClipData surface_quad_clip_rect(
+    ClipData surface_quad_clip_rect = {
         true, cc::MathUtil::MapEnclosingClippedRect(
-                  source_sqs->quad_to_target_transform, source_visible_rect));
+                  source_sqs->quad_to_target_transform, source_visible_rect)};
     if (source_sqs->is_clipped) {
       surface_quad_clip_rect.rect.Intersect(source_sqs->clip_rect);
     }
@@ -572,8 +601,7 @@
                  /* backdrop_filter_quality*/ 1.0f);
   }
 
-  // Need to re-query since referenced_surfaces_ iterators are not stable.
-  referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+  referenced_surfaces_.erase(surface_id);
 }
 
 void SurfaceAggregator::EmitDefaultBackgroundColorQuad(
@@ -772,20 +800,6 @@
       occluding_damage_rect_valid);
 }
 
-SurfaceAggregator::RoundedCornerInfo::RoundedCornerInfo(
-    const gfx::RRectF& bounds_arg,
-    bool is_fast_rounded_corner_arg,
-    const gfx::Transform target_transform) {
-  is_fast_rounded_corner = is_fast_rounded_corner_arg;
-  if (bounds_arg.IsEmpty())
-    return;
-  DCHECK(target_transform.Preserves2dAxisAlignment());
-  bounds = bounds_arg;
-  SkMatrix matrix = target_transform.matrix();
-  bounds.Scale(matrix.getScaleX(), matrix.getScaleY());
-  bounds.Offset(target_transform.To2dTranslation());
-}
-
 SharedQuadState* SurfaceAggregator::CopyAndScaleSharedQuadState(
     const SharedQuadState* source_sqs,
     const gfx::Transform& scaled_quad_to_target_transform,
@@ -799,7 +813,7 @@
     bool occluding_damage_rect_valid) {
   auto* shared_quad_state = dest_render_pass->CreateAndAppendSharedQuadState();
   ClipData new_clip_rect = CalculateClipRect(
-      clip_rect, ClipData(source_sqs->is_clipped, source_sqs->clip_rect),
+      clip_rect, {source_sqs->is_clipped, source_sqs->clip_rect},
       target_transform);
 
   // target_transform contains any transformation that may exist
@@ -868,7 +882,7 @@
     // Both cannot be set at once. If this happens then a surface is being
     // merged when it should not.
     DCHECK(quad->shared_quad_state->rounded_corner_bounds.IsEmpty() ||
-           parent_rounded_corner_info.IsEmpty());
+           parent_rounded_corner_info.bounds.IsEmpty());
 
     if (quad->material == DrawQuad::Material::kSurfaceContent) {
       const auto* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
@@ -879,7 +893,7 @@
       if (!surface_quad->surface_range.end().is_valid())
         continue;
 
-      if (parent_rounded_corner_info.IsEmpty()) {
+      if (parent_rounded_corner_info.bounds.IsEmpty()) {
         new_rounded_corner_info = RoundedCornerInfo(
             quad->shared_quad_state->rounded_corner_bounds,
             quad->shared_quad_state->is_fast_rounded_corner, target_transform);
@@ -891,7 +905,7 @@
           &damage_rect_in_quad_space_valid, new_rounded_corner_info);
     } else {
       if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
-        if (parent_rounded_corner_info.IsEmpty()) {
+        if (parent_rounded_corner_info.bounds.IsEmpty()) {
           new_rounded_corner_info =
               RoundedCornerInfo(quad->shared_quad_state->rounded_corner_bounds,
                                 quad->shared_quad_state->is_fast_rounded_corner,
@@ -1040,7 +1054,7 @@
                     frame.device_scale_factor(), child_to_parent_map,
                     apply_surface_transform_to_root_pass ? surface_transform
                                                          : gfx::Transform(),
-                    ClipData(), copy_pass.get(), surface->surface_id(),
+                    {}, copy_pass.get(), surface->surface_id(),
                     RoundedCornerInfo(), occluding_damage_rect,
                     occluding_damage_rect_valid);
 
@@ -1372,8 +1386,7 @@
       has_cached_render_passes_ = true;
   }
 
-  auto it = referenced_surfaces_.find(surface->surface_id());
-  referenced_surfaces_.erase(it);
+  referenced_surfaces_.erase(surface->surface_id());
   if (!damage_rect.IsEmpty() && frame.metadata.may_contain_video)
     result->may_contain_video = true;
 
@@ -1417,8 +1430,7 @@
       prewalk_result->undrawn_surfaces.erase(surface_id);
       referenced_surfaces_.insert(surface_id);
       CopyPasses(surface->GetActiveFrame(), surface);
-      // CopyPasses may have mutated container, need to re-query to erase.
-      referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+      referenced_surfaces_.erase(surface_id);
     }
   }
 }
@@ -1444,7 +1456,7 @@
     const RoundedCornerInfo& rounded_corner_info,
     const RenderPass& root_render_pass) {
   // If the quad has no rounded corner, then we do not have to block merging.
-  if (rounded_corner_info.IsEmpty())
+  if (rounded_corner_info.bounds.IsEmpty())
     return true;
 
   // If the quad has rounded corner and it is not a fast rounded corner, we
@@ -1518,8 +1530,7 @@
   CopyUndrawnSurfaces(&prewalk_result);
   referenced_surfaces_.insert(surface_id);
   CopyPasses(root_surface_frame, surface);
-  // CopyPasses may have mutated container, need to re-query to erase.
-  referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
+  referenced_surfaces_.erase(surface_id);
   AddColorConversionPass();
 
   moved_pixel_passes_.clear();
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 9012e6d..085527e68 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -75,25 +75,9 @@
   void SetFrameAnnotator(std::unique_ptr<FrameAnnotator> frame_annotator);
 
  private:
-  struct ClipData {
-    ClipData() : is_clipped(false) {}
-    ClipData(bool is_clipped, const gfx::Rect& rect)
-        : is_clipped(is_clipped), rect(rect) {}
-
-    std::string ToString() const;
-
-    bool is_clipped;
-    gfx::Rect rect;
-  };
-
-  struct PrewalkResult {
-    PrewalkResult();
-    ~PrewalkResult();
-    // This is the set of Surfaces that were referenced by another Surface, but
-    // not included in a SurfaceDrawQuad.
-    base::flat_set<SurfaceId> undrawn_surfaces;
-    bool may_contain_video = false;
-  };
+  struct ClipData;
+  struct PrewalkResult;
+  struct RoundedCornerInfo;
 
   struct RenderPassInfo {
     // This is the id the pass is mapped to.
@@ -102,20 +86,6 @@
     bool in_use = true;
   };
 
-  struct RoundedCornerInfo {
-    RoundedCornerInfo() : is_fast_rounded_corner(false) {}
-    // |target_transform| is the transform that maps |bounds| from its current
-    // space into the desired target space. It must be a scale+translation
-    // matrix.
-    RoundedCornerInfo(const gfx::RRectF& bounds,
-                      bool is_fast_rounded_corner,
-                      const gfx::Transform target_transform);
-
-    bool IsEmpty() const { return bounds.IsEmpty(); }
-    gfx::RRectF bounds;
-    bool is_fast_rounded_corner;
-  };
-
   ClipData CalculateClipRect(const ClipData& surface_clip,
                              const ClipData& quad_clip,
                              const gfx::Transform& target_transform);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 97a3a70e..fb4db9f9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1789,6 +1789,8 @@
     "storage_partition_impl_map.h",
     "system_connector_impl.cc",
     "system_connector_impl.h",
+    "theme_helper.cc",
+    "theme_helper.h",
     "theme_helper_mac.h",
     "theme_helper_mac.mm",
     "tracing/background_memory_tracing_observer.cc",
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 5bf01d1..4e7e971 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -194,6 +194,10 @@
          GetRole() == ax::mojom::Role::kWebArea;
 }
 
+bool BrowserAccessibility::IsIgnored() const {
+  return node()->IsIgnored();
+}
+
 bool BrowserAccessibility::IsTextOnlyObject() const {
   return node_ && node_->IsText();
 }
@@ -1106,7 +1110,7 @@
 }
 
 std::string BrowserAccessibility::GetLiveRegionText() const {
-  if (GetRole() == ax::mojom::Role::kIgnored)
+  if (IsIgnored())
     return "";
 
   std::string text = GetStringAttribute(ax::mojom::StringAttribute::kName);
@@ -1373,7 +1377,7 @@
 const std::vector<gfx::NativeViewAccessible>
 BrowserAccessibility::GetDescendants() const {
   std::vector<gfx::NativeViewAccessible> descendants;
-  if (!HasState(ax::mojom::State::kIgnored) && PlatformChildCount() > 0) {
+  if (!IsIgnored() && PlatformChildCount() > 0) {
     BrowserAccessibility* next_sibling_node = PlatformGetNextSibling();
     BrowserAccessibility* next_descendant_node =
         BrowserAccessibilityManager::NextInTreeOrder(this);
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 820d81d01..182f378 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -106,6 +106,8 @@
 
   bool IsEditField() const;
 
+  bool IsIgnored() const;
+
   // Returns true if this object is used only for representing text.
   bool IsTextOnlyObject() const;
 
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 899e66d..6432891 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1718,7 +1718,7 @@
 
   win_attributes_->value = GetValue();
 
-  win_attributes_->ignored = owner()->HasState(ax::mojom::State::kIgnored);
+  win_attributes_->ignored = owner()->IsIgnored();
 }
 
 void BrowserAccessibilityComWin::UpdateStep2ComputeHypertext() {
@@ -1728,7 +1728,7 @@
 void BrowserAccessibilityComWin::UpdateStep3FireEvents(
     bool is_subtree_creation) {
   int32_t state = MSAAState();
-  const bool ignored = owner()->HasState(ax::mojom::State::kIgnored);
+  const bool ignored = owner()->IsIgnored();
 
   // Suppress all of these events when the node is ignored, or when the ignored
   // state has changed.
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 8d7b38ee..78cdfdf 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -158,7 +158,7 @@
       // Since AuraLinux needs to send the children-changed::add event with the
       // index in parent, the event must be fired after the node is unignored.
       // children-changed:remove is handled in |OnStateChanged|
-      if (!node->HasState(ax::mojom::State::kIgnored)) {
+      if (!node->IsIgnored()) {
         if (node->IsNative() && node->GetParent()) {
           g_signal_emit_by_name(node->GetParent(), "children-changed::add",
                                 node->GetIndexInParent(),
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index 5b1efadd..5847fc6 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -181,9 +181,7 @@
       // If this node is ignored, notify from the platform parent if available,
       // since it will be unignored.
       BrowserAccessibility* target_node =
-          node->GetData().HasState(ax::mojom::State::kIgnored)
-              ? node->PlatformGetParent()
-              : node;
+          node->IsIgnored() ? node->PlatformGetParent() : node;
       if (target_node) {
         FireWinAccessibilityEvent(EVENT_OBJECT_REORDER, target_node);
         FireUiaStructureChangedEvent(StructureChangeType_ChildrenReordered,
@@ -241,7 +239,7 @@
       aria_properties_events_.insert(node);
       break;
     case ui::AXEventGenerator::Event::IGNORED_CHANGED:
-      if (node->HasState(ax::mojom::State::kIgnored)) {
+      if (node->IsIgnored()) {
         FireWinAccessibilityEvent(EVENT_OBJECT_HIDE, node);
         FireUiaStructureChangedEvent(StructureChangeType_ChildRemoved, node);
       } else {
@@ -422,7 +420,7 @@
       default:
         return;
     }
-  } else if (node->HasState(ax::mojom::State::kIgnored)) {
+  } else if (node->IsIgnored()) {
     return;
   }
 
@@ -446,8 +444,7 @@
   if (!ShouldFireEventForNode(node))
     return;
   // Suppress events when |IGNORED_CHANGED|
-  if (node->HasState(ax::mojom::State::kIgnored) ||
-      base::Contains(ignored_changed_nodes_, node))
+  if (node->IsIgnored() || base::Contains(ignored_changed_nodes_, node))
     return;
 
   ::UiaRaiseAutomationEvent(ToBrowserAccessibilityWin(node)->GetCOM(),
@@ -461,12 +458,10 @@
     return;
   if (!ShouldFireEventForNode(node))
     return;
-
   // Suppress events when |IGNORED_CHANGED| with the exception for firing
   // UIA_AriaPropertiesPropertyId-hidden event on non-text node marked as
   // ignored.
-  if (node->HasState(ax::mojom::State::kIgnored) ||
-      base::Contains(ignored_changed_nodes_, node)) {
+  if (node->IsIgnored() || base::Contains(ignored_changed_nodes_, node)) {
     if (uia_property != UIA_AriaPropertiesPropertyId ||
         node->IsTextOnlyObject())
       return;
@@ -501,7 +496,7 @@
       default:
         return;
     }
-  } else if (node->HasState(ax::mojom::State::kIgnored)) {
+  } else if (node->IsIgnored()) {
     return;
   }
 
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 2ee58a4f..12a7cf2 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -649,7 +649,7 @@
 static size_t ActualUnignoredChildCount(const ui::AXNode* node) {
   size_t count = 0;
   for (const ui::AXNode* child : node->children()) {
-    if (child->data().HasState(ax::mojom::State::kIgnored)) {
+    if (child->IsIgnored()) {
       count += ActualUnignoredChildCount(child);
     } else {
       ++count;
@@ -678,7 +678,7 @@
   if (node->GetData().GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
                                          &tag))
     base_message << ", node_tag: <" << tag.c_str() << ">";
-  if (node->GetData().HasState(ax::mojom::State::kIgnored))
+  if (node->IsIgnored())
     base_message << ", IGNORED";
   if (BrowserAccessibilityManager* manager = node->manager()) {
     if (ui::AXTree* tree = manager->ax_tree()) {
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 23798cdb..46807ac 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -3104,7 +3104,7 @@
           base::nullopt /* appcache_host_id */,
           mojom::WasActivatedOption::kUnknown,
           base::UnguessableToken::Create() /* navigation_token */,
-          std::vector<PrefetchedSignedExchangeInfo>(),
+          std::vector<mojom::PrefetchedSignedExchangeInfoPtr>(),
 #if defined(OS_ANDROID)
           std::string(), /* data_url_as_string */
 #endif
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 6d57d65..4a1f737 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -733,7 +733,7 @@
           should_clear_history_list(), mojom::NavigationTiming::New(),
           base::nullopt, mojom::WasActivatedOption::kUnknown,
           base::UnguessableToken::Create(),
-          std::vector<PrefetchedSignedExchangeInfo>(),
+          std::vector<mojom::PrefetchedSignedExchangeInfoPtr>(),
 #if defined(OS_ANDROID)
           std::string(),
 #endif
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 76341f9..c7a2820 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -52,6 +52,7 @@
 #include "content/common/content_constants_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/navigation_params.h"
+#include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/navigation_params_utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -628,7 +629,8 @@
           mojom::WasActivatedOption::kUnknown,
           base::UnguessableToken::Create(),  // navigation_token
           std::vector<
-              PrefetchedSignedExchangeInfo>(),  // prefetched_signed_exchanges
+              mojom::
+                  PrefetchedSignedExchangeInfoPtr>(),  // prefetched_signed_exchanges
 #if defined(OS_ANDROID)
           std::string(),  // data_url_as_string
 #endif
@@ -699,7 +701,7 @@
           mojom::NavigationTiming::New(), base::nullopt /* appcache_host_id; */,
           mojom::WasActivatedOption::kUnknown,
           base::UnguessableToken::Create() /* navigation_token */,
-          std::vector<PrefetchedSignedExchangeInfo>(),
+          std::vector<mojom::PrefetchedSignedExchangeInfoPtr>(),
 #if defined(OS_ANDROID)
           std::string(), /* data_url_as_string */
 #endif
@@ -2184,17 +2186,19 @@
         render_frame_host_->GetProcess()->GetID(),
         render_frame_host_->GetRoutingID(), &service_worker_provider_info);
   }
+  auto common_params = common_params_.Clone();
+  auto commit_params = commit_params_.Clone();
   if (subresource_loader_params_ &&
       !subresource_loader_params_->prefetched_signed_exchanges.empty()) {
-    commit_params_->prefetched_signed_exchanges =
+    commit_params->prefetched_signed_exchanges =
         std::move(subresource_loader_params_->prefetched_signed_exchanges);
   }
 
   render_frame_host_->CommitNavigation(
-      this, *common_params_, *commit_params_, response_head_.get(),
-      std::move(response_body_), std::move(url_loader_client_endpoints_),
-      is_view_source_, std::move(subresource_loader_params_),
-      std::move(subresource_overrides_),
+      this, std::move(common_params), std::move(commit_params),
+      response_head_.get(), std::move(response_body_),
+      std::move(url_loader_client_endpoints_), is_view_source_,
+      std::move(subresource_loader_params_), std::move(subresource_overrides_),
       std::move(service_worker_provider_info), devtools_navigation_token_,
       std::move(bundled_exchanges_handle_));
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 904a7f61..28ddfd0 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -125,6 +125,7 @@
 #include "content/common/input/input_handler.mojom.h"
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/navigation_params.h"
+#include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/navigation_params_utils.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/common/renderer.mojom.h"
@@ -4444,7 +4445,7 @@
   NavigationDownloadPolicy download_policy;
   download_policy.SetDisallowed(NavigationDownloadType::kInterstitial);
 
-  mojom::CommonNavigationParams common_params(
+  auto common_params = mojom::CommonNavigationParams::New(
       data_url, base::nullopt, blink::mojom::Referrer::New(),
       ui::PAGE_TRANSITION_LINK, mojom::NavigationType::DIFFERENT_DOCUMENT,
       download_policy, false, GURL(), GURL(), PREVIEWS_OFF,
@@ -4452,16 +4453,14 @@
       false /* started_from_context_menu */, false /* has_user_gesture */,
       InitiatorCSPInfo(), std::vector<int>(), std::string(),
       false /* is_history_navigation_in_new_child_frame */, base::TimeTicks());
-  mojom::CommitNavigationParams commit_params;
-  commit_params.navigation_token = base::UnguessableToken::Create();
-  commit_params.navigation_timing = mojom::NavigationTiming::New();
-  CommitNavigation(
-      nullptr /* navigation_request */, common_params, commit_params,
-      nullptr /* response_head */, mojo::ScopedDataPipeConsumerHandle(),
-      network::mojom::URLLoaderClientEndpointsPtr(), false, base::nullopt,
-      base::nullopt /* subresource_overrides */, nullptr /* provider_info */,
-      base::UnguessableToken::Create() /* not traced */,
-      nullptr /* bundled_exchanges_factory */);
+  CommitNavigation(nullptr /* navigation_request */, std::move(common_params),
+                   CreateCommitNavigationParams(), nullptr /* response_head */,
+                   mojo::ScopedDataPipeConsumerHandle(),
+                   network::mojom::URLLoaderClientEndpointsPtr(), false,
+                   base::nullopt, base::nullopt /* subresource_overrides */,
+                   nullptr /* provider_info */,
+                   base::UnguessableToken::Create() /* not traced */,
+                   nullptr /* bundled_exchanges_factory */);
 }
 
 void RenderFrameHostImpl::Stop() {
@@ -4804,8 +4803,8 @@
 
 void RenderFrameHostImpl::CommitNavigation(
     NavigationRequest* navigation_request,
-    const mojom::CommonNavigationParams& common_params,
-    const mojom::CommitNavigationParams& commit_params,
+    mojom::CommonNavigationParamsPtr common_params,
+    mojom::CommitNavigationParamsPtr commit_params,
     network::ResourceResponse* response_head,
     mojo::ScopedDataPipeConsumerHandle response_body,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -4820,8 +4819,8 @@
 
   TRACE_EVENT2("navigation", "RenderFrameHostImpl::CommitNavigation",
                "frame_tree_node", frame_tree_node_->frame_tree_node_id(), "url",
-               common_params.url.possibly_invalid_spec());
-  DCHECK(!IsRendererDebugURL(common_params.url));
+               common_params->url.possibly_invalid_spec());
+  DCHECK(!IsRendererDebugURL(common_params->url));
 
   bool is_mhtml_iframe =
       navigation_request && navigation_request->IsForMhtmlSubframe();
@@ -4829,9 +4828,9 @@
   // A |response| and a |url_loader_client_endpoints| must always be provided,
   // except for edge cases, where another way to load the document exist.
   DCHECK((response_head && url_loader_client_endpoints) ||
-         common_params.url.SchemeIs(url::kDataScheme) ||
-         NavigationTypeUtils::IsSameDocument(common_params.navigation_type) ||
-         !IsURLHandledByNetworkStack(common_params.url) || is_mhtml_iframe);
+         common_params->url.SchemeIs(url::kDataScheme) ||
+         NavigationTypeUtils::IsSameDocument(common_params->navigation_type) ||
+         !IsURLHandledByNetworkStack(common_params->url) || is_mhtml_iframe);
 
   // All children of MHTML documents must be MHTML documents.
   // As a defensive measure, crash the browser if something went wrong.
@@ -4839,7 +4838,7 @@
     RenderFrameHostImpl* root =
         frame_tree_node()->frame_tree()->root()->current_frame_host();
     if (root->is_mhtml_document_ &&
-        !common_params.url.SchemeIs(url::kDataScheme)) {
+        !common_params->url.SchemeIs(url::kDataScheme)) {
       bool loaded_from_outside_the_archive =
           response_head || url_loader_client_endpoints;
       CHECK(!loaded_from_outside_the_archive);
@@ -4856,10 +4855,10 @@
   //
   // TODO(arthursonzogni): Replace DumpWithoutCrashing by a CHECK on M79 if it
   // is never reached.
-  bool is_srcdoc = common_params.url.IsAboutSrcdoc();
+  bool is_srcdoc = common_params->url.IsAboutSrcdoc();
   if (is_srcdoc) {
     if (frame_tree_node_->IsMainFrame() ||
-        common_params.url != GURL(url::kAboutSrcdocURL)) {
+        common_params->url != GURL(url::kAboutSrcdocURL)) {
       base::debug::DumpWithoutCrashing();
     }
   }
@@ -4871,9 +4870,9 @@
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
   const GURL& lock_url = GetSiteInstance()->lock_url();
   if (lock_url != GURL(kUnreachableWebDataURL) &&
-      common_params.url.IsStandard() &&
+      common_params->url.IsStandard() &&
       !policy->CanAccessDataForOrigin(GetProcess()->GetID(),
-                                      common_params.url) &&
+                                      common_params->url) &&
       !is_mhtml_iframe) {
     base::debug::SetCrashKeyString(
         base::debug::AllocateCrashKeyString("lock_url",
@@ -4882,20 +4881,20 @@
     base::debug::SetCrashKeyString(
         base::debug::AllocateCrashKeyString("commit_origin",
                                             base::debug::CrashKeySize::Size64),
-        common_params.url.GetOrigin().spec());
+        common_params->url.GetOrigin().spec());
     base::debug::SetCrashKeyString(
         base::debug::AllocateCrashKeyString("is_main_frame",
                                             base::debug::CrashKeySize::Size32),
         frame_tree_node_->IsMainFrame() ? "true" : "false");
     NOTREACHED() << "Commiting in incompatible process for URL: " << lock_url
-                 << " lock vs " << common_params.url.GetOrigin();
+                 << " lock vs " << common_params->url.GetOrigin();
     base::debug::DumpWithoutCrashing();
   }
 
   const bool is_first_navigation = !has_committed_any_navigation_;
   has_committed_any_navigation_ = true;
 
-  UpdatePermissionsForNavigation(common_params, commit_params);
+  UpdatePermissionsForNavigation(*common_params, *commit_params);
 
   // Get back to a clean state, in case we start a new navigation without
   // completing an unload handler.
@@ -4911,20 +4910,20 @@
   const network::ResourceResponseHead head =
       response_head ? response_head->head : network::ResourceResponseHead();
   const bool is_same_document =
-      NavigationTypeUtils::IsSameDocument(common_params.navigation_type);
+      NavigationTypeUtils::IsSameDocument(common_params->navigation_type);
 
   // Network isolation key should be filled before the URLLoaderFactory for
   // sub-resources is created. Only update for cross document navigations since
   // for opaque origin same document navigations, a new origin should not be
   // created as that would be different from the original.
   // TODO(crbug.com/971796): For about:blank and other such urls,
-  // common_params.url currently leads to an opaque origin to be created. Once
+  // common_params->url currently leads to an opaque origin to be created. Once
   // this is fixed, the origin which these navigations eventually get committed
   // to will be available here as well.
   // TODO(crbug.com/979296): Consider changing this code to copy an origin
   // instead of creating one from a URL which lacks opacity information.
   if (!is_same_document) {
-    const url::Origin frame_origin = url::Origin::Create(common_params.url);
+    const url::Origin frame_origin = url::Origin::Create(common_params->url);
     const url::Origin top_frame_origin =
         frame_tree_node_->IsMainFrame() ? frame_origin
                                         : frame_tree_->root()->current_origin();
@@ -4979,7 +4978,7 @@
         pending_default_factory;
 
     // See if this is for WebUI.
-    std::string scheme = common_params.url.scheme();
+    std::string scheme = common_params->url.scheme();
     const auto& webui_schemes = URLDataManagerBackend::GetWebUISchemes();
     if (base::Contains(webui_schemes, scheme)) {
       network::mojom::URLLoaderFactoryPtr factory_for_webui =
@@ -4990,7 +4989,7 @@
       // of WebUIs that need to be fixed to not make network requests in JS.
       if ((enabled_bindings_ & kWebUIBindingsPolicyMask) &&
           !GetContentClient()->browser()->IsWebUIAllowedToMakeNetworkRequests(
-              url::Origin::Create(common_params.url.GetOrigin()))) {
+              url::Origin::Create(common_params->url.GetOrigin()))) {
         pending_default_factory = factory_for_webui.PassInterface();
         // WebUIURLLoaderFactory will kill the renderer if it sees a request
         // with a non-chrome scheme. Register a URLLoaderFactory for the about
@@ -5035,7 +5034,7 @@
     // Other URLs like about:srcdoc or about:blank might be able load files, but
     // only because they will inherit loaders from their parents instead of the
     // ones provided by the browser process here.
-    if (common_params.url.SchemeIsFile()) {
+    if (common_params->url.SchemeIsFile()) {
       auto file_factory = std::make_unique<FileURLLoaderFactory>(
           browser_context->GetPath(),
           browser_context->GetSharedCorsOriginAccessList(),
@@ -5046,7 +5045,7 @@
     }
 
 #if defined(OS_ANDROID)
-    if (common_params.url.SchemeIs(url::kContentScheme)) {
+    if (common_params->url.SchemeIs(url::kContentScheme)) {
       // Only content:// URLs can load content:// subresources
       auto content_factory = std::make_unique<ContentURLLoaderFactory>(
           base::CreateSequencedTaskRunner(
@@ -5114,14 +5113,16 @@
          subresource_loader_factories);
 
   if (is_same_document) {
+    bool should_replace_current_entry =
+        common_params->should_replace_current_entry;
     DCHECK(same_document_navigation_request_);
     GetNavigationControl()->CommitSameDocumentNavigation(
-        common_params.Clone(), commit_params.Clone(),
+        std::move(common_params), std::move(commit_params),
         base::BindOnce(&RenderFrameHostImpl::OnSameDocumentCommitProcessed,
                        base::Unretained(this),
                        same_document_navigation_request_->navigation_handle()
                            ->GetNavigationId(),
-                       common_params.should_replace_current_entry));
+                       should_replace_current_entry));
   } else {
     // Pass the controller service worker info if we have one.
     blink::mojom::ControllerServiceWorkerInfoPtr controller;
@@ -5222,9 +5223,13 @@
       DCHECK(!prefetch_loader_factory);
     }
 
+    // If a network request was made, update the Previews state.
+    if (IsURLHandledByNetworkStack(common_params->url))
+      last_navigation_previews_state_ = common_params->previews_state;
+
     SendCommitNavigation(
-        navigation_client, navigation_request, common_params.Clone(),
-        commit_params.Clone(), head, std::move(response_body),
+        navigation_client, navigation_request, std::move(common_params),
+        std::move(commit_params), head, std::move(response_body),
         std::move(url_loader_client_endpoints),
         std::move(subresource_loader_factories),
         std::move(subresource_overrides), std::move(controller),
@@ -5242,10 +5247,6 @@
               subresource_loader_params->controller_service_worker_object_host,
               std::move(remote_object), sent_state));
     }
-
-    // If a network request was made, update the Previews state.
-    if (IsURLHandledByNetworkStack(common_params.url))
-      last_navigation_previews_state_ = common_params.previews_state;
   }
 
   is_loading_ = true;
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index b877b7ba..916f673 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -690,8 +690,8 @@
   // process, e.g. by AppCache etc.
   void CommitNavigation(
       NavigationRequest* navigation_request,
-      const mojom::CommonNavigationParams& common_params,
-      const mojom::CommitNavigationParams& commit_params,
+      mojom::CommonNavigationParamsPtr common_params,
+      mojom::CommitNavigationParamsPtr commit_params,
       network::ResourceResponse* response_head,
       mojo::ScopedDataPipeConsumerHandle response_body,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
index 76a5a5f..c2dd756 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_coding.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
@@ -204,7 +204,7 @@
       EncodeDouble(value.number(), into);
       DCHECK_EQ(9u, static_cast<size_t>(into->size() - previous_size));
       return;
-    case blink::mojom::IDBKeyType::Null:
+    case blink::mojom::IDBKeyType::None:
     case blink::mojom::IDBKeyType::Invalid:
     case blink::mojom::IDBKeyType::Min:
     default:
diff --git a/content/browser/navigation_subresource_loader_params.h b/content/browser/navigation_subresource_loader_params.h
index 6eb1951..0cc3345 100644
--- a/content/browser/navigation_subresource_loader_params.h
+++ b/content/browser/navigation_subresource_loader_params.h
@@ -7,7 +7,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
-#include "content/common/prefetched_signed_exchange_info.h"
+#include "content/common/prefetched_signed_exchange_info.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
 
@@ -50,7 +50,8 @@
   // navigation was served from the cache, |prefetched_signed_exchanges|
   // contains the all prefetched signed exchanges and they will be passed to the
   // renderer.
-  std::vector<PrefetchedSignedExchangeInfo> prefetched_signed_exchanges;
+  std::vector<mojom::PrefetchedSignedExchangeInfoPtr>
+      prefetched_signed_exchanges;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 0d15fe7..27e6b1fd 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -131,6 +131,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/storage_partition_impl.h"
+#include "content/browser/theme_helper.h"
 #include "content/browser/tracing/background_tracing_manager_impl.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/common/child_process.mojom.h"
@@ -4421,6 +4422,9 @@
     GetRendererInterface()->EnableV8LowMemoryMode();
   }
 
+  // Send the initial system color info to the renderer.
+  ThemeHelper::GetInstance()->SendSystemColorInfo(GetRendererInterface());
+
   // NOTE: This needs to be before flushing queued messages, because
   // ExtensionService uses this notification to initialize the renderer process
   // with state that must be there before any JavaScript executes.
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 4722a62..f540cadf 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -77,6 +77,10 @@
                              const std::string& highlight_color) override {
     NOTREACHED();
   }
+  void UpdateSystemColorInfo(
+      mojom::UpdateSystemColorInfoParamsPtr params) override {
+    NOTREACHED();
+  }
   void PurgePluginListCache(bool reload_pages) override { NOTREACHED(); }
   void SetProcessState(mojom::RenderProcessState process_state) override {
     NOTREACHED();
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc
index ff0c513..bb2e13a 100644
--- a/content/browser/sms/sms_browsertest.cc
+++ b/content/browser/sms/sms_browsertest.cc
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 #include "content/browser/browser_main_loop.h"
-#include "content/browser/sms/sms_provider.h"
 #include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/test/mock_sms_dialog.h"
+#include "content/browser/sms/test/mock_sms_provider.h"
+#include "content/browser/sms/test/mock_sms_web_contents_delegate.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/sms_dialog.h"
-#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -29,42 +29,6 @@
 
 namespace {
 
-class MockSmsDialog : public SmsDialog {
- public:
-  MockSmsDialog() : SmsDialog() {}
-  ~MockSmsDialog() override = default;
-
-  MOCK_METHOD3(Open,
-               void(RenderFrameHost*, base::OnceClosure, base::OnceClosure));
-  MOCK_METHOD0(Close, void());
-  MOCK_METHOD0(SmsReceived, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSmsDialog);
-};
-
-class MockWebContentsDelegate : public WebContentsDelegate {
- public:
-  MockWebContentsDelegate() = default;
-  ~MockWebContentsDelegate() override = default;
-
-  MOCK_METHOD0(CreateSmsDialog, std::unique_ptr<SmsDialog>());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockWebContentsDelegate);
-};
-
-class MockSmsProvider : public SmsProvider {
- public:
-  MockSmsProvider() = default;
-  ~MockSmsProvider() override = default;
-
-  MOCK_METHOD0(Retrieve, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSmsProvider);
-};
-
 class SmsBrowserTest : public ContentBrowserTest {
  public:
   SmsBrowserTest() = default;
@@ -92,7 +56,7 @@
     cert_verifier_.TearDownInProcessBrowserTestFixture();
   }
 
-  NiceMock<MockWebContentsDelegate> delegate_;
+  NiceMock<MockSmsWebContentsDelegate> delegate_;
   // Similar to net::MockCertVerifier, but also updates the CertVerifier
   // used by the NetworkService.
   ContentMockCertVerifier cert_verifier_;
@@ -474,7 +438,7 @@
   BrowserMainLoop::GetInstance()->SetSmsProviderForTesting(
       base::WrapUnique(provider));
 
-  StrictMock<MockWebContentsDelegate> delegate;
+  StrictMock<MockSmsWebContentsDelegate> delegate;
   shell()->web_contents()->SetDelegate(&delegate);
 
   auto* dialog = new StrictMock<MockSmsDialog>();
diff --git a/content/browser/sms/sms_parser.cc b/content/browser/sms/sms_parser.cc
index 156d24e..68d85ef 100644
--- a/content/browser/sms/sms_parser.cc
+++ b/content/browser/sms/sms_parser.cc
@@ -9,6 +9,7 @@
 #include "content/browser/sms/sms_parser.h"
 
 #include "base/optional.h"
+#include "net/base/url_util.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -28,7 +29,11 @@
 
   GURL gurl(url);
 
-  if (!gurl.is_valid() || !gurl.SchemeIs(url::kHttpsScheme)) {
+  if (!gurl.is_valid()) {
+    return base::nullopt;
+  }
+
+  if (!(gurl.SchemeIs(url::kHttpsScheme) || net::IsLocalhost(gurl))) {
     return base::nullopt;
   }
 
diff --git a/content/browser/sms/sms_parser_unittest.cc b/content/browser/sms/sms_parser_unittest.cc
index e8e811a..fa0f443 100644
--- a/content/browser/sms/sms_parser_unittest.cc
+++ b/content/browser/sms/sms_parser_unittest.cc
@@ -55,6 +55,16 @@
   ASSERT_EQ(origin, url::Origin::Create(url));
 }
 
+TEST(SmsParserTest, LocalhostForDevelopment) {
+  ASSERT_EQ(SmsParser::Parse("For: http://localhost:8080"),
+            url::Origin::Create(GURL("http://localhost:8080")));
+  ASSERT_EQ(SmsParser::Parse("For: http://localhost:80"),
+            url::Origin::Create(GURL("http://localhost:80")));
+  ASSERT_EQ(SmsParser::Parse("For: http://localhost"),
+            url::Origin::Create(GURL("http://localhost")));
+  ASSERT_FALSE(SmsParser::Parse("For: localhost"));
+}
+
 TEST(SmsParserTest, Paths) {
   base::Optional<url::Origin> origin =
       SmsParser::Parse("For: https://example.com/foobar");
@@ -177,6 +187,10 @@
   }
 }
 
+TEST(SmsParserTest, HarmlessOriginsButInvalid) {
+  ASSERT_FALSE(SmsParser::Parse("For: data://123"));
+}
+
 TEST(SmsParserTest, AppHash) {
   base::Optional<url::Origin> origin =
       SmsParser::Parse("<#> Hello World\nFor: https://example.com?s3LhKBB0M33");
diff --git a/content/browser/sms/sms_service_unittest.cc b/content/browser/sms/sms_service_unittest.cc
index 7a552dbf..2a81cebe 100644
--- a/content/browser/sms/sms_service_unittest.cc
+++ b/content/browser/sms/sms_service_unittest.cc
@@ -14,9 +14,10 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
-#include "content/browser/sms/sms_provider.h"
+#include "content/browser/sms/test/mock_sms_dialog.h"
+#include "content/browser/sms/test/mock_sms_provider.h"
+#include "content/browser/sms/test/mock_sms_web_contents_delegate.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
@@ -49,51 +50,10 @@
 
 class RenderFrameHost;
 
-using blink::mojom::SmsReceiverPtr;
-using ::testing::_;
-using ::testing::Invoke;
-using ::testing::NiceMock;
-
 namespace {
 
 const char kTestUrl[] = "https://www.google.com";
 
-class MockSmsProvider : public SmsProvider {
- public:
-  MockSmsProvider() = default;
-  ~MockSmsProvider() override = default;
-
-  MOCK_METHOD0(Retrieve, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSmsProvider);
-};
-
-class MockSmsDialog : public SmsDialog {
- public:
-  MockSmsDialog() : SmsDialog() {}
-  ~MockSmsDialog() override = default;
-
-  MOCK_METHOD3(Open,
-               void(RenderFrameHost*, base::OnceClosure, base::OnceClosure));
-  MOCK_METHOD0(Close, void());
-  MOCK_METHOD0(SmsReceived, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSmsDialog);
-};
-
-class MockWebContentsDelegate : public WebContentsDelegate {
- public:
-  MockWebContentsDelegate() = default;
-  ~MockWebContentsDelegate() override = default;
-
-  MOCK_METHOD0(CreateSmsDialog, std::unique_ptr<SmsDialog>());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockWebContentsDelegate);
-};
-
 // Service encapsulates a SmsService endpoint, with all of its dependencies
 // mocked out (and the common plumbing needed to inject them), and a
 // SmsReceiverPtr endpoint that tests can use to make requests.
@@ -115,9 +75,8 @@
       : Service(web_contents,
                 web_contents->GetMainFrame()->GetLastCommittedOrigin()) {}
 
-  NiceMock<MockWebContentsDelegate>* delegate() { return &delegate_; }
+  NiceMock<MockSmsWebContentsDelegate>* delegate() { return &delegate_; }
   NiceMock<MockSmsProvider>* provider() { return &provider_; }
-  blink::mojom::SmsReceiverPtr* client() { return &service_ptr_; }
 
   void SetupSmsDialog(content::RenderFrameHost* rfh) {
     auto* dialog = new NiceMock<MockSmsDialog>();
@@ -150,7 +109,7 @@
   }
 
  private:
-  NiceMock<MockWebContentsDelegate> delegate_;
+  NiceMock<MockSmsWebContentsDelegate> delegate_;
   NiceMock<MockSmsProvider> provider_;
   blink::mojom::SmsReceiverPtr service_ptr_;
   std::unique_ptr<SmsService> service_;
@@ -397,7 +356,7 @@
 TEST_F(SmsServiceTest, CleansUp) {
   NavigateAndCommit(GURL(kTestUrl));
 
-  NiceMock<MockWebContentsDelegate> delegate;
+  NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
       reinterpret_cast<WebContentsImpl*>(web_contents());
   web_contents_impl->SetDelegate(&delegate);
diff --git a/content/browser/sms/test/mock_sms_dialog.cc b/content/browser/sms/test/mock_sms_dialog.cc
new file mode 100644
index 0000000..a49b250
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_dialog.cc
@@ -0,0 +1,13 @@
+// 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/sms/test/mock_sms_dialog.h"
+
+namespace content {
+
+MockSmsDialog::MockSmsDialog() : SmsDialog() {}
+
+MockSmsDialog::~MockSmsDialog() = default;
+
+}  // namespace content
diff --git a/content/browser/sms/test/mock_sms_dialog.h b/content/browser/sms/test/mock_sms_dialog.h
new file mode 100644
index 0000000..8daf897
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_dialog.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 CONTENT_BROWSER_SMS_TEST_MOCK_SMS_DIALOG_H_
+#define CONTENT_BROWSER_SMS_TEST_MOCK_SMS_DIALOG_H_
+
+#include "base/macros.h"
+#include "content/public/browser/sms_dialog.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+class MockSmsDialog : public SmsDialog {
+ public:
+  MockSmsDialog();
+  ~MockSmsDialog() override;
+
+  MOCK_METHOD3(Open,
+               void(RenderFrameHost*, base::OnceClosure, base::OnceClosure));
+  MOCK_METHOD0(Close, void());
+  MOCK_METHOD0(SmsReceived, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSmsDialog);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_TEST_MOCK_SMS_DIALOG_H_
diff --git a/content/browser/sms/test/mock_sms_provider.cc b/content/browser/sms/test/mock_sms_provider.cc
new file mode 100644
index 0000000..0e7c8305
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_provider.cc
@@ -0,0 +1,13 @@
+// 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/sms/test/mock_sms_provider.h"
+
+namespace content {
+
+MockSmsProvider::MockSmsProvider() = default;
+
+MockSmsProvider::~MockSmsProvider() = default;
+
+}  // namespace content
diff --git a/content/browser/sms/test/mock_sms_provider.h b/content/browser/sms/test/mock_sms_provider.h
new file mode 100644
index 0000000..986222d
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_provider.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 CONTENT_BROWSER_SMS_TEST_MOCK_SMS_PROVIDER_H_
+#define CONTENT_BROWSER_SMS_TEST_MOCK_SMS_PROVIDER_H_
+
+#include "base/macros.h"
+#include "content/browser/sms/sms_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+class MockSmsProvider : public SmsProvider {
+ public:
+  MockSmsProvider();
+  ~MockSmsProvider() override;
+
+  MOCK_METHOD0(Retrieve, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSmsProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_TEST_MOCK_SMS_PROVIDER_H_
diff --git a/content/browser/sms/test/mock_sms_web_contents_delegate.cc b/content/browser/sms/test/mock_sms_web_contents_delegate.cc
new file mode 100644
index 0000000..e1453346
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_web_contents_delegate.cc
@@ -0,0 +1,13 @@
+// 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/sms/test/mock_sms_web_contents_delegate.h"
+
+namespace content {
+
+MockSmsWebContentsDelegate::MockSmsWebContentsDelegate() = default;
+
+MockSmsWebContentsDelegate::~MockSmsWebContentsDelegate() = default;
+
+}  // namespace content
diff --git a/content/browser/sms/test/mock_sms_web_contents_delegate.h b/content/browser/sms/test/mock_sms_web_contents_delegate.h
new file mode 100644
index 0000000..33d1812b
--- /dev/null
+++ b/content/browser/sms/test/mock_sms_web_contents_delegate.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_SMS_TEST_MOCK_SMS_WEB_CONTENTS_DELEGATE_H_
+#define CONTENT_BROWSER_SMS_TEST_MOCK_SMS_WEB_CONTENTS_DELEGATE_H_
+
+#include "base/macros.h"
+#include "content/public/browser/sms_dialog.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+class MockSmsWebContentsDelegate : public WebContentsDelegate {
+ public:
+  MockSmsWebContentsDelegate();
+  ~MockSmsWebContentsDelegate() override;
+
+  MOCK_METHOD0(CreateSmsDialog, std::unique_ptr<SmsDialog>());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSmsWebContentsDelegate);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_TEST_MOCK_SMS_WEB_CONTENTS_DELEGATE_H_
diff --git a/content/browser/theme_helper.cc b/content/browser/theme_helper.cc
new file mode 100644
index 0000000..817f085
--- /dev/null
+++ b/content/browser/theme_helper.cc
@@ -0,0 +1,57 @@
+// 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/theme_helper.h"
+
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/common/renderer.mojom.h"
+
+namespace content {
+
+// static
+ThemeHelper* ThemeHelper::GetInstance() {
+  static base::NoDestructor<ThemeHelper> s_theme_helper;
+  return s_theme_helper.get();
+}
+
+ThemeHelper::ThemeHelper() : theme_observer_(this) {
+  theme_observer_.Add(ui::NativeTheme::GetInstanceForWeb());
+}
+
+ThemeHelper::~ThemeHelper() {}
+
+mojom::UpdateSystemColorInfoParamsPtr MakeUpdateSystemColorInfoParams(
+    ui::NativeTheme* native_theme) {
+  mojom::UpdateSystemColorInfoParamsPtr params =
+      mojom::UpdateSystemColorInfoParams::New();
+  params->is_dark_mode = native_theme->ShouldUseDarkColors();
+  params->is_high_contrast = native_theme->UsesHighContrastColors();
+  params->preferred_color_scheme = native_theme->GetPreferredColorScheme();
+  const auto& colors = native_theme->GetSystemColors();
+  params->colors.insert(colors.begin(), colors.end());
+
+  return params;
+}
+
+void ThemeHelper::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
+  DCHECK(theme_observer_.IsObserving(observed_theme));
+
+  mojom::UpdateSystemColorInfoParamsPtr params =
+      MakeUpdateSystemColorInfoParams(observed_theme);
+  for (auto iter = RenderProcessHost::AllHostsIterator(); !iter.IsAtEnd();
+       iter.Advance()) {
+    if (iter.GetCurrentValue()->IsInitializedAndNotDead()) {
+      iter.GetCurrentValue()->GetRendererInterface()->UpdateSystemColorInfo(
+          params->Clone());
+    }
+  }
+}
+
+void ThemeHelper::SendSystemColorInfo(mojom::Renderer* renderer) const {
+  mojom::UpdateSystemColorInfoParamsPtr params =
+      MakeUpdateSystemColorInfoParams(ui::NativeTheme::GetInstanceForWeb());
+  renderer->UpdateSystemColorInfo(std::move(params));
+}
+
+}  // namespace content
diff --git a/content/browser/theme_helper.h b/content/browser/theme_helper.h
new file mode 100644
index 0000000..06fa6cc
--- /dev/null
+++ b/content/browser/theme_helper.h
@@ -0,0 +1,40 @@
+// 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_THEME_HELPER_H_
+#define CONTENT_BROWSER_THEME_HELPER_H_
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/scoped_observer.h"
+#include "content/common/renderer.mojom-forward.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/native_theme/native_theme_observer.h"
+
+namespace content {
+
+// This class is used to monitor system color info changes and to notify the
+// renderer processes.
+class ThemeHelper : public ui::NativeThemeObserver {
+ public:
+  static ThemeHelper* GetInstance();
+
+  void SendSystemColorInfo(mojom::Renderer* renderer) const;
+
+ private:
+  friend class base::NoDestructor<ThemeHelper>;
+  ThemeHelper();
+  ~ThemeHelper() override;
+
+  // Overridden from ui::NativeThemeObserver:
+  void OnNativeThemeUpdated(ui::NativeTheme* updated_theme) override;
+
+  ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver> theme_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThemeHelper);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_THEME_HELPER_H_
\ No newline at end of file
diff --git a/content/browser/web_package/prefetched_signed_exchange_cache.cc b/content/browser/web_package/prefetched_signed_exchange_cache.cc
index ae7abdd..0c4d6b9 100644
--- a/content/browser/web_package/prefetched_signed_exchange_cache.cc
+++ b/content/browser/web_package/prefetched_signed_exchange_cache.cc
@@ -378,7 +378,7 @@
  public:
   PrefetchedNavigationLoaderInterceptor(
       std::unique_ptr<const PrefetchedSignedExchangeCache::Entry> exchange,
-      std::vector<PrefetchedSignedExchangeInfo> info_list)
+      std::vector<mojom::PrefetchedSignedExchangeInfoPtr> info_list)
       : exchange_(std::move(exchange)), info_list_(std::move(info_list)) {}
 
   ~PrefetchedNavigationLoaderInterceptor() override {}
@@ -451,7 +451,7 @@
 
   State state_ = State::kInitial;
   std::unique_ptr<const PrefetchedSignedExchangeCache::Entry> exchange_;
-  std::vector<PrefetchedSignedExchangeInfo> info_list_;
+  std::vector<mojom::PrefetchedSignedExchangeInfoPtr> info_list_;
 
   base::WeakPtrFactory<PrefetchedNavigationLoaderInterceptor> weak_factory_{
       this};
@@ -700,7 +700,7 @@
                            headers_size_total);
 }
 
-std::vector<PrefetchedSignedExchangeInfo>
+std::vector<mojom::PrefetchedSignedExchangeInfoPtr>
 PrefetchedSignedExchangeCache::GetInfoListForNavigation(
     const Entry& main_exchange,
     const base::Time& now) {
@@ -712,7 +712,7 @@
       url::Origin::Create(main_exchange.inner_url());
   const auto inner_url_header_integrity_map = GetAllowedAltSXG(main_exchange);
 
-  std::vector<PrefetchedSignedExchangeInfo> info_list;
+  std::vector<mojom::PrefetchedSignedExchangeInfoPtr> info_list;
   EntryMap::iterator exchanges_it = exchanges_.begin();
   while (exchanges_it != exchanges_.end()) {
     const std::unique_ptr<const Entry>& exchange = exchanges_it->second;
@@ -735,10 +735,10 @@
       new SubresourceSignedExchangeURLLoaderFactory(
           mojo::MakeRequest(&loader_factory_info), exchange->Clone(),
           request_initiator_site_lock);
-      info_list.emplace_back(
+      info_list.emplace_back(mojom::PrefetchedSignedExchangeInfo::New(
           exchange->outer_url(), *exchange->header_integrity(),
           exchange->inner_url(), *exchange->inner_response(),
-          std::move(loader_factory_info).PassHandle().release());
+          std::move(loader_factory_info).PassHandle()));
     }
     ++exchanges_it;
   }
diff --git a/content/browser/web_package/prefetched_signed_exchange_cache.h b/content/browser/web_package/prefetched_signed_exchange_cache.h
index 41a5e0c..0887908 100644
--- a/content/browser/web_package/prefetched_signed_exchange_cache.h
+++ b/content/browser/web_package/prefetched_signed_exchange_cache.h
@@ -10,7 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
-#include "content/common/prefetched_signed_exchange_info.h"
+#include "content/common/prefetched_signed_exchange_info.mojom.h"
 #include "net/base/hash_value.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
@@ -122,7 +122,7 @@
   // |main_exchange|'s inner response and which outer URL's origin is same as
   // the origin of |main_exchange|'s outer URL. Note that this method erases
   // expired entries in |exchanges_|.
-  std::vector<PrefetchedSignedExchangeInfo> GetInfoListForNavigation(
+  std::vector<mojom::PrefetchedSignedExchangeInfoPtr> GetInfoListForNavigation(
       const Entry& main_exchange,
       const base::Time& now);
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 51303db..53d4fcf 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -138,10 +138,10 @@
     "input/input_event_ack.h",
     "input/input_event_ack_state.cc",
     "input/input_event_dispatch_type.h",
-    "input/input_event_stream_validator.cc",
-    "input/input_event_stream_validator.h",
     "input/input_event_mojom_traits.cc",
     "input/input_event_mojom_traits.h",
+    "input/input_event_stream_validator.cc",
+    "input/input_event_stream_validator.h",
     "input/input_param_traits.cc",
     "input/input_param_traits.h",
     "input/sync_compositor_messages.cc",
@@ -187,6 +187,7 @@
     "navigation_gesture.h",
     "navigation_params.cc",
     "navigation_params.h",
+    "navigation_params_mojom_traits.h",
     "navigation_params_utils.h",
     "net/record_load_histograms.cc",
     "net/record_load_histograms.h",
@@ -201,8 +202,6 @@
     "pepper_plugin_list.h",
     "pepper_renderer_instance_data.cc",
     "pepper_renderer_instance_data.h",
-    "prefetched_signed_exchange_info.cc",
-    "prefetched_signed_exchange_info.h",
     "process_type.cc",
     "render_widget_surface_properties.cc",
     "render_widget_surface_properties.h",
@@ -330,6 +329,7 @@
     "//ui/gl",
     "//ui/gl/init",
     "//ui/latency/ipc",
+    "//ui/native_theme",
     "//ui/shell_dialogs",
     "//url",
     "//url/ipc:url_ipc",
@@ -480,6 +480,7 @@
     "navigation_client.mojom",
     "navigation_params.mojom",
     "page_state.mojom",
+    "prefetched_signed_exchange_info.mojom",
     "render_frame_metadata.mojom",
     "render_message_filter.mojom",
     "renderer.mojom",
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 88cddca..d23817a8 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -466,14 +466,6 @@
   IPC_STRUCT_TRAITS_MEMBER(initiator_self_source)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(content::PrefetchedSignedExchangeInfo)
-  IPC_STRUCT_TRAITS_MEMBER(outer_url)
-  IPC_STRUCT_TRAITS_MEMBER(header_integrity)
-  IPC_STRUCT_TRAITS_MEMBER(inner_url)
-  IPC_STRUCT_TRAITS_MEMBER(inner_response)
-  IPC_STRUCT_TRAITS_MEMBER(loader_factory_handle)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(blink::ParsedFeaturePolicyDeclaration)
   IPC_STRUCT_TRAITS_MEMBER(feature)
   IPC_STRUCT_TRAITS_MEMBER(values)
diff --git a/content/common/native_types.mojom b/content/common/native_types.mojom
index a08d65b..445e9d1 100644
--- a/content/common/native_types.mojom
+++ b/content/common/native_types.mojom
@@ -96,3 +96,9 @@
 
 [Native]
 enum TouchAction;
+
+[Native]
+enum PreferredColorScheme;
+
+[Native]
+enum SystemThemeColor;
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap
index bef49a4f..19d80b6 100644
--- a/content/common/native_types.typemap
+++ b/content/common/native_types.typemap
@@ -29,6 +29,7 @@
   "//ui/events/blink/did_overscroll_params.h",
   "//ui/events/blink/web_input_event_traits.h",
   "//ui/latency/ipc/latency_info_param_traits.h",
+  "//ui/native_theme/native_theme.h",
 ]
 traits_headers = [
   "//content/common/frame_messages.h",
@@ -78,7 +79,9 @@
   "content.mojom.NetworkConnectionType=net::NetworkChangeNotifier::ConnectionType",
   "content.mojom.DidOverscrollParams=ui::DidOverscrollParams",
   "content.mojom.PointerType=blink::WebPointerProperties::PointerType",
+  "content.mojom.PreferredColorScheme=ui::NativeTheme::PreferredColorScheme",
   "content.mojom.VisualProperties=content::VisualProperties",
+  "content.mojom.SystemThemeColor=ui::NativeTheme::SystemThemeColor",
   "content.mojom.SyntheticSmoothDrag=content::SyntheticSmoothDragGestureParams",
   "content.mojom.SyntheticSmoothScroll=content::SyntheticSmoothScrollGestureParams",
   "content.mojom.SyntheticPinch=content::SyntheticPinchGestureParams",
diff --git a/content/common/navigation_params.h b/content/common/navigation_params.h
index 6c89ffb..9775680e 100644
--- a/content/common/navigation_params.h
+++ b/content/common/navigation_params.h
@@ -20,7 +20,7 @@
 #include "content/common/content_security_policy/content_security_policy.h"
 #include "content/common/content_security_policy/csp_disposition_enum.h"
 #include "content/common/navigation_params.mojom-forward.h"
-#include "content/common/prefetched_signed_exchange_info.h"
+#include "content/common/prefetched_signed_exchange_info.mojom.h"
 #include "content/public/common/navigation_policy.h"
 #include "content/public/common/page_state.h"
 #include "content/public/common/previews_state.h"
diff --git a/content/common/navigation_params.mojom b/content/common/navigation_params.mojom
index c0016a2d..bdcc940 100644
--- a/content/common/navigation_params.mojom
+++ b/content/common/navigation_params.mojom
@@ -4,6 +4,7 @@
 
 module content.mojom;
 
+import "content/common/prefetched_signed_exchange_info.mojom";
 import "content/public/common/was_activated_option.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
@@ -14,7 +15,6 @@
 import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
-
 [Native]
 struct InitiatorCSPInfo;
 
@@ -31,9 +31,6 @@
 enum PageTransition;
 
 [Native]
-struct PrefetchedSignedExchangeInfo;
-
-[Native]
 struct SourceLocation;
 
 enum NavigationType {
diff --git a/content/common/navigation_params.typemap b/content/common/navigation_params.typemap
index c3ab177..3067bad 100644
--- a/content/common/navigation_params.typemap
+++ b/content/common/navigation_params.typemap
@@ -4,8 +4,8 @@
 
 mojom = "//content/common/navigation_params.mojom"
 public_headers = [
+  "//content/common/content_param_traits.h",
   "//content/common/navigation_params.h",
-  "//content/common/prefetched_signed_exchange_info.h",
   "//content/public/common/page_state.h",
   "//content/public/common/navigation_policy.h",
   "//third_party/blink/public/platform/web_mixed_content_context_type.h",
@@ -24,6 +24,6 @@
   "content.mojom.NavigationDownloadPolicy=::content::NavigationDownloadPolicy",
   "content.mojom.PageState=::content::PageState",
   "content.mojom.PageTransition=::ui::PageTransition",
-  "content.mojom.PrefetchedSignedExchangeInfo=::content::PrefetchedSignedExchangeInfo",
+  "content.mojom.SHA256HashValue=::net::SHA256HashValue",
   "content.mojom.SourceLocation=::content::SourceLocation",
 ]
diff --git a/content/common/navigation_params_mojom_traits.h b/content/common/navigation_params_mojom_traits.h
new file mode 100644
index 0000000..b08f7758d
--- /dev/null
+++ b/content/common/navigation_params_mojom_traits.h
@@ -0,0 +1,26 @@
+// 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_COMMON_NAVIGATION_PARAMS_MOJOM_TRAITS_H_
+#define CONTENT_COMMON_NAVIGATION_PARAMS_MOJOM_TRAITS_H_
+
+#include "content/common/prefetched_signed_exchange_info.mojom.h"
+#include "mojo/public/cpp/bindings/clone_traits.h"
+
+namespace mojo {
+
+template <>
+struct CloneTraits<content::mojom::PrefetchedSignedExchangeInfoPtr, true> {
+  static content::mojom::PrefetchedSignedExchangeInfoPtr Clone(
+      const content::mojom::PrefetchedSignedExchangeInfoPtr& input) {
+    return content::mojom::PrefetchedSignedExchangeInfo::New(
+        mojo::Clone(input->outer_url), mojo::Clone(input->header_integrity),
+        mojo::Clone(input->inner_url), mojo::Clone(input->inner_response),
+        ScopedMessagePipeHandle());
+  }
+};
+
+}  // namespace mojo
+
+#endif  // CONTENT_COMMON_NAVIGATION_PARAMS_MOJOM_TRAITS_H_
diff --git a/content/common/prefetched_signed_exchange_info.cc b/content/common/prefetched_signed_exchange_info.cc
deleted file mode 100644
index 2f6fdca..0000000
--- a/content/common/prefetched_signed_exchange_info.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/common/prefetched_signed_exchange_info.h"
-
-namespace content {
-
-PrefetchedSignedExchangeInfo::PrefetchedSignedExchangeInfo() = default;
-PrefetchedSignedExchangeInfo::PrefetchedSignedExchangeInfo(
-    const PrefetchedSignedExchangeInfo&) = default;
-PrefetchedSignedExchangeInfo::PrefetchedSignedExchangeInfo(
-    const GURL& outer_url,
-    const net::SHA256HashValue& header_integrity,
-    const GURL& inner_url,
-    const network::ResourceResponseHead& inner_response,
-    mojo::MessagePipeHandle loader_factory_handle)
-    : outer_url(outer_url),
-      header_integrity(header_integrity),
-      inner_url(inner_url),
-      inner_response(inner_response),
-      loader_factory_handle(loader_factory_handle) {}
-PrefetchedSignedExchangeInfo::~PrefetchedSignedExchangeInfo() = default;
-
-}  // namespace content
diff --git a/content/common/prefetched_signed_exchange_info.h b/content/common/prefetched_signed_exchange_info.h
deleted file mode 100644
index 3555c09..0000000
--- a/content/common/prefetched_signed_exchange_info.h
+++ /dev/null
@@ -1,40 +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.
-
-#ifndef CONTENT_COMMON_PREFETCHED_SIGNED_EXCHANGE_INFO_H_
-#define CONTENT_COMMON_PREFETCHED_SIGNED_EXCHANGE_INFO_H_
-
-#include "content/common/content_export.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "net/base/hash_value.h"
-#include "services/network/public/cpp/resource_response.h"
-#include "url/gurl.h"
-
-namespace content {
-
-// Used for SignedExchangeSubresourcePrefetch.
-// This struct keeps the information about a prefetched signed exchange. It is
-// used in CommitNavigationParams to be passed from the browser process to the
-// renderer process in CommitNavigation IPC.
-struct CONTENT_EXPORT PrefetchedSignedExchangeInfo {
-  PrefetchedSignedExchangeInfo();
-  PrefetchedSignedExchangeInfo(const PrefetchedSignedExchangeInfo&);
-  PrefetchedSignedExchangeInfo(
-      const GURL& outer_url,
-      const net::SHA256HashValue& header_integrity,
-      const GURL& inner_url,
-      const network::ResourceResponseHead& inner_response,
-      mojo::MessagePipeHandle loader_factory_handle);
-  ~PrefetchedSignedExchangeInfo();
-
-  GURL outer_url;
-  net::SHA256HashValue header_integrity;
-  GURL inner_url;
-  network::ResourceResponseHead inner_response;
-  mojo::MessagePipeHandle loader_factory_handle;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_PREFETCHED_SIGNED_EXCHANGE_INFO_H_
diff --git a/content/common/prefetched_signed_exchange_info.mojom b/content/common/prefetched_signed_exchange_info.mojom
new file mode 100644
index 0000000..521eea5
--- /dev/null
+++ b/content/common/prefetched_signed_exchange_info.mojom
@@ -0,0 +1,23 @@
+// 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.
+
+module content.mojom;
+
+import "services/network/public/mojom/url_loader.mojom";
+import "url/mojom/url.mojom";
+
+[Native]
+struct SHA256HashValue;
+
+// Used for SignedExchangeSubresourcePrefetch.
+// This struct keeps the information about a prefetched signed exchange. It is
+// used in CommitNavigationParams to be passed from the browser process to the
+// renderer process in CommitNavigation IPC.
+struct PrefetchedSignedExchangeInfo {
+  url.mojom.Url outer_url;
+  SHA256HashValue header_integrity;
+  url.mojom.Url inner_url;
+  network.mojom.URLResponseHead inner_response;
+  handle<message_pipe> loader_factory_handle;
+};
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index cc97bb7..860f29a0 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -157,6 +157,14 @@
   bool scroll_view_rubber_banding;
 };
 
+struct UpdateSystemColorInfoParams {
+  bool is_dark_mode;
+  bool is_high_contrast;
+  PreferredColorScheme preferred_color_scheme;
+  // uint32 represents a SkColor in the map.
+  map<SystemThemeColor, uint32> colors;
+};
+
 enum RenderProcessState {
   kVisible,
   // Hidden render processes can still be foregrounded. For example, a hidden
@@ -244,6 +252,9 @@
   OnSystemColorsChanged(int32 aqua_color_variant, string highlight_text_color,
                         string highlight_color);
 
+  // Tells the renderer process the new system color info.
+  UpdateSystemColorInfo(UpdateSystemColorInfoParams params);
+
   // Tells the renderer to empty its plugin list cache, optional reloading
   // pages containing plugins.
   PurgePluginListCache(bool reload_pages);
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index d78e27e2..632b7b8 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -55,6 +55,7 @@
 #include "ui/gfx/ipc/color/gfx_param_traits.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
 #include "ui/gfx/ipc/skia/gfx_skia_param_traits.h"
+#include "ui/native_theme/native_theme.h"
 
 #if defined(OS_MACOSX)
 #include "third_party/blink/public/platform/mac/web_scrollbar_theme.h"
@@ -86,6 +87,11 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::ScrollerStyle, blink::kScrollerStyleOverlay)
 #endif
 
+IPC_ENUM_TRAITS_MAX_VALUE(ui::NativeTheme::PreferredColorScheme,
+                          ui::NativeTheme::PreferredColorScheme::kMaxValue)
+IPC_ENUM_TRAITS_MAX_VALUE(ui::NativeTheme::SystemThemeColor,
+                          ui::NativeTheme::SystemThemeColor::kMaxValue)
+
 IPC_STRUCT_TRAITS_BEGIN(blink::WebPluginAction)
   IPC_STRUCT_TRAITS_MEMBER(type)
   IPC_STRUCT_TRAITS_MEMBER(enable)
diff --git a/content/public/test/content_mock_cert_verifier.cc b/content/public/test/content_mock_cert_verifier.cc
index 8e2e379..3b25a01 100644
--- a/content/public/test/content_mock_cert_verifier.cc
+++ b/content/public/test/content_mock_cert_verifier.cc
@@ -68,7 +68,7 @@
 
 void ContentMockCertVerifier::CertVerifier::
     EnsureNetworkServiceTestInitialized() {
-  if (!network_service_test_) {
+  if (!network_service_test_ || network_service_test_.encountered_error()) {
     GetSystemConnector()->BindInterface(mojom::kNetworkServiceName,
                                         &network_service_test_);
   }
diff --git a/content/renderer/input/input_event_prediction_unittest.cc b/content/renderer/input/input_event_prediction_unittest.cc
index 2bd7889..621445b 100644
--- a/content/renderer/input/input_event_prediction_unittest.cc
+++ b/content/renderer/input/input_event_prediction_unittest.cc
@@ -108,9 +108,9 @@
 
   ConfigureFieldTrialAndInitialize(
       features::kResamplingInputEvents,
-      ui::input_prediction::kScrollPredictorNameKalmanTimeFiltered);
+      ui::input_prediction::kScrollPredictorNameKalman);
   EXPECT_EQ(event_predictor_->selected_predictor_type_,
-            PredictorType::kScrollPredictorTypeKalmanTimeFiltered);
+            PredictorType::kScrollPredictorTypeKalman);
 
   ConfigureFieldTrialAndInitialize(
       features::kResamplingInputEvents,
@@ -407,7 +407,7 @@
 TEST_F(InputEventPredictionTest, PredictedEventsTimeIntervalEqualRealEvents) {
   ConfigureFieldTrialAndInitialize(
       features::kResamplingInputEvents,
-      ui::input_prediction::kScrollPredictorNameKalmanTimeFiltered);
+      ui::input_prediction::kScrollPredictorNameKalman);
 
   base::TimeTicks event_time = ui::EventTimeForNow();
   // Send 3 mouse move each has 6ms interval to get kalman predictor ready.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index af69214..4b16625 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -59,6 +59,7 @@
 #include "content/common/input_messages.h"
 #include "content/common/navigation_gesture.h"
 #include "content/common/navigation_params.h"
+#include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/navigation_params_utils.h"
 #include "content/common/page_messages.h"
 #include "content/common/renderer_host.mojom.h"
@@ -498,17 +499,17 @@
     for (const auto& exchange : commit_params.prefetched_signed_exchanges) {
       blink::WebURLResponse web_response;
       WebURLLoaderImpl::PopulateURLResponse(
-          exchange.inner_url, exchange.inner_response, &web_response,
+          exchange->inner_url, exchange->inner_response, &web_response,
           false /* report_security_info*/, -1 /* request_id */);
       navigation_params->prefetched_signed_exchanges.emplace_back(
           std::make_unique<
               blink::WebNavigationParams::PrefetchedSignedExchange>(
-              exchange.outer_url,
+              exchange->outer_url,
               WebString::FromLatin1(
                   signed_exchange_utils::CreateHeaderIntegrityHashString(
-                      exchange.header_integrity)),
-              exchange.inner_url, web_response,
-              mojo::ScopedMessagePipeHandle(exchange.loader_factory_handle)));
+                      exchange->header_integrity)),
+              exchange->inner_url, web_response,
+              std::move(exchange->loader_factory_handle)));
     }
   }
 
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index db19505..4385beb5 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -19,6 +19,7 @@
 #include "build/build_config.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
+#include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/unfreezable_frame_messages.h"
 #include "content/common/widget_messages.h"
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 03f7b2c..bff56efdb 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2158,6 +2158,13 @@
 #endif
 }
 
+void RenderThreadImpl::UpdateSystemColorInfo(
+    mojom::UpdateSystemColorInfoParamsPtr params) {
+  ui::NativeTheme::GetInstanceForWeb()->UpdateSystemColorInfo(
+      params->is_dark_mode, params->is_high_contrast,
+      params->preferred_color_scheme, params->colors);
+}
+
 void RenderThreadImpl::PurgePluginListCache(bool reload_pages) {
 #if BUILDFLAG(ENABLE_PLUGINS)
   blink::ResetPluginCache(reload_pages);
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 1470459..8a22ef7 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -534,6 +534,8 @@
   void OnSystemColorsChanged(int32_t aqua_color_variant,
                              const std::string& highlight_text_color,
                              const std::string& highlight_color) override;
+  void UpdateSystemColorInfo(
+      mojom::UpdateSystemColorInfoParamsPtr params) override;
   void PurgePluginListCache(bool reload_pages) override;
   void SetProcessState(mojom::RenderProcessState process_state) override;
   void SetSchedulerKeepActive(bool keep_active) override;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 6139273..de997ab 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -669,7 +669,6 @@
   settings->SetJavaScriptCanAccessClipboard(
       prefs.javascript_can_access_clipboard);
   WebRuntimeFeatures::EnableXSLT(prefs.xslt_enabled);
-  settings->SetXSSAuditorEnabled(prefs.xss_auditor_enabled);
   settings->SetDNSPrefetchingEnabled(prefs.dns_prefetching_enabled);
   blink::WebNetworkStateNotifier::SetSaveDataEnabled(prefs.data_saver_enabled);
   settings->SetLocalStorageEnabled(prefs.local_storage_enabled);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 472569f4..5bf3288c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -76,6 +76,12 @@
     "../browser/service_worker/service_worker_test_utils.h",
     "../browser/service_worker/test_service_worker_observer.cc",
     "../browser/service_worker/test_service_worker_observer.h",
+    "../browser/sms/test/mock_sms_dialog.cc",
+    "../browser/sms/test/mock_sms_dialog.h",
+    "../browser/sms/test/mock_sms_provider.cc",
+    "../browser/sms/test/mock_sms_provider.h",
+    "../browser/sms/test/mock_sms_web_contents_delegate.cc",
+    "../browser/sms/test/mock_sms_web_contents_delegate.h",
     "../browser/web_package/mock_bundled_exchanges_reader_factory.cc",
     "../browser/web_package/mock_bundled_exchanges_reader_factory.h",
     "../browser/web_package/mock_signed_exchange_handler.cc",
diff --git a/docs/ui/android/night_mode.md b/docs/ui/android/night_mode.md
index 26ae1bd7..d6693be 100644
--- a/docs/ui/android/night_mode.md
+++ b/docs/ui/android/night_mode.md
@@ -144,7 +144,7 @@
 * Turn on power save mode (aka **battery saver**) on Android P+
 * Go to **Android Settings -> Developer options -> Night mode** on Android P
 * Go to **Android Settings -> Display -> Theme** on Android Q
-* [Set color scheme](https://cs.chromium.org/chromium/src/third_party/android_sdk/androidx_browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java?) to `COLOR_SCHEME_DARK` on creating a `CustomTabsIntent.Builder`
+* [Set color scheme](https://cs.chromium.org/chromium/src/third_party/custom_tabs_client/src/customtabs/src/android/support/customtabs/CustomTabsIntent.java?) to `COLOR_SCHEME_DARK` on creating a `CustomTabsIntent.Builder`
 
 Some tips:
 
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index 3b9bd04c..80b4f69 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -218,7 +218,7 @@
       base::Bind(&OvenHandler::HandleBakeDonuts, base::Unretained(this)));
 }
 
-void OverHandler::HandleBakeDonuts(const base::ListValue* args) {
+void OvenHandler::HandleBakeDonuts(const base::ListValue* args) {
   AllowJavascript();
 
   CHECK_EQ(1u, args->GetSize());
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 79ad0b7..3400986 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -619,6 +619,7 @@
     "extension_pref_value_map_unittest.cc",
     "extension_registrar_unittest.cc",
     "extension_registry_unittest.cc",
+    "extension_util_unittest.cc",
     "file_highlighter_unittest.cc",
     "file_reader_unittest.cc",
     "image_loader_unittest.cc",
diff --git a/extensions/browser/api/declarative_net_request/constants.cc b/extensions/browser/api/declarative_net_request/constants.cc
index 784ecb40fc..73a6f3e0 100644
--- a/extensions/browser/api/declarative_net_request/constants.cc
+++ b/extensions/browser/api/declarative_net_request/constants.cc
@@ -49,6 +49,9 @@
     "values are: [*].";
 const char kErrorQueryAndTransformBothSpecified[] =
     "Rule with id * cannot specify both \"*\" and \"*\" keys.";
+const char kErrorJavascriptRedirect[] =
+    "Rule with id * specifies an incorrect value for the \"*\" key. Redirects "
+    "to javascript urls are not supported.";
 const char kErrorListNotPassed[] = "Rules file must contain a list.";
 
 const char kRuleCountExceeded[] =
diff --git a/extensions/browser/api/declarative_net_request/constants.h b/extensions/browser/api/declarative_net_request/constants.h
index 9b83bd3..71a478a 100644
--- a/extensions/browser/api/declarative_net_request/constants.h
+++ b/extensions/browser/api/declarative_net_request/constants.h
@@ -44,6 +44,7 @@
   ERROR_INVALID_TRANSFORM_QUERY,
   ERROR_INVALID_TRANSFORM_FRAGMENT,
   ERROR_QUERY_AND_TRANSFORM_BOTH_SPECIFIED,
+  ERROR_JAVASCRIPT_REDIRECT,
 };
 
 // Describes the ways in which updating dynamic rules can fail.
@@ -107,6 +108,7 @@
 extern const char kErrorInvalidKey[];
 extern const char kErrorInvalidTransformScheme[];
 extern const char kErrorQueryAndTransformBothSpecified[];
+extern const char kErrorJavascriptRedirect[];
 
 extern const char kErrorListNotPassed[];
 
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index 8d8bf33..aa20994 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -16,6 +16,7 @@
 #include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/utils.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -314,9 +315,13 @@
   DCHECK(indexed_rule);
 
   if (redirect.url) {
-    if (!GURL(*redirect.url).is_valid())
+    GURL redirect_url = GURL(*redirect.url);
+    if (!redirect_url.is_valid())
       return ParseResult::ERROR_INVALID_REDIRECT_URL;
 
+    if (redirect_url.SchemeIs(url::kJavaScriptScheme))
+      return ParseResult::ERROR_JAVASCRIPT_REDIRECT;
+
     indexed_rule->redirect_url = std::move(*redirect.url);
     return ParseResult::SUCCESS;
   }
@@ -326,6 +331,10 @@
       return ParseResult::ERROR_INVALID_EXTENSION_PATH;
 
     GURL redirect_url = base_url.Resolve(*redirect.extension_path);
+
+    // Sanity check that Resolve works as expected.
+    DCHECK_EQ(base_url.GetOrigin(), redirect_url.GetOrigin());
+
     if (!redirect_url.is_valid())
       return ParseResult::ERROR_INVALID_EXTENSION_PATH;
 
@@ -373,8 +382,6 @@
   }
 
   if (is_redirect_rule) {
-    // TODO(crbug.com/983685): Prevent extensions from specifying redirects to
-    // Javascript urls.
     if (!parsed_rule.action.redirect)
       return ParseResult::ERROR_INVALID_REDIRECT;
 
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
index 4913da0d..353465f 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
@@ -505,6 +505,11 @@
       base::nullopt
     },
     {
+      R"({"url": "javascript:window.alert(\"hello,world\");"})",
+      ParseResult::ERROR_JAVASCRIPT_REDIRECT,
+      base::nullopt
+    },
+    {
       R"({"url": "http://google.com"})",
       ParseResult::SUCCESS,
       std::string("http://google.com")
diff --git a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
index 2ee019ff..e08e5832 100644
--- a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
@@ -125,7 +125,7 @@
   EXPECT_EQ(StripCommentsAndWhitespace(kFlatbufferSchemaExpected),
             StripCommentsAndWhitespace(flatbuffer_schema))
       << "Schema change detected; update this test and the schema version.";
-  EXPECT_EQ(9, GetIndexedRulesetFormatVersionForTesting())
+  EXPECT_EQ(10, GetIndexedRulesetFormatVersionForTesting())
       << "Update this test if you update the schema version.";
 }
 
diff --git a/extensions/browser/api/declarative_net_request/parse_info.cc b/extensions/browser/api/declarative_net_request/parse_info.cc
index a2e1e0b..802f3caa 100644
--- a/extensions/browser/api/declarative_net_request/parse_info.cc
+++ b/extensions/browser/api/declarative_net_request/parse_info.cc
@@ -150,6 +150,12 @@
       error = ErrorUtils::FormatErrorMessage(
           kErrorQueryAndTransformBothSpecified, base::NumberToString(*rule_id_),
           kTransformQueryPath, kTransformQueryTransformPath);
+      break;
+    case ParseResult::ERROR_JAVASCRIPT_REDIRECT:
+      error = ErrorUtils::FormatErrorMessage(kErrorJavascriptRedirect,
+                                             base::NumberToString(*rule_id_),
+                                             kRedirectUrlPath);
+      break;
   }
   return error;
 }
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
index 9e2cebc..8c08200 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
@@ -25,6 +25,7 @@
 #include "net/base/escape.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
+#include "url/url_constants.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -407,6 +408,12 @@
   else
     *redirect_url = GetTransformedURL(params, *metadata->transform());
 
+  // Sanity check that we don't redirect to a javascript url. Specifying
+  // redirect to a javascript url and specifying javascript as a transform
+  // scheme is prohibited. In addition extensions can't intercept requests to
+  // javascript urls. Hence we should never end up with a javascript url here.
+  DCHECK(!redirect_url->SchemeIs(url::kJavaScriptScheme));
+
   // Prevent a redirect loop where a URL continuously redirects to itself.
   return (redirect_url->is_valid() && *params.url != *redirect_url) ? rule
                                                                     : nullptr;
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index e73f3e4..393db78 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -33,7 +33,7 @@
 // url_pattern_index.fbs. Whenever an extension with an indexed ruleset format
 // version different from the one currently used by Chrome is loaded, the
 // extension ruleset will be reindexed.
-constexpr int kIndexedRulesetFormatVersion = 9;
+constexpr int kIndexedRulesetFormatVersion = 10;
 
 // This static assert is meant to catch cases where
 // url_pattern_index::kUrlPatternIndexFormatVersion is incremented without
diff --git a/extensions/browser/extension_util.cc b/extensions/browser/extension_util.cc
index 2ad8528f..6077d4e 100644
--- a/extensions/browser/extension_util.cc
+++ b/extensions/browser/extension_util.cc
@@ -13,6 +13,7 @@
 #include "extensions/common/features/feature_provider.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handlers/incognito_info.h"
+#include "extensions/common/manifest_handlers/shared_module_info.h"
 
 namespace extensions {
 namespace util {
@@ -68,5 +69,64 @@
   return storage_partition;
 }
 
+// This function is security sensitive. Bugs could cause problems that break
+// restrictions on local file access or NaCl's validation caching. If you modify
+// this function, please get a security review from a NaCl person.
+bool MapUrlToLocalFilePath(const ExtensionSet* extensions,
+                           const GURL& file_url,
+                           bool use_blocking_api,
+                           base::FilePath* file_path) {
+  // Check that the URL is recognized by the extension system.
+  const Extension* extension = extensions->GetExtensionOrAppByURL(file_url);
+  if (!extension)
+    return false;
+
+  // This is a short-cut which avoids calling a blocking file operation
+  // (GetFilePath()), so that this can be called on the non blocking threads. It
+  // only handles a subset of the urls.
+  if (!use_blocking_api) {
+    if (file_url.SchemeIs(extensions::kExtensionScheme)) {
+      std::string path = file_url.path();
+      base::TrimString(path, "/", &path);  // Remove first slash
+      *file_path = extension->path().AppendASCII(path);
+      return true;
+    }
+    return false;
+  }
+
+  std::string path = file_url.path();
+  ExtensionResource resource;
+
+  if (SharedModuleInfo::IsImportedPath(path)) {
+    // Check if this is a valid path that is imported for this extension.
+    std::string new_extension_id;
+    std::string new_relative_path;
+    SharedModuleInfo::ParseImportedPath(path, &new_extension_id,
+                                        &new_relative_path);
+    const Extension* new_extension = extensions->GetByID(new_extension_id);
+    if (!new_extension)
+      return false;
+
+    if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id))
+      return false;
+
+    resource = new_extension->GetResource(new_relative_path);
+  } else {
+    // Check that the URL references a resource in the extension.
+    resource = extension->GetResource(path);
+  }
+
+  if (resource.empty())
+    return false;
+
+  // GetFilePath is a blocking function call.
+  const base::FilePath resource_file_path = resource.GetFilePath();
+  if (resource_file_path.empty())
+    return false;
+
+  *file_path = resource_file_path;
+  return true;
+}
+
 }  // namespace util
 }  // namespace extensions
diff --git a/extensions/browser/extension_util.h b/extensions/browser/extension_util.h
index 05fc35f8..d8138ed 100644
--- a/extensions/browser/extension_util.h
+++ b/extensions/browser/extension_util.h
@@ -9,6 +9,10 @@
 
 #include "url/gurl.h"
 
+namespace base {
+class FilePath;
+}
+
 namespace content {
 class BrowserContext;
 class StoragePartition;
@@ -16,6 +20,7 @@
 
 namespace extensions {
 class Extension;
+class ExtensionSet;
 
 namespace util {
 
@@ -44,6 +49,17 @@
     const std::string& extension_id,
     content::BrowserContext* browser_context);
 
+// Maps a |file_url| to a |file_path| on the local filesystem, including
+// resources in extensions. Returns true on success. See NaClBrowserDelegate for
+// full details. If |use_blocking_api| is false, only a subset of URLs will be
+// handled. If |use_blocking_api| is true, blocking file operations may be used,
+// and this must be called on threads that allow blocking. Otherwise this can be
+// called on any thread.
+bool MapUrlToLocalFilePath(const ExtensionSet* extensions,
+                           const GURL& file_url,
+                           bool use_blocking_api,
+                           base::FilePath* file_path);
+
 }  // namespace util
 }  // namespace extensions
 
diff --git a/extensions/browser/extension_util_unittest.cc b/extensions/browser/extension_util_unittest.cc
new file mode 100644
index 0000000..6efa6e7
--- /dev/null
+++ b/extensions/browser/extension_util_unittest.cc
@@ -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.
+
+#include "extensions/browser/extension_util.h"
+
+#include "base/path_service.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/extension_paths.h"
+#include "extensions/common/extension_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace {
+// Returns a barebones test Extension object with the given name.
+static scoped_refptr<const Extension> CreateExtension(const std::string& name) {
+  base::FilePath path;
+  base::PathService::Get(DIR_TEST_DATA, &path);
+
+  return ExtensionBuilder(name).SetPath(path.AppendASCII(name)).Build();
+}
+}  // namespace
+
+// Tests that extension URLs are properly mapped to local file paths.
+TEST(ExtensionUtilTest, MapUrlToLocalFilePath) {
+  scoped_refptr<const Extension> app(CreateExtension("platform_app"));
+  ExtensionSet extensions;
+  extensions.Insert(app);
+
+  // Non-extension URLs don't map to anything.
+  base::FilePath non_extension_path;
+  GURL non_extension_url("http://not-an-extension.com/");
+  EXPECT_FALSE(util::MapUrlToLocalFilePath(&extensions, non_extension_url,
+                                           false, &non_extension_path));
+  EXPECT_TRUE(non_extension_path.empty());
+
+  // Valid resources return a valid path.
+  base::FilePath valid_path;
+  GURL valid_url = app->GetResourceURL("manifest.json");
+  EXPECT_TRUE(util::MapUrlToLocalFilePath(
+      &extensions, valid_url, true /* use_blocking_api */, &valid_path));
+  EXPECT_FALSE(valid_path.empty());
+
+  // A file must exist to be mapped to a path using the blocking API.
+  base::FilePath does_not_exist_path;
+  GURL does_not_exist_url = app->GetResourceURL("does-not-exist.html");
+  EXPECT_FALSE(util::MapUrlToLocalFilePath(&extensions, does_not_exist_url,
+                                           true /* use_blocking_api */,
+                                           &does_not_exist_path));
+  EXPECT_TRUE(does_not_exist_path.empty());
+
+  // A file does not need to exist to be mapped to a path with the non-blocking
+  // API. This avoids hitting the disk to see if it exists.
+  EXPECT_TRUE(util::MapUrlToLocalFilePath(&extensions, does_not_exist_url,
+                                          false /* use_blocking_api */,
+                                          &does_not_exist_path));
+  EXPECT_FALSE(does_not_exist_path.empty());
+}
+}  // namespace extensions
diff --git a/extensions/browser/info_map.cc b/extensions/browser/info_map.cc
index 39c077a..497e73e4 100644
--- a/extensions/browser/info_map.cc
+++ b/extensions/browser/info_map.cc
@@ -12,7 +12,6 @@
 #include "extensions/common/extension_resource.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest_handlers/incognito_info.h"
-#include "extensions/common/manifest_handlers/shared_module_info.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "url/gurl.h"
 
@@ -138,64 +137,6 @@
   process_map_.RemoveAllFromProcess(process_id);
 }
 
-// This function is security sensitive. Bugs could cause problems that break
-// restrictions on local file access or NaCl's validation caching. If you modify
-// this function, please get a security review from a NaCl person.
-bool InfoMap::MapUrlToLocalFilePath(const GURL& file_url,
-                                    bool use_blocking_api,
-                                    base::FilePath* file_path) {
-  // Check that the URL is recognized by the extension system.
-  const Extension* extension = extensions_.GetExtensionOrAppByURL(file_url);
-  if (!extension)
-    return false;
-
-  // This is a short-cut which avoids calling a blocking file operation
-  // (GetFilePath()), so that this can be called on the IO thread. It only
-  // handles a subset of the urls.
-  if (!use_blocking_api) {
-    if (file_url.SchemeIs(extensions::kExtensionScheme)) {
-      std::string path = file_url.path();
-      base::TrimString(path, "/", &path);  // Remove first slash
-      *file_path = extension->path().AppendASCII(path);
-      return true;
-    }
-    return false;
-  }
-
-  std::string path = file_url.path();
-  ExtensionResource resource;
-
-  if (SharedModuleInfo::IsImportedPath(path)) {
-    // Check if this is a valid path that is imported for this extension.
-    std::string new_extension_id;
-    std::string new_relative_path;
-    SharedModuleInfo::ParseImportedPath(
-        path, &new_extension_id, &new_relative_path);
-    const Extension* new_extension = extensions_.GetByID(new_extension_id);
-    if (!new_extension)
-      return false;
-
-    if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id))
-      return false;
-
-    resource = new_extension->GetResource(new_relative_path);
-  } else {
-    // Check that the URL references a resource in the extension.
-    resource = extension->GetResource(path);
-  }
-
-  if (resource.empty())
-    return false;
-
-  // GetFilePath is a blocking function call.
-  const base::FilePath resource_file_path = resource.GetFilePath();
-  if (resource_file_path.empty())
-    return false;
-
-  *file_path = resource_file_path;
-  return true;
-}
-
 QuotaService* InfoMap::GetQuotaService() {
   CheckOnValidThread();
   if (!quota_service_)
diff --git a/extensions/browser/info_map.h b/extensions/browser/info_map.h
index 414ceba..e56bf890 100644
--- a/extensions/browser/info_map.h
+++ b/extensions/browser/info_map.h
@@ -16,10 +16,6 @@
 #include "extensions/common/extension_set.h"
 #include "extensions/common/permissions/api_permission.h"
 
-namespace base {
-class FilePath;
-}
-
 namespace extensions {
 class ContentVerifier;
 class Extension;
@@ -72,13 +68,6 @@
                                   int site_instance_id);
   void UnregisterAllExtensionsInProcess(int process_id);
 
-  // Maps a |file_url| to a |file_path| on the local filesystem, including
-  // resources in extensions. Returns true on success. See NaClBrowserDelegate
-  // for full details.
-  bool MapUrlToLocalFilePath(const GURL& file_url,
-                             bool use_blocking_api,
-                             base::FilePath* file_path);
-
   // Returns the IO thread QuotaService. Creates the instance on first call.
   QuotaService* GetQuotaService();
 
diff --git a/extensions/browser/info_map_unittest.cc b/extensions/browser/info_map_unittest.cc
index 5a8d6e0..23fd5641 100644
--- a/extensions/browser/info_map_unittest.cc
+++ b/extensions/browser/info_map_unittest.cc
@@ -79,38 +79,4 @@
   EXPECT_EQ(extension2.get(), info_map->extensions().GetByID(extension2->id()));
 }
 
-// Tests that extension URLs are properly mapped to local file paths.
-TEST_F(InfoMapTest, MapUrlToLocalFilePath) {
-  scoped_refptr<InfoMap> info_map(new InfoMap());
-  scoped_refptr<const Extension> app(CreateExtension("platform_app"));
-  info_map->AddExtension(app.get(), base::Time(), false, false);
-
-  // Non-extension URLs don't map to anything.
-  base::FilePath non_extension_path;
-  GURL non_extension_url("http://not-an-extension.com/");
-  EXPECT_FALSE(info_map->MapUrlToLocalFilePath(
-      non_extension_url, false, &non_extension_path));
-  EXPECT_TRUE(non_extension_path.empty());
-
-  // Valid resources return a valid path.
-  base::FilePath valid_path;
-  GURL valid_url = app->GetResourceURL("manifest.json");
-  EXPECT_TRUE(info_map->MapUrlToLocalFilePath(
-      valid_url, true /* use_blocking_api */, &valid_path));
-  EXPECT_FALSE(valid_path.empty());
-
-  // A file must exist to be mapped to a path using the blocking API.
-  base::FilePath does_not_exist_path;
-  GURL does_not_exist_url = app->GetResourceURL("does-not-exist.html");
-  EXPECT_FALSE(info_map->MapUrlToLocalFilePath(
-      does_not_exist_url, true /* use_blocking_api */, &does_not_exist_path));
-  EXPECT_TRUE(does_not_exist_path.empty());
-
-  // A file does not need to exist to be mapped to a path with the non-blocking
-  // API. This avoids hitting the disk to see if it exists.
-  EXPECT_TRUE(info_map->MapUrlToLocalFilePath(
-      does_not_exist_url, false /* use_blocking_api */, &does_not_exist_path));
-  EXPECT_FALSE(does_not_exist_path.empty());
-}
-
 }  // namespace extensions
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index f255773..8f7e388 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -72,7 +72,8 @@
 
   // Describes modification to various url components.
   dictionary URLTransform {
-     // The new scheme for the request.
+     // The new scheme for the request. Allowed values are "http", "https",
+     // "ftp" and "chrome-extension".
      DOMString? scheme;
 
      // The new host for the request.
@@ -107,7 +108,7 @@
      DOMString? extensionPath;
      // Url transformations to perform.
      URLTransform? transform;
-     // The redirect url.
+     // The redirect url. Redirects to JavaScript urls are not allowed.
      DOMString? url;
   };
 
diff --git a/extensions/shell/browser/shell_nacl_browser_delegate.cc b/extensions/shell/browser/shell_nacl_browser_delegate.cc
index ed4cfbc..5416340 100644
--- a/extensions/shell/browser/shell_nacl_browser_delegate.cc
+++ b/extensions/shell/browser/shell_nacl_browser_delegate.cc
@@ -17,8 +17,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/site_instance.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/browser/info_map.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -109,47 +109,49 @@
 // person before you modify it.
 // TODO(jamescook): Refactor this code into the extensions module so it can
 // be shared with Chrome's NaClBrowserDelegateImpl. http://crbug.com/403017
-bool ShellNaClBrowserDelegate::MapUrlToLocalFilePath(
-    const GURL& file_url,
-    bool use_blocking_api,
-    const base::FilePath& profile_directory,
-    base::FilePath* file_path) {
-  ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
-  DCHECK(extension_system);
+NaClBrowserDelegate::MapUrlToLocalFilePathCallback
+ShellNaClBrowserDelegate::GetMapUrlToLocalFilePathCallback(
+    const base::FilePath& profile_directory) {
+  auto extensions = std::make_unique<ExtensionSet>();
+  extensions->InsertAll(
+      ExtensionRegistry::Get(browser_context_)->enabled_extensions());
+  return base::BindRepeating(
+      [](const ExtensionSet* extensions, const GURL& file_url,
+         bool use_blocking_api, base::FilePath* file_path) {
+        // Check that the URL is recognized by the extension system.
+        const Extension* extension =
+            extensions->GetExtensionOrAppByURL(file_url);
+        if (!extension)
+          return false;
 
-  // Check that the URL is recognized by the extension system.
-  const Extension* extension =
-      extension_system->info_map()->extensions().GetExtensionOrAppByURL(
-          file_url);
-  if (!extension)
-    return false;
+        // This is a short-cut which avoids calling a blocking file operation
+        // (GetFilePath()), so that this can be called on the IO thread. It only
+        // handles a subset of the urls.
+        if (!use_blocking_api) {
+          if (file_url.SchemeIs(kExtensionScheme)) {
+            std::string path = file_url.path();
+            base::TrimString(path, "/", &path);  // Remove first slash
+            *file_path = extension->path().AppendASCII(path);
+            return true;
+          }
+          return false;
+        }
 
-  // This is a short-cut which avoids calling a blocking file operation
-  // (GetFilePath()), so that this can be called on the IO thread. It only
-  // handles a subset of the urls.
-  if (!use_blocking_api) {
-    if (file_url.SchemeIs(kExtensionScheme)) {
-      std::string path = file_url.path();
-      base::TrimString(path, "/", &path);  // Remove first slash
-      *file_path = extension->path().AppendASCII(path);
-      return true;
-    }
-    return false;
-  }
+        // Check that the URL references a resource in the extension.
+        // NOTE: app_shell does not support shared modules.
+        ExtensionResource resource = extension->GetResource(file_url.path());
+        if (resource.empty())
+          return false;
 
-  // Check that the URL references a resource in the extension.
-  // NOTE: app_shell does not support shared modules.
-  ExtensionResource resource = extension->GetResource(file_url.path());
-  if (resource.empty())
-    return false;
+        // GetFilePath is a blocking function call.
+        const base::FilePath resource_file_path = resource.GetFilePath();
+        if (resource_file_path.empty())
+          return false;
 
-  // GetFilePath is a blocking function call.
-  const base::FilePath resource_file_path = resource.GetFilePath();
-  if (resource_file_path.empty())
-    return false;
-
-  *file_path = resource_file_path;
-  return true;
+        *file_path = resource_file_path;
+        return true;
+      },
+      base::Owned(std::move(extensions)));
 }
 
 bool ShellNaClBrowserDelegate::IsNonSfiModeAllowed(
diff --git a/extensions/shell/browser/shell_nacl_browser_delegate.h b/extensions/shell/browser/shell_nacl_browser_delegate.h
index 80907be..2a12e48 100644
--- a/extensions/shell/browser/shell_nacl_browser_delegate.h
+++ b/extensions/shell/browser/shell_nacl_browser_delegate.h
@@ -35,10 +35,8 @@
   std::string GetVersionString() const override;
   ppapi::host::HostFactory* CreatePpapiHostFactory(
       content::BrowserPpapiHost* ppapi_host) override;
-  bool MapUrlToLocalFilePath(const GURL& url,
-                             bool is_blocking,
-                             const base::FilePath& profile_directory,
-                             base::FilePath* file_path) override;
+  MapUrlToLocalFilePathCallback GetMapUrlToLocalFilePathCallback(
+      const base::FilePath& profile_directory) override;
   void SetDebugPatterns(const std::string& debug_patterns) override;
   bool URLMatchesDebugPatterns(const GURL& manifest_url) override;
   bool IsNonSfiModeAllowed(const base::FilePath& profile_directory,
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn
index 56841a8..6044a73 100644
--- a/fuchsia/BUILD.gn
+++ b/fuchsia/BUILD.gn
@@ -14,7 +14,6 @@
     "fidl/cast/api_bindings.fidl",
     "fidl/cast/application_config.fidl",
     "fidl/cast/application_controller.fidl",
-    "fidl/cast/queryable_data.fidl",
   ]
 
   public_deps = [
diff --git a/fuchsia/fidl/cast/queryable_data.fidl b/fuchsia/fidl/cast/queryable_data.fidl
deleted file mode 100644
index c7148a139..0000000
--- a/fuchsia/fidl/cast/queryable_data.fidl
+++ /dev/null
@@ -1,22 +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.
-
-library chromium.cast;
-
-struct QueryableDataEntry {
-  string key;
-  string json_value;
-};
-
-/// Interface for serving configuration- and environmental-related values
-/// to CastRunner instances.
-[Discoverable]
-protocol QueryableData {
-  /// Gets a list of changed entries since the last call to GetChangedEntries,
-  /// delaying execution of the the pending callback until a change becomes
-  /// available. The initial call will return all entries managed by the
-  /// QueryableData service.
-  GetChangedEntries() -> (vector<QueryableDataEntry> values);
-};
-
diff --git a/fuchsia/runners/cast/fake_queryable_data.cc b/fuchsia/runners/cast/fake_queryable_data.cc
deleted file mode 100644
index 4f3758d..0000000
--- a/fuchsia/runners/cast/fake_queryable_data.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "fuchsia/runners/cast/fake_queryable_data.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/json/json_writer.h"
-
-FakeQueryableData::FakeQueryableData() = default;
-
-FakeQueryableData::~FakeQueryableData() = default;
-
-void FakeQueryableData::SendChanges(
-    std::vector<std::pair<base::StringPiece, base::Value&&>> changes) {
-  for (const auto& change : changes) {
-    std::string value_json;
-    CHECK(base::JSONWriter::Write(change.second, &value_json));
-    changes_.push_back({change.first.as_string(), value_json});
-  }
-
-  if (get_changed_callback_)
-    DeliverChanges();
-}
-
-void FakeQueryableData::GetChangedEntries(GetChangedEntriesCallback callback) {
-  DCHECK(!get_changed_callback_);
-  get_changed_callback_ = std::move(callback);
-
-  if (!changes_.empty())
-    DeliverChanges();
-}
-
-void FakeQueryableData::DeliverChanges() {
-  (*get_changed_callback_)(std::move(changes_));
-  get_changed_callback_ = base::nullopt;
-}
diff --git a/fuchsia/runners/cast/fake_queryable_data.h b/fuchsia/runners/cast/fake_queryable_data.h
deleted file mode 100644
index 1c3f14d3..0000000
--- a/fuchsia/runners/cast/fake_queryable_data.h
+++ /dev/null
@@ -1,41 +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.
-
-#ifndef FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_
-#define FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/strings/string_piece.h"
-#include "base/values.h"
-#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
-
-// A simple STL map-backed implementation of QueryableData, for testing
-// purposes.
-class FakeQueryableData : public chromium::cast::QueryableData {
- public:
-  FakeQueryableData();
-  ~FakeQueryableData() override;
-
-  // Sends, or prepares to send, a set of changed values to the page.
-  void SendChanges(
-      std::vector<std::pair<base::StringPiece, base::Value&&>> changes);
-
- private:
-  void DeliverChanges();
-
-  // chromium::cast::QueryableData implementation.
-  void GetChangedEntries(GetChangedEntriesCallback callback) override;
-
-  base::Optional<GetChangedEntriesCallback> get_changed_callback_;
-  std::vector<chromium::cast::QueryableDataEntry> changes_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeQueryableData);
-};
-
-#endif  // FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_
diff --git a/ios/chrome/app/application_delegate/browser_launcher.h b/ios/chrome/app/application_delegate/browser_launcher.h
index 6e1be2b..23e3f02e 100644
--- a/ios/chrome/app/application_delegate/browser_launcher.h
+++ b/ios/chrome/app/application_delegate/browser_launcher.h
@@ -34,7 +34,9 @@
 // Browser view information created during startup.
 @property(nonatomic, readonly) id<BrowserInterfaceProvider> interfaceProvider;
 
-// Initializes the application up to |stage|.
+// Initializes the application up to |stage|. This is safe to call multiple
+// times for the same value of |stage|; the actions for each stage will only
+// be run once during the lifetime of the app.
 - (void)startUpBrowserToStage:(BrowserInitializationStageType)stage;
 
 @end
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index 537c166..bf2747f 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -173,6 +173,10 @@
   if ([_appState isInSafeMode])
     return NO;
 
+  // Enusre Chrome is fuilly started up in case it had launched to the
+  // background.
+  [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+
   return
       [UserActivityHandler willContinueUserActivityWithType:userActivityType];
 }
@@ -184,6 +188,10 @@
   if ([_appState isInSafeMode])
     return NO;
 
+  // Enusre Chrome is fuilly started up in case it had launched to the
+  // background.
+  [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+
   BOOL applicationIsActive =
       [application applicationState] == UIApplicationStateActive;
 
@@ -199,6 +207,10 @@
   if ([_appState isInSafeMode])
     return;
 
+  // Enusre Chrome is fuilly started up in case it had launched to the
+  // background.
+  [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+
   [UserActivityHandler
       performActionForShortcutItem:shortcutItem
                  completionHandler:completionHandler
@@ -219,6 +231,13 @@
   if ([_appState isInSafeMode])
     return NO;
 
+  // The various URL handling mechanisms require that the application has
+  // fully started up; there are some cases (crbug.com/658420) where a
+  // launch via this method crashes because some services (specifically,
+  // CommandLine) aren't initialized yet. So: before anything further is
+  // done, make sure that Chrome is fully started up.
+  [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+
   if (ios::GetChromeBrowserProvider()
           ->GetChromeIdentityService()
           ->HandleApplicationOpenURL(application, url, options)) {
diff --git a/ios/chrome/app/memory_monitor.mm b/ios/chrome/app/memory_monitor.mm
index c989736..5b99c4c 100644
--- a/ios/chrome/app/memory_monitor.mm
+++ b/ios/chrome/app/memory_monitor.mm
@@ -37,7 +37,7 @@
       static_cast<int>(base::SysInfo::AmountOfAvailablePhysicalMemory() / 1024);
   breakpad_helper::SetCurrentFreeMemoryInKB(free_memory);
 
-  NSURL* fileURL = [[NSURL alloc] initFileURLWithPath:@"/"];
+  NSURL* fileURL = [[NSURL alloc] initFileURLWithPath:NSHomeDirectory()];
   NSDictionary* results = [fileURL resourceValuesForKeys:@[
     NSURLVolumeAvailableCapacityForImportantUsageKey
   ]
diff --git a/ios/chrome/browser/overlays/BUILD.gn b/ios/chrome/browser/overlays/BUILD.gn
index 0985ace..22c60786 100644
--- a/ios/chrome/browser/overlays/BUILD.gn
+++ b/ios/chrome/browser/overlays/BUILD.gn
@@ -6,10 +6,12 @@
   public = [
     "public/overlay_dismissal_callback.h",
     "public/overlay_modality.h",
+    "public/overlay_presentation_callback.h",
     "public/overlay_presentation_context.h",
     "public/overlay_presentation_context_observer.h",
     "public/overlay_presenter.h",
     "public/overlay_presenter_observer.h",
+    "public/overlay_presenter_observer_bridge.h",
     "public/overlay_request.h",
     "public/overlay_request_queue.h",
     "public/overlay_response.h",
@@ -18,6 +20,7 @@
   sources = [
     "overlay_presenter_impl.h",
     "overlay_presenter_impl.mm",
+    "overlay_presenter_observer_bridge.mm",
     "overlay_request_impl.cc",
     "overlay_request_impl.h",
     "overlay_request_queue_impl.h",
@@ -42,6 +45,7 @@
   testonly = true
   sources = [
     "overlay_presenter_impl_unittest.mm",
+    "overlay_presenter_observer_bridge_unittest.mm",
     "overlay_request_impl_unittest.cc",
     "overlay_request_queue_impl_unittest.mm",
     "overlay_request_unittest.cc",
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.h b/ios/chrome/browser/overlays/overlay_presenter_impl.h
index 7f3110c..8365b4d 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.h
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.h
@@ -79,6 +79,13 @@
   // only be called when |presenting_| is false.
   void PresentOverlayForActiveRequest();
 
+  // Notifies this object that the UI for |request| has finished being
+  // presented in |presentation_context|.  This function is called when the
+  // OverlayPresentationCallback provided to the presentation context is
+  // executed.
+  void OverlayWasPresented(OverlayPresentationContext* presentation_context,
+                           OverlayRequest* request);
+
   // Notifies this object that the UI for |request| has finished being dismissed
   // in |presentation_context| in for |reason|.  |queue| is |request|'s queue.
   // This function is called when the OverlayDismissalCallback provided to
diff --git a/ios/chrome/browser/overlays/overlay_presenter_impl.mm b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
index 94f7fbb..a8f09d3 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_presenter_impl.mm
@@ -200,13 +200,27 @@
   }
 
   // Present the overlay UI via the UI delegate.
+  OverlayPresentationCallback presentation_callback = base::BindOnce(
+      &OverlayPresenterImpl::OverlayWasPresented, weak_factory_.GetWeakPtr(),
+      presentation_context_, request);
   OverlayDismissalCallback dismissal_callback = base::BindOnce(
       &OverlayPresenterImpl::OverlayWasDismissed, weak_factory_.GetWeakPtr(),
       presentation_context_, request, GetActiveQueue()->GetWeakPtr());
   presentation_context_->ShowOverlayUI(this, request,
+                                       std::move(presentation_callback),
                                        std::move(dismissal_callback));
 }
 
+void OverlayPresenterImpl::OverlayWasPresented(
+    OverlayPresentationContext* presentation_context,
+    OverlayRequest* request) {
+  DCHECK_EQ(presentation_context_, presentation_context);
+  DCHECK_EQ(GetActiveRequest(), request);
+  for (auto& observer : observers_) {
+    observer.DidShowOverlay(this, request);
+  }
+}
+
 void OverlayPresenterImpl::OverlayWasDismissed(
     OverlayPresentationContext* presentation_context,
     OverlayRequest* request,
diff --git a/ios/chrome/browser/overlays/overlay_presenter_observer_bridge.mm b/ios/chrome/browser/overlays/overlay_presenter_observer_bridge.mm
new file mode 100644
index 0000000..c68610e
--- /dev/null
+++ b/ios/chrome/browser/overlays/overlay_presenter_observer_bridge.mm
@@ -0,0 +1,51 @@
+// 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/overlays/public/overlay_presenter_observer_bridge.h"
+
+#include "base/logging.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OverlayPresenterObserverBridge::OverlayPresenterObserverBridge(
+    id<OverlayPresenterObserving> observer)
+    : observer_(observer) {
+  DCHECK(observer_);
+}
+
+OverlayPresenterObserverBridge::~OverlayPresenterObserverBridge() = default;
+
+void OverlayPresenterObserverBridge::WillShowOverlay(
+    OverlayPresenter* presenter,
+    OverlayRequest* request) {
+  if ([observer_ respondsToSelector:@selector(overlayPresenter:
+                                        willShowOverlayForRequest:)]) {
+    [observer_ overlayPresenter:presenter willShowOverlayForRequest:request];
+  }
+}
+
+void OverlayPresenterObserverBridge::DidShowOverlay(OverlayPresenter* presenter,
+                                                    OverlayRequest* request) {
+  if ([observer_ respondsToSelector:@selector(overlayPresenter:
+                                        didShowOverlayForRequest:)]) {
+    [observer_ overlayPresenter:presenter didShowOverlayForRequest:request];
+  }
+}
+
+void OverlayPresenterObserverBridge::DidHideOverlay(OverlayPresenter* presenter,
+                                                    OverlayRequest* request) {
+  if ([observer_ respondsToSelector:@selector(overlayPresenter:
+                                        didHideOverlayForRequest:)]) {
+    [observer_ overlayPresenter:presenter didHideOverlayForRequest:request];
+  }
+}
+
+void OverlayPresenterObserverBridge::OverlayPresenterDestroyed(
+    OverlayPresenter* presenter) {
+  if ([observer_ respondsToSelector:@selector(overlayPresenterDestroyed:)]) {
+    [observer_ overlayPresenterDestroyed:presenter];
+  }
+}
diff --git a/ios/chrome/browser/overlays/overlay_presenter_observer_bridge_unittest.mm b/ios/chrome/browser/overlays/overlay_presenter_observer_bridge_unittest.mm
new file mode 100644
index 0000000..c36b612
--- /dev/null
+++ b/ios/chrome/browser/overlays/overlay_presenter_observer_bridge_unittest.mm
@@ -0,0 +1,90 @@
+// 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/overlays/public/overlay_presenter_observer_bridge.h"
+
+#import <Foundation/Foundation.h>
+
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Fake implementation of OverlayPresenterObserving that records whether the
+// callbacks are executed.
+@interface FakeOverlayPresenterObserver : NSObject <OverlayPresenterObserving>
+// Whether each of the OverlayPresenterObserving callbacks have been called.
+@property(nonatomic, readonly) BOOL willShowCalled;
+@property(nonatomic, readonly) BOOL didShowCalled;
+@property(nonatomic, readonly) BOOL didHideCalled;
+@property(nonatomic, readonly) BOOL destroyedCalled;
+@end
+
+@implementation FakeOverlayPresenterObserver
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    willShowOverlayForRequest:(OverlayRequest*)request {
+  _willShowCalled = YES;
+}
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didShowOverlayForRequest:(OverlayRequest*)request {
+  _didShowCalled = YES;
+}
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didHideOverlayForRequest:(OverlayRequest*)request {
+  _didHideCalled = YES;
+}
+
+- (void)overlayPresenterDestroyed:(OverlayPresenter*)presenter {
+  _destroyedCalled = YES;
+}
+
+@end
+
+// Test fixture for OverlayPresenterObserverBridge.
+class OverlayPresenterObserverBridgeTest : public PlatformTest {
+ public:
+  OverlayPresenterObserverBridgeTest()
+      : observer_([[FakeOverlayPresenterObserver alloc] init]),
+        bridge_(observer_) {}
+
+ protected:
+  FakeOverlayPresenterObserver* observer_;
+  OverlayPresenterObserverBridge bridge_;
+};
+
+// Tests that OverlayPresenterObserver::WillShowOverlay() is correctly
+// forwarded.
+TEST_F(OverlayPresenterObserverBridgeTest, WillShowCalled) {
+  ASSERT_FALSE(observer_.willShowCalled);
+  bridge_.WillShowOverlay(nullptr, nullptr);
+  EXPECT_TRUE(observer_.willShowCalled);
+}
+
+// Tests that OverlayPresenterObserver::DidShowOverlay() is correctly
+// forwarded.
+TEST_F(OverlayPresenterObserverBridgeTest, DidShowCalled) {
+  ASSERT_FALSE(observer_.didShowCalled);
+  bridge_.DidShowOverlay(nullptr, nullptr);
+  EXPECT_TRUE(observer_.didShowCalled);
+}
+
+// Tests that OverlayPresenterObserver::DidHideOverlay() is correctly
+// forwarded.
+TEST_F(OverlayPresenterObserverBridgeTest, DidHideCalled) {
+  ASSERT_FALSE(observer_.didHideCalled);
+  bridge_.DidHideOverlay(nullptr, nullptr);
+  EXPECT_TRUE(observer_.didHideCalled);
+}
+
+// Tests that OverlayPresenterObserver::OverlayPresenterDestroyed() is correctly
+// forwarded.
+TEST_F(OverlayPresenterObserverBridgeTest, DestroyedCalled) {
+  ASSERT_FALSE(observer_.destroyedCalled);
+  bridge_.OverlayPresenterDestroyed(nullptr);
+  EXPECT_TRUE(observer_.destroyedCalled);
+}
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_callback.h b/ios/chrome/browser/overlays/public/overlay_presentation_callback.h
new file mode 100644
index 0000000..e30a632
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_callback.h
@@ -0,0 +1,15 @@
+// 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_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CALLBACK_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CALLBACK_H_
+
+#include "base/callback.h"
+
+// Overlay UI presented by OverlayPresenter::Delegate are provided with an
+// OverlayPresentationCallback that is used to notify the presenter when
+// requested overlay UI has finished being presented.
+typedef base::OnceCallback<void()> OverlayPresentationCallback;
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CALLBACK_H_
diff --git a/ios/chrome/browser/overlays/public/overlay_presentation_context.h b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
index 4ac8c4b..f6342b4 100644
--- a/ios/chrome/browser/overlays/public/overlay_presentation_context.h
+++ b/ios/chrome/browser/overlays/public/overlay_presentation_context.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTATION_CONTEXT_H_
 
 #include "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
+#include "ios/chrome/browser/overlays/public/overlay_presentation_callback.h"
 
 class OverlayPresenter;
 class OverlayRequest;
@@ -26,10 +27,12 @@
   virtual bool IsActive() const = 0;
 
   // Called by |presenter| to show the overlay UI for |request|.
-  // |dismissal_callback| must be stored and called whenever the UI is finished
-  // being dismissed for user interaction, hiding, or cancellation.
+  // |presentation_callback| must be called when the UI is finished being
+  // presented. |dismissal_callback| must be stored and called whenever the UI
+  // is finished being dismissed for user interaction, hiding, or cancellation.
   virtual void ShowOverlayUI(OverlayPresenter* presenter,
                              OverlayRequest* request,
+                             OverlayPresentationCallback presentation_callback,
                              OverlayDismissalCallback dismissal_callback) = 0;
 
   // Called by |presenter| to hide the overlay UI for |request|.  Hidden
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter_observer.h b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
index e4bcf81..5c6f35e 100644
--- a/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
+++ b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
@@ -20,6 +20,11 @@
   virtual void WillShowOverlay(OverlayPresenter* presenter,
                                OverlayRequest* request) {}
 
+  // Called when |presenter| has finished showing the overlay UI for
+  // |request|.
+  virtual void DidShowOverlay(OverlayPresenter* presenter,
+                              OverlayRequest* request) {}
+
   // Called when |presenter| is finished dismissing its overlay UI.
   virtual void DidHideOverlay(OverlayPresenter* presenter,
                               OverlayRequest* request) {}
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h b/ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h
new file mode 100644
index 0000000..d4a9067
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_BRIDGE_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_BRIDGE_H_
+
+#import <Foundation/Foundation.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "ios/chrome/browser/overlays/public/overlay_presenter_observer.h"
+
+// Observes overlay UI presentation events from Objective-C. To use as an
+// OverlayPresenterObserver, wrap in an OverlayPresenterObserverBridge.
+@protocol OverlayPresenterObserving <NSObject>
+@optional
+
+// Invoked by OverlayPresenterObserver::WillShowOverlay().
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    willShowOverlayForRequest:(OverlayRequest*)request;
+
+// Invoked by OverlayPresenterObserver::DidShowOverlay().
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didShowOverlayForRequest:(OverlayRequest*)request;
+
+// Invoked by OverlayPresenterObserver::DidHideOverlay().
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didHideOverlayForRequest:(OverlayRequest*)request;
+
+// Invoked by OverlayPresenterObserver::OverlayPresenterDestroyed().
+- (void)overlayPresenterDestroyed:(OverlayPresenter*)presenter;
+
+@end
+
+// C++ wrapper that forwards OverlayPresenterObserver callbacks to an Objective-
+// C object conforming to OverlayPresenterObserving.
+class OverlayPresenterObserverBridge : public OverlayPresenterObserver {
+ public:
+  // It it the responsibility of calling code to add/remove the instance
+  // from OverlayPresenter's observer list.
+  OverlayPresenterObserverBridge(id<OverlayPresenterObserving> observer);
+  ~OverlayPresenterObserverBridge() override;
+
+  // OverlayPresenterObserver:
+  void WillShowOverlay(OverlayPresenter* presenter,
+                       OverlayRequest* request) override;
+  void DidShowOverlay(OverlayPresenter* presenter,
+                      OverlayRequest* request) override;
+  void DidHideOverlay(OverlayPresenter* presenter,
+                      OverlayRequest* request) override;
+  void OverlayPresenterDestroyed(OverlayPresenter* presenter) override;
+
+ private:
+  __weak id<OverlayPresenterObserving> observer_ = nil;
+
+  DISALLOW_COPY_AND_ASSIGN(OverlayPresenterObserverBridge);
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_PRESENTER_OBSERVER_BRIDGE_H_
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
index 52ede06..550f77d 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.cc
@@ -14,25 +14,26 @@
 
 FakeOverlayPresentationContext::PresentationState
 FakeOverlayPresentationContext::GetPresentationState(OverlayRequest* request) {
-  return presentation_states_[request];
+  return states_[request].presentation_state;
 }
 
 void FakeOverlayPresentationContext::SimulateDismissalForRequest(
     OverlayRequest* request,
     OverlayDismissalReason reason) {
-  DCHECK_EQ(PresentationState::kPresented, presentation_states_[request]);
+  FakeUIState& state = states_[request];
+  DCHECK_EQ(PresentationState::kPresented, state.presentation_state);
   switch (reason) {
     case OverlayDismissalReason::kUserInteraction:
-      presentation_states_[request] = PresentationState::kUserDismissed;
+      state.presentation_state = PresentationState::kUserDismissed;
       break;
     case OverlayDismissalReason::kHiding:
-      presentation_states_[request] = PresentationState::kHidden;
+      state.presentation_state = PresentationState::kHidden;
       break;
     case OverlayDismissalReason::kCancellation:
-      presentation_states_[request] = PresentationState::kCancelled;
+      state.presentation_state = PresentationState::kCancelled;
       break;
   }
-  std::move(overlay_callbacks_[request]).Run(reason);
+  std::move(state.dismissal_callback).Run(reason);
 }
 
 void FakeOverlayPresentationContext::SetIsActive(bool active) {
@@ -65,9 +66,12 @@
 void FakeOverlayPresentationContext::ShowOverlayUI(
     OverlayPresenter* presenter,
     OverlayRequest* request,
+    OverlayPresentationCallback presentation_callback,
     OverlayDismissalCallback dismissal_callback) {
-  presentation_states_[request] = PresentationState::kPresented;
-  overlay_callbacks_[request] = std::move(dismissal_callback);
+  FakeUIState& state = states_[request];
+  state.presentation_state = PresentationState::kPresented;
+  state.presentation_callback = std::move(presentation_callback);
+  state.dismissal_callback = std::move(dismissal_callback);
 }
 
 void FakeOverlayPresentationContext::HideOverlayUI(OverlayPresenter* presenter,
@@ -78,10 +82,14 @@
 void FakeOverlayPresentationContext::CancelOverlayUI(
     OverlayPresenter* presenter,
     OverlayRequest* request) {
-  PresentationState& state = presentation_states_[request];
-  if (state == PresentationState::kPresented) {
+  FakeUIState& state = states_[request];
+  if (state.presentation_state == PresentationState::kPresented) {
     SimulateDismissalForRequest(request, OverlayDismissalReason::kCancellation);
   } else {
-    state = PresentationState::kCancelled;
+    state.presentation_state = PresentationState::kCancelled;
   }
 }
+
+FakeOverlayPresentationContext::FakeUIState::FakeUIState() = default;
+
+FakeOverlayPresentationContext::FakeUIState::~FakeUIState() = default;
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
index b159253..0806f99 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
+++ b/ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h
@@ -46,6 +46,7 @@
   bool IsActive() const override;
   void ShowOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request,
+                     OverlayPresentationCallback presentation_callback,
                      OverlayDismissalCallback dismissal_callback) override;
   void HideOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request) override;
@@ -53,10 +54,18 @@
                        OverlayRequest* request) override;
 
  private:
-  // The presentation states for each OverlayRequest.
-  std::map<OverlayRequest*, PresentationState> presentation_states_;
-  // The callbacks for each OverlayRequest.
-  std::map<OverlayRequest*, OverlayDismissalCallback> overlay_callbacks_;
+  // Struct used to store state for the fake presentation context.
+  struct FakeUIState {
+    FakeUIState();
+    ~FakeUIState();
+
+    PresentationState presentation_state = PresentationState::kNotPresented;
+    OverlayPresentationCallback presentation_callback;
+    OverlayDismissalCallback dismissal_callback;
+  };
+
+  // The UI states for each request.
+  std::map<OverlayRequest*, FakeUIState> states_;
   // Whether the context is active.
   bool active_ = true;
 
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.h b/ios/chrome/browser/providers/chromium_browser_provider.h
index f333e12..a80ca906 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.h
+++ b/ios/chrome/browser/providers/chromium_browser_provider.h
@@ -18,6 +18,7 @@
   void SetChromeIdentityServiceForTesting(
       std::unique_ptr<ios::ChromeIdentityService> service) override;
   ios::ChromeIdentityService* GetChromeIdentityService() override;
+  UITextField* CreateStyledTextField() const override NS_RETURNS_RETAINED;
   UITextField<TextFieldStyling>* CreateStyledTextField(
       CGRect frame) const override NS_RETURNS_RETAINED;
   VoiceSearchProvider* GetVoiceSearchProvider() const override;
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.mm b/ios/chrome/browser/providers/chromium_browser_provider.mm
index 62599c5..341a97c 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.mm
+++ b/ios/chrome/browser/providers/chromium_browser_provider.mm
@@ -61,6 +61,10 @@
   return chrome_identity_service_.get();
 }
 
+UITextField* ChromiumBrowserProvider::CreateStyledTextField() const {
+  return [[UITextField alloc] initWithFrame:CGRectZero];
+}
+
 UITextField<TextFieldStyling>* ChromiumBrowserProvider::CreateStyledTextField(
     CGRect frame) const {
   return [[ChromiumStyledTextField alloc] initWithFrame:CGRectZero];
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
index b6bc375..d121a08e 100644
--- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
+++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
@@ -361,10 +361,8 @@
   }
 
   if ([self inputExpirationIsValid:item]) {
-    item.showDateInputError = NO;
     item.errorMessage = @"";
   } else {
-    item.showDateInputError = NO;
     item.errorMessage = l10n_util::GetNSString(
         IDS_AUTOFILL_CARD_UNMASK_INVALID_EXPIRATION_DATE);
   }
@@ -436,7 +434,6 @@
   _CVCItem.errorMessage = @"";
   _CVCItem.showDateInput = YES;
   _CVCItem.showNewCardButton = NO;
-  _CVCItem.showDateInputError = NO;
   _CVCItem.showCVCInputError = NO;
 
   [self reconfigureCellsForItems:@[ _CVCItem ]];
diff --git a/ios/chrome/browser/ui/autofill/cells/cvc_item.h b/ios/chrome/browser/ui/autofill/cells/cvc_item.h
index d1bf8295..953a8f2 100644
--- a/ios/chrome/browser/ui/autofill/cells/cvc_item.h
+++ b/ios/chrome/browser/ui/autofill/cells/cvc_item.h
@@ -10,15 +10,13 @@
 #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"
 
-@protocol TextFieldStyling;
-
 // Item corresponding to a CVCCell.
 @interface CVCItem : CollectionViewItem
 
 // The instructions text to display.
 @property(nonatomic, copy) NSString* instructionsText;
 
-// The optioonal error message to display.
+// The optional error message to display.
 @property(nonatomic, copy) NSString* errorMessage;
 
 // The month text appearing in the |monthInput| of the cell, if |showDateInput|
@@ -38,9 +36,6 @@
 // Whether the cell should show the "New Card?" button.
 @property(nonatomic, assign) BOOL showNewCardButton;
 
-// Whether the date inputs contain erroneous data.
-@property(nonatomic, assign) BOOL showDateInputError;
-
 // Whether the CVC input contains erroneous data.
 @property(nonatomic, assign) BOOL showCVCInputError;
 
@@ -65,14 +60,13 @@
 @property(nonatomic, readonly, strong) UILabel* errorLabel;
 
 // The text field for entering the month.
-@property(nonatomic, readonly, strong)
-    UITextField<TextFieldStyling>* monthInput;
+@property(nonatomic, readonly, strong) UITextField* monthInput;
 
 // The text field for entering the year.
-@property(nonatomic, readonly, strong) UITextField<TextFieldStyling>* yearInput;
+@property(nonatomic, readonly, strong) UITextField* yearInput;
 
 // The text field for entering the CVC.
-@property(nonatomic, readonly, strong) UITextField<TextFieldStyling>* CVCInput;
+@property(nonatomic, readonly, strong) UITextField* CVCInput;
 
 // The image view to display the CVC image with the |CVCResourceID|.
 @property(nonatomic, readonly, strong) UIImageView* CVCImageView;
diff --git a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
index a26e6c0..5311d77 100644
--- a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
@@ -9,7 +9,6 @@
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
-#import "ios/public/provider/chrome/browser/ui/text_field_styling.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.h"
@@ -52,7 +51,6 @@
 @synthesize CVCText = _CVCText;
 @synthesize showDateInput = _showDateInput;
 @synthesize showNewCardButton = _showNewCardButton;
-@synthesize showDateInputError = _showDateInputError;
 @synthesize showCVCInputError = _showCVCInputError;
 @synthesize CVCImageResourceID = _CVCImageResourceID;
 
@@ -80,10 +78,6 @@
   cell.CVCContainerLeadingConstraintWithDate.active = self.showDateInput;
   cell.CVCContainerLeadingConstraintWithoutDate.active = !self.showDateInput;
 
-  [cell.monthInput setUseErrorStyling:self.showDateInputError];
-  [cell.yearInput setUseErrorStyling:self.showDateInputError];
-  [cell.CVCInput setUseErrorStyling:self.showDateInputError];
-
   cell.buttonForNewCard.hidden = !self.showNewCardButton;
 
   cell.CVCImageView.image = NativeImage(self.CVCImageResourceID);
@@ -316,9 +310,6 @@
   self.dateContainerView.hidden = YES;
   self.CVCContainerLeadingConstraintWithDate.active = NO;
   self.CVCContainerLeadingConstraintWithoutDate.active = YES;
-  [self.monthInput setUseErrorStyling:NO];
-  [self.yearInput setUseErrorStyling:NO];
-  [self.CVCInput setUseErrorStyling:NO];
   self.buttonForNewCard.hidden = YES;
   self.CVCImageView.image = nil;
 }
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.mm
index 973c133..97e17dd 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.mm
@@ -39,7 +39,6 @@
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
-#import "ios/public/provider/chrome/browser/ui/text_field_styling.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/gfx/image/image.h"
 #include "url/gurl.h"
@@ -79,10 +78,9 @@
 const CGFloat kEstimatedTableSectionFooterHeight = 40;
 }  // namespace
 
-@interface BookmarkEditViewController ()<BookmarkFolderViewControllerDelegate,
-                                         BookmarkModelBridgeObserver,
-                                         BookmarkTextFieldItemDelegate,
-                                         TextFieldValidation> {
+@interface BookmarkEditViewController () <BookmarkFolderViewControllerDelegate,
+                                          BookmarkModelBridgeObserver,
+                                          BookmarkTextFieldItemDelegate> {
   // Flag to ignore bookmark model changes notifications.
   BOOL _ignoresBookmarkModelChanges;
 
@@ -499,17 +497,6 @@
   return YES;
 }
 
-#pragma mark - TextFieldValidation
-
-- (NSString*)validationErrorForTextField:(id<TextFieldStyling>)field {
-  [self updateSaveButtonState];
-  if ([self inputURLIsValid]) {
-    return nil;
-  } else {
-    return l10n_util::GetNSString(IDS_IOS_BOOKMARK_URL_FIELD_VALIDATION_FAILED);
-  }
-}
-
 #pragma mark - UITableViewDataSource
 
 - (UITableViewCell*)tableView:(UITableView*)tableView
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 3d8ad0b..d936060 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -31,7 +31,6 @@
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/accessibility_util.h"
@@ -208,7 +207,7 @@
   [BookmarksTestCase assertBookmarksWithTitle:bookmarkTitle expectedCount:1];
 
   // Verify the star is lit.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     [[EarlGrey
         selectElementWithMatcher:grey_accessibilityLabel(
                                      l10n_util::GetNSString(IDS_TOOLTIP_STAR))]
@@ -216,7 +215,7 @@
   }
 
   // Open the BookmarkEditor.
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     [ChromeEarlGreyUI openToolsMenu];
     [[[EarlGrey
         selectElementWithMatcher:grey_allOf(grey_accessibilityID(
@@ -242,7 +241,7 @@
   [BookmarksTestCase assertBookmarksWithTitle:bookmarkTitle expectedCount:0];
 
   // Verify the the page is no longer bookmarked.
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     [ChromeEarlGreyUI openToolsMenu];
     [[[EarlGrey
         selectElementWithMatcher:grey_allOf(grey_accessibilityID(
@@ -350,7 +349,7 @@
       performAction:grey_tap()];
 
   // Edit the bookmark.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     [[EarlGrey selectElementWithMatcher:StarButton()] performAction:grey_tap()];
   } else {
     [ChromeEarlGreyUI openToolsMenu];
@@ -1750,7 +1749,7 @@
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
   // meantime.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad.");
   }
 
@@ -1800,7 +1799,7 @@
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
   // meantime.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad on iOS11.");
   }
 
@@ -3425,7 +3424,7 @@
 
   // Dismiss the context menu. On non compact width tap the Bookmarks TableView
   // to dismiss, since there might not be a cancel button.
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     [[EarlGrey
         selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_CANCEL)]
         performAction:grey_tap()];
@@ -4456,7 +4455,7 @@
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
   // meantime.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad on iOS11.");
   }
 
@@ -4484,7 +4483,7 @@
   // grey_swipeFastInDirectionWithStartPoint doesn't work either and it might
   // fail on devices. Disabling this test under these conditions on the
   // meantime.
-  if (!IsCompactWidth()) {
+  if (![ChromeEarlGrey isCompactWidth]) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iPad on iOS11.");
   }
 
diff --git a/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h b/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h
index 87cae9ad..69d5a17 100644
--- a/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h
+++ b/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h
@@ -10,7 +10,6 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
 
 @class BookmarkTextFieldItem;
-@protocol TextFieldStyling;
 
 // Delegates the cell's text field's events.
 @protocol BookmarkTextFieldItemDelegate<UITextFieldDelegate>
diff --git a/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.mm b/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.mm
index cf9729b..3c9d893 100644
--- a/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.mm
+++ b/ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
-#import "ios/public/provider/chrome/browser/ui/text_field_styling.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index c7b76ab..547fb50 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -134,7 +134,6 @@
     "//ios/chrome/browser/ui/overscroll_actions",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/public",
-    "//ios/chrome/browser/ui/toolbar/resources:tab_toolbar_shadow_color",
     "//ios/chrome/browser/ui/util:util",
     "//ios/chrome/common/colors",
     "//ios/chrome/common/favicon",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
index 7888b6ad..be312777 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -252,8 +252,7 @@
   DCHECK(searchField.superview == self);
 
   self.separator = [[UIView alloc] init];
-  self.separator.backgroundColor =
-      [UIColor colorNamed:@"tab_toolbar_shadow_color"];
+  self.separator.backgroundColor = [UIColor colorNamed:kToolbarShadowColor];
   self.separator.alpha = 0;
   self.separator.translatesAutoresizingMaskIntoConstraints = NO;
   [searchField addSubview:self.separator];
diff --git a/ios/chrome/browser/ui/history/history_entry_item.mm b/ios/chrome/browser/ui/history/history_entry_item.mm
index bc3d7ac..33f38d0 100644
--- a/ios/chrome/browser/ui/history/history_entry_item.mm
+++ b/ios/chrome/browser/ui/history/history_entry_item.mm
@@ -56,10 +56,6 @@
   cell.titleLabel.text = self.text;
   cell.URLLabel.text = self.detailText;
   cell.metadataLabel.text = self.timeText;
-  cell.faviconContainerView.backgroundColor = styler.tableViewBackgroundColor;
-  cell.titleLabel.backgroundColor = styler.tableViewBackgroundColor;
-  cell.URLLabel.backgroundColor = styler.tableViewBackgroundColor;
-  cell.metadataLabel.backgroundColor = styler.tableViewBackgroundColor;
   cell.isAccessibilityElement = YES;
   cell.accessibilityCustomActions = self.accessibilityActions;
   [cell configureUILayout];
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index e6a3946c..9a23ed2 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -60,8 +60,6 @@
     "//ios/chrome/browser/ui/omnibox/popup",
     "//ios/chrome/browser/ui/orchestrator:orchestrator",
     "//ios/chrome/browser/ui/toolbar/buttons",
-    "//ios/chrome/browser/ui/toolbar/buttons/resources:tab_toolbar_button_color",
-    "//ios/chrome/browser/ui/toolbar/buttons/resources:tab_toolbar_button_color_incognito",
     "//ios/chrome/browser/ui/toolbar/keyboard_assist:keyboard_assist",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
index 2df04ea..56a52771 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
@@ -34,15 +34,6 @@
 // changes. (This is usually when the default search engine changes).
 - (void)updateSearchByImageSupported:(BOOL)searchByImageSupported;
 
-// Notifies the consumer to display or hide the Infobar badge.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)displayInfobarBadge:(BOOL)display type:(InfobarType)infobarType;
-
-// Notifies the consumer that the InfobarBadge active state has changed.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)activeInfobarBadge:(BOOL)active;
 
 @end
 
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index b5bbac2..b87a0ac 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/ui/badges/badge_mediator.h"
+#import "ios/chrome/browser/ui/badges/badge_view_controller.h"
 #include "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/load_query_commands.h"
@@ -163,8 +164,13 @@
 
   // Create BadgeMediator and set the viewController as its consumer.
   if (IsInfobarUIRebootEnabled()) {
+    BadgeViewController* badgeViewController =
+        [[BadgeViewController alloc] init];
+    [self.viewController addChildViewController:badgeViewController];
+    [self.viewController setBadgeView:badgeViewController.view];
+    [badgeViewController didMoveToParentViewController:self.viewController];
     self.badgeMediator =
-        [[BadgeMediator alloc] initWithConsumer:self.viewController
+        [[BadgeMediator alloc] initWithConsumer:badgeViewController
                                    webStateList:self.webStateList];
   }
 
@@ -394,21 +400,6 @@
   self.viewController.searchByImageEnabled = searchByImageSupported;
 }
 
-- (void)displayInfobarBadge:(BOOL)display type:(InfobarType)infobarType {
-  InfobarMetricsRecorder* metricsRecorder;
-  // If the Badge will be displayed create a metrics recorder to log its
-  // interactions, if its hidden metrics recorder should be nil.
-  if (display)
-    metricsRecorder = [[InfobarMetricsRecorder alloc] initWithType:infobarType];
-
-  [self.viewController displayInfobarButton:display
-                            metricsRecorder:metricsRecorder];
-}
-
-- (void)activeInfobarBadge:(BOOL)active {
-  [self.viewController setInfobarButtonStyleActive:active];
-}
-
 #pragma mark - private
 
 // Returns a dictionary with variation headers for qualified URLs. Can be empty.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.h b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.h
index a2ed0a3a..1a4832e 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.h
@@ -40,9 +40,9 @@
 // Sets the location label's text and styles it as if it were placeholder text.
 - (void)setLocationLabelPlaceholderText:(NSString*)string;
 
-// Displays the location InfobarBadgeButton if |display| is YES, hides it if
+// Displays the location badge view if |display| is YES, hides it if
 // |display| is NO. Will animate change if |animated| is YES.
-- (void)displayBadge:(BOOL)display animated:(BOOL)animated;
+- (void)displayBadgeView:(BOOL)display animated:(BOOL)animated;
 
 // Toggles |enabled| state of the trailing button and updates accessibility
 // appropriately.
@@ -52,12 +52,9 @@
 @property(nonatomic, strong) UIButton* locationButton;
 // The label displaying the current location URL.
 @property(nonatomic, strong) UILabel* locationLabel;
-// The InfobarBadgeButton displayed in the leading corner of the view.
-// Call displayBadge:animated: in this class instead of InfobarBadgeButton's
-// directly.
-// TODO(crbug.com/935804): This button is currently only being used in the
-// Infobar redesign.
-@property(nonatomic, strong) InfobarBadgeButton* leadingButton;
+// The view displaying badges in the leading corner of the view.
+// TODO(crbug.com/991241): Pass into init as parameter.
+@property(nonatomic, strong) UIView* badgeView;
 // The button displayed in the trailing corner of the view, i.e. share button.
 @property(nonatomic, strong) UIButton* trailingButton;
 // The string that describes the current security level. Used for a11y.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
index cd0452aa..0f47745 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -24,7 +24,6 @@
 
 // Length of the trailing button side.
 const CGFloat kButtonSize = 24;
-
 // Space between the location icon and the location label.
 const CGFloat kLocationImageToLabelSpacing = -4.0;
 // Minimal horizontal padding between the leading edge of the location bar and
@@ -33,7 +32,8 @@
 // Trailing space between the trailing button and the trailing edge of the
 // location bar.
 const CGFloat kButtonTrailingSpacing = 10;
-
+// Duration of display and hide animation of the badge view, in seconds.
+const CGFloat kbadgeViewAnimationDuration = 0.2;
 }  // namespace
 
 @interface LocationBarSteadyView ()
@@ -45,6 +45,11 @@
 // view.
 @property(nonatomic, strong) UIView* locationContainerView;
 
+// Leading constraint for locationContainerView when there is no BadgeView to
+// its left.
+@property(nonatomic, strong)
+    NSLayoutConstraint* locationContainerViewLeadingAnchorConstraint;
+
 // Constraints to hide the location image view.
 @property(nonatomic, strong)
     NSArray<NSLayoutConstraint*>* hideLocationImageConstraints;
@@ -68,7 +73,7 @@
 
   scheme.fontColor = [UIColor colorNamed:kTextPrimaryColor];
   scheme.placeholderColor = [UIColor colorNamed:kTextfieldPlaceholderColor];
-  scheme.trailingButtonColor = [UIColor colorNamed:@"tab_toolbar_button_color"];
+  scheme.trailingButtonColor = [UIColor colorNamed:kToolbarButtonColor];
 
   return scheme;
 }
@@ -82,8 +87,7 @@
   // TODO(crbug.com/981889): Clean up after iOS 12 support is dropped.
   scheme.fontColor = [UIColor colorNamed:kTextPrimaryDarkColor];
   scheme.placeholderColor = [UIColor colorNamed:kTextfieldPlaceholderDarkColor];
-  scheme.trailingButtonColor =
-      [UIColor colorNamed:@"tab_toolbar_button_color_incognito"];
+  scheme.trailingButtonColor = [UIColor colorNamed:kToolbarButtonDarkColor];
 
   return scheme;
 }
@@ -198,6 +202,11 @@
         constraintEqualToAnchor:self.centerXAnchor];
     centerX.priority = UILayoutPriorityDefaultHigh;
 
+    _locationContainerViewLeadingAnchorConstraint =
+        [_locationContainerView.leadingAnchor
+            constraintGreaterThanOrEqualToAnchor:self.leadingAnchor
+                                        constant:kLocationBarLeadingPadding];
+
     // Setup and activate constraints.
     [NSLayoutConstraint activateConstraints:@[
       [_trailingButton.centerYAnchor
@@ -213,44 +222,17 @@
           constraintEqualToAnchor:self.trailingAnchor
                          constant:-kButtonTrailingSpacing],
       centerX,
+      _locationContainerViewLeadingAnchorConstraint,
     ]];
-
-    if (IsInfobarUIRebootEnabled()) {
-      // Setup leading button.
-      _leadingButton = [InfobarBadgeButton buttonWithType:UIButtonTypeSystem];
-      _leadingButton.translatesAutoresizingMaskIntoConstraints = NO;
-      [_locationButton addSubview:_leadingButton];
-
-      // Setup and activate the leading button constraints.
-      [NSLayoutConstraint activateConstraints:@[
-        [_leadingButton.widthAnchor
-            constraintEqualToAnchor:_leadingButton.heightAnchor],
-        [_leadingButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-        [_leadingButton.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
-        [_leadingButton.leadingAnchor
-            constraintEqualToAnchor:self.leadingAnchor],
-        [_leadingButton.trailingAnchor
-            constraintLessThanOrEqualToAnchor:_locationContainerView
-                                                  .leadingAnchor],
-        [_leadingButton.centerYAnchor
-            constraintEqualToAnchor:self.centerYAnchor],
-      ]];
-    } else {
-      // Since there is no leading button, |locationContainerView|'s
-      // leadingAnchor will be pinned to |self|.
-      [NSLayoutConstraint activateConstraints:@[
-        [_locationContainerView.leadingAnchor
-            constraintGreaterThanOrEqualToAnchor:self.leadingAnchor
-                                        constant:kLocationBarLeadingPadding],
-      ]];
-    }
   }
 
   // Setup accessibility.
   _trailingButton.isAccessibilityElement = YES;
-  if (IsInfobarUIRebootEnabled()) {
-    _leadingButton.isAccessibilityElement = YES;
-    _leadingButton.accessibilityLabel =
+  if (self.badgeView) {
+    self.badgeView.isAccessibilityElement = YES;
+    // TODO(crbug.com/989233): This needs to reflect the currently displayed
+    // badge.
+    self.badgeView.accessibilityLabel =
         l10n_util::GetNSString(IDS_IOS_INFOBAR_BADGES_PASSWORD_HINT);
   }
   _locationButton.isAccessibilityElement = YES;
@@ -325,17 +307,46 @@
   [self updateAccessibility];
 }
 
-- (void)displayBadge:(BOOL)display animated:(BOOL)animated {
+- (void)setBadgeView:(UIView*)badgeView {
+  BOOL hadBadgeView = _badgeView != nil;
+  _badgeView = badgeView;
+  if (!hadBadgeView && badgeView) {
+    _badgeView.translatesAutoresizingMaskIntoConstraints = NO;
+    [self.locationButton addSubview:_badgeView];
+
+    [NSLayoutConstraint deactivateConstraints:@[
+      self.locationContainerViewLeadingAnchorConstraint
+    ]];
+    [NSLayoutConstraint activateConstraints:@[
+      [_badgeView.topAnchor constraintEqualToAnchor:self.topAnchor],
+      [_badgeView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
+      [_badgeView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+      [_badgeView.trailingAnchor
+          constraintLessThanOrEqualToAnchor:self.locationContainerView
+                                                .leadingAnchor],
+    ]];
+  }
+}
+
+- (void)displayBadgeView:(BOOL)display animated:(BOOL)animated {
   if (display) {
     // Adding InfobarBadge button as an accessibility element behind location
     // label. Thus, there should be at least one object alreading in
     // |accessibleElements|.
     DCHECK([self.accessibleElements count] > 0);
-    [self.accessibleElements insertObject:self.leadingButton atIndex:1];
+    [self.accessibleElements insertObject:self.badgeView atIndex:1];
   } else {
-    [self.accessibleElements removeObject:self.leadingButton];
+    [self.accessibleElements removeObject:self.badgeView];
   }
-  [self.leadingButton displayBadge:display animated:animated];
+  void (^changeHiddenState)() = ^{
+    self.badgeView.hidden = !display;
+  };
+  if (animated) {
+    [UIView animateWithDuration:kbadgeViewAnimationDuration
+                     animations:changeHiddenState];
+  } else {
+    changeHiddenState();
+  }
 }
 
 - (void)enableTrailingButton:(BOOL)enabled {
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
index 31db378..8c54c86 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
@@ -36,12 +36,16 @@
 // the omnibox textfield is displayed; in the non-editing state, the current
 // location is displayed.
 @interface LocationBarViewController
-    : UIViewController <BadgeConsumer, FullscreenUIElement, LocationBarAnimatee>
+    : UIViewController <FullscreenUIElement, LocationBarAnimatee>
 
 // Sets the edit view to use in the editing state. This must be set before the
 // view of this view controller is initialized. This must only be called once.
 - (void)setEditView:(UIView*)editView;
 
+// Sets the badge view to display badges. This must be set before the
+// view of this view controller is initialized. This must only be called once.
+- (void)setBadgeView:(UIView*)badgeView;
+
 @property(nonatomic, assign) BOOL incognito;
 
 // The dispatcher for the share button, voice search, and long press actions.
@@ -77,16 +81,6 @@
 - (void)updateForNTP:(BOOL)isNTP;
 // Sets |enabled| of the share button.
 - (void)setShareButtonEnabled:(BOOL)enabled;
-// Displays or hides the InfobarButton. |metricsRecorder| can be nil.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)displayInfobarButton:(BOOL)display
-             metricsRecorder:(InfobarMetricsRecorder*)metricsRecorder;
-// If |active| is YES applies the active styling to the InfobarButton, if NO it
-// removes it.
-// TODO(crbug.com/935804): This method is currently only being used in the
-// Infobar redesign.
-- (void)setInfobarButtonStyleActive:(BOOL)active;
 
 // Displays the voice search button instead of the share button in steady state,
 // and adds the voice search button to the empty textfield.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index c010f1a..0d6328b 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -46,6 +46,9 @@
 // The injected edit view.
 @property(nonatomic, strong) UIView* editView;
 
+// The injected badge view.
+@property(nonatomic, strong) UIView* badgeView;
+
 // The view that displays current location when the omnibox is not focused.
 @property(nonatomic, strong) LocationBarSteadyView* locationBarSteadyView;
 
@@ -61,17 +64,6 @@
 // icon (in iPad multitasking).
 @property(nonatomic, assign) BOOL shareButtonEnabled;
 
-// Keeps the status of the leading button of the location bar steady view. Used
-// to preserve leading button visibility during animations.
-@property(nonatomic, assign) BOOL shouldShowLeadingButton;
-
-// Used to build and record Infobar metrics.
-@property(nonatomic, strong) InfobarMetricsRecorder* infobarMetricsRecorder;
-
-// Whether the InfobarBadge is active or not.
-// TODO(crbug.com/961343): Move this into a future BadgeContainer.
-@property(nonatomic, assign) BOOL activeBadge;
-
 // Starts voice search, updating the NamedGuide to be constrained to the
 // trailing button.
 - (void)startVoiceSearch;
@@ -100,17 +92,6 @@
   self = [super init];
   if (self) {
     _locationBarSteadyView = [[LocationBarSteadyView alloc] init];
-
-    [_locationBarSteadyView.locationButton
-               addTarget:self
-                  action:@selector(locationBarSteadyViewTapped)
-        forControlEvents:UIControlEventTouchUpInside];
-
-    UILongPressGestureRecognizer* recognizer =
-        [[UILongPressGestureRecognizer alloc]
-            initWithTarget:self
-                    action:@selector(showLongPressMenu:)];
-    [_locationBarSteadyView.locationButton addGestureRecognizer:recognizer];
   }
   return self;
 }
@@ -120,6 +101,11 @@
   _editView = editView;
 }
 
+- (void)setBadgeView:(UIView*)badgeView {
+  DCHECK(!self.badgeView);
+  _badgeView = badgeView;
+}
+
 - (void)switchToEditing:(BOOL)editing {
   self.editView.hidden = !editing;
   self.locationBarSteadyView.hidden = editing;
@@ -177,6 +163,22 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  if (IsInfobarUIRebootEnabled()) {
+    DCHECK(self.badgeView) << "The badge view must be set at this point";
+    self.locationBarSteadyView.badgeView = self.badgeView;
+  }
+
+  [_locationBarSteadyView.locationButton
+             addTarget:self
+                action:@selector(locationBarSteadyViewTapped)
+      forControlEvents:UIControlEventTouchUpInside];
+
+  UILongPressGestureRecognizer* recognizer =
+      [[UILongPressGestureRecognizer alloc]
+          initWithTarget:self
+                  action:@selector(showLongPressMenu:)];
+  [_locationBarSteadyView.locationButton addGestureRecognizer:recognizer];
+
   DCHECK(self.editView) << "The edit view must be set at this point";
 
   [self.view addSubview:self.editView];
@@ -188,10 +190,6 @@
   AddSameConstraints(self.locationBarSteadyView, self.view);
 
   [self switchToEditing:NO];
-
-  if (IsInfobarUIRebootEnabled()) {
-    [self updateInfobarButton];
-  }
 }
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
@@ -206,7 +204,7 @@
   CGFloat scaleValue = 0.79 + 0.21 * progress;
   self.locationBarSteadyView.trailingButton.alpha = alphaValue;
   if (IsInfobarUIRebootEnabled()) {
-    self.locationBarSteadyView.leadingButton.alpha = alphaValue;
+    self.locationBarSteadyView.badgeView.alpha = alphaValue;
   }
   self.locationBarSteadyView.transform =
       CGAffineTransformMakeScale(scaleValue, scaleValue);
@@ -259,13 +257,6 @@
   }
 }
 
-- (void)displayInfobarButton:(BOOL)display
-             metricsRecorder:(InfobarMetricsRecorder*)metricsRecorder {
-  self.infobarMetricsRecorder = metricsRecorder;
-  self.shouldShowLeadingButton = display;
-  [self.locationBarSteadyView displayBadge:display animated:YES];
-}
-
 #pragma mark - LocationBarAnimatee
 
 - (void)offsetEditViewToMatchSteadyView {
@@ -296,13 +287,16 @@
   self.locationBarSteadyView.alpha = hidden ? 0 : 1;
 }
 
-- (void)hideSteadyViewLeadingButton {
-  [self.locationBarSteadyView displayBadge:NO animated:NO];
+- (void)hideSteadyViewBadgeView {
+  if (IsInfobarUIRebootEnabled()) {
+    [self.locationBarSteadyView displayBadgeView:NO animated:NO];
+  }
 }
 
-- (void)showSteadyViewLeadingButtonIfNeeded {
-  [self.locationBarSteadyView displayBadge:self.shouldShowLeadingButton
-                                  animated:NO];
+- (void)showSteadyViewBadgeView {
+  if (IsInfobarUIRebootEnabled()) {
+    [self.locationBarSteadyView displayBadgeView:YES animated:NO];
+  }
 }
 
 - (void)setEditViewFaded:(BOOL)hidden {
@@ -456,70 +450,6 @@
   base::RecordAction(base::UserMetricsAction("MobileToolbarShareMenu"));
 }
 
-// TODO(crbug.com/935804): Create constants variables for the magic numbers
-// being used here if/when this stops being temporary.
-- (void)updateInfobarButton {
-  DCHECK(IsInfobarUIRebootEnabled());
-
-  [self.locationBarSteadyView.leadingButton
-             addTarget:self
-                action:@selector(displayModalInfobar)
-      forControlEvents:UIControlEventTouchUpInside];
-  // Set as hidden as it should only be shown by |displayInfobarButton:|
-  [self.locationBarSteadyView.leadingButton displayBadge:NO animated:NO];
-}
-
-- (void)displayModalInfobar {
-  MobileMessagesBadgeState state;
-  if (self.activeBadge) {
-    state = MobileMessagesBadgeState::Active;
-    base::RecordAction(
-        base::UserMetricsAction("MobileMessagesBadgeAcceptedTapped"));
-  } else {
-    state = MobileMessagesBadgeState::Inactive;
-    base::RecordAction(
-        base::UserMetricsAction("MobileMessagesBadgeNonAcceptedTapped"));
-  }
-  [self.infobarMetricsRecorder recordBadgeTappedInState:state];
-  [self.dispatcher displayModalInfobar];
-}
-
-- (void)setInfobarButtonStyleActive:(BOOL)active {
-  self.activeBadge = active;
-  [self.locationBarSteadyView.leadingButton setActive:active animated:YES];
-}
-
-#pragma mark - BadgeConsumer
-
-- (void)setupWithBadges:(NSArray*)badges {
-  BOOL hasBadge = badges.count > 0;
-  if (hasBadge) {
-    id<BadgeItem> firstBadge = badges[0];
-    BOOL isAccepted = firstBadge.isAccepted;
-    self.activeBadge = isAccepted;
-    [self.locationBarSteadyView.leadingButton setActive:isAccepted animated:NO];
-  }
-  [self.locationBarSteadyView.leadingButton displayBadge:hasBadge animated:NO];
-  self.shouldShowLeadingButton = hasBadge;
-}
-
-- (void)addBadge:(id<BadgeItem>)badgeItem {
-  self.activeBadge = badgeItem.isAccepted;
-  [self.locationBarSteadyView.leadingButton setActive:badgeItem.isAccepted
-                                             animated:NO];
-  [self.locationBarSteadyView.leadingButton displayBadge:YES animated:YES];
-  self.shouldShowLeadingButton = YES;
-}
-
-- (void)removeBadge:(id<BadgeItem>)badgeItem {
-  [self.locationBarSteadyView.leadingButton displayBadge:NO animated:NO];
-  self.shouldShowLeadingButton = NO;
-}
-
-- (void)updateBadge:(id<BadgeItem>)badgeItem {
-  [self.locationBarSteadyView.leadingButton setActive:badgeItem.isAccepted
-                                             animated:YES];
-}
 
 #pragma mark - UIMenu
 
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index 3597527..f416ce8 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -142,8 +142,6 @@
     "//ios/chrome/browser/ui/location_bar:constants",
     "//ios/chrome/browser/ui/omnibox/popup",
     "//ios/chrome/browser/ui/orchestrator:orchestrator",
-    "//ios/chrome/browser/ui/toolbar/buttons/resources:tab_toolbar_button_color",
-    "//ios/chrome/browser/ui/toolbar/buttons/resources:tab_toolbar_button_color_incognito",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
     "//ios/chrome/browser/ui/util",
@@ -188,7 +186,6 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/omnibox/popup:popup_ui",
-    "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 754eb31..730a1a8 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -9,7 +9,6 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -98,7 +97,7 @@
 - (void)testTrailingButton {
   [self openPage1];
 
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     [[EarlGrey selectElementWithMatcher:chrome_test_util::ShareButton()]
         assertWithMatcher:grey_sufficientlyVisible()];
   }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 26880d9..c260073 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -68,12 +68,12 @@
   UIColor* iconTintColor;
   if (base::FeatureList::IsEnabled(kNewOmniboxPopupLayout)) {
     iconTintColor = color::IncognitoDynamicColor(
-        self.incognito, [UIColor colorNamed:@"tab_toolbar_button_color"],
-        [UIColor colorNamed:@"tab_toolbar_button_color_incognito"]);
+        self.incognito, [UIColor colorNamed:kToolbarButtonColor],
+        [UIColor colorNamed:kToolbarButtonDarkColor]);
   } else {
     iconTintColor = color::IncognitoDynamicColor(
-        self.incognito, [UIColor colorNamed:@"tab_toolbar_button_color"],
-        [UIColor colorNamed:@"tab_toolbar_button_color_incognito"]);
+        self.incognito, [UIColor colorNamed:kToolbarButtonColor],
+        [UIColor colorNamed:kToolbarButtonDarkColor]);
   }
 
   self.view = [[OmniboxContainerView alloc]
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index 2f9fc91..c9950ff7d 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -34,9 +34,11 @@
     "//ios/chrome/browser/ui/omnibox:omnibox_util",
     "//ios/chrome/browser/ui/omnibox/popup/shortcuts",
     "//ios/chrome/browser/ui/toolbar/buttons",
+    "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list:web_state_list",
+    "//ios/chrome/common/colors",
     "//ios/chrome/common/favicon",
     "//ios/chrome/common/ui_util",
     "//ios/web/public:public",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
index 09de7d6..21b2287 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
@@ -6,11 +6,14 @@
 
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
+#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 #include "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
+#import "ui/gfx/ios/uikit_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -27,6 +30,9 @@
 @property(nonatomic, weak) id<OmniboxPopupPresenterDelegate> delegate;
 @property(nonatomic, weak) UIViewController* viewController;
 @property(nonatomic, strong) UIView* popupContainerView;
+// Separator for the bottom edge of the popup on iPad.
+@property(nonatomic, strong) UIView* bottomSeparator;
+
 @end
 
 @implementation OmniboxPopupPresenter
@@ -65,6 +71,26 @@
     _popupContainerView.translatesAutoresizingMaskIntoConstraints = NO;
     viewController.view.translatesAutoresizingMaskIntoConstraints = NO;
     AddSameConstraints(viewController.view, _popupContainerView);
+
+    // Add bottom separator. This will only be visible on iPad where
+    // the omnibox doesn't fill the whole screen.
+    _bottomSeparator = [[UIView alloc] initWithFrame:CGRectZero];
+    _bottomSeparator.translatesAutoresizingMaskIntoConstraints = NO;
+    _bottomSeparator.backgroundColor = [UIColor colorNamed:kToolbarShadowColor];
+
+    [_popupContainerView addSubview:self.bottomSeparator];
+    CGFloat separatorHeight =
+        ui::AlignValueToUpperPixel(kToolbarSeparatorHeight);
+    [NSLayoutConstraint activateConstraints:@[
+      [self.bottomSeparator.heightAnchor
+          constraintEqualToConstant:separatorHeight],
+      [self.bottomSeparator.leadingAnchor
+          constraintEqualToAnchor:_popupContainerView.leadingAnchor],
+      [self.bottomSeparator.trailingAnchor
+          constraintEqualToAnchor:_popupContainerView.trailingAnchor],
+      [self.bottomSeparator.topAnchor
+          constraintEqualToAnchor:_popupContainerView.bottomAnchor],
+    ]];
   }
   return self;
 }
@@ -78,6 +104,7 @@
     // popup view.
     if (!IsIPadIdiom()) {
       self.bottomConstraint.active = NO;
+      self.bottomSeparator.hidden = YES;
     }
 
     [self.viewController willMoveToParentViewController:nil];
@@ -99,6 +126,7 @@
 
     if (!IsIPadIdiom()) {
       self.bottomConstraint.active = YES;
+      self.bottomSeparator.hidden = NO;
     }
 
     self.open = YES;
diff --git a/ios/chrome/browser/ui/orchestrator/location_bar_animatee.h b/ios/chrome/browser/ui/orchestrator/location_bar_animatee.h
index c276d9b..78edcfd 100644
--- a/ios/chrome/browser/ui/orchestrator/location_bar_animatee.h
+++ b/ios/chrome/browser/ui/orchestrator/location_bar_animatee.h
@@ -25,11 +25,10 @@
 // See comment for -resetEditViewOffsetAndOffsetSteadyViewToMatch.
 - (void)resetSteadyViewOffsetAndOffsetEditViewToMatch;
 
-// Hides leading button for steady view.
-- (void)hideSteadyViewLeadingButton;
-// Call this after calling -hideSteadyViewLeadingButton. Restores the displayed
-// state of the leading button of the steady view.
-- (void)showSteadyViewLeadingButtonIfNeeded;
+// Hides badge view for steady view.
+- (void)hideSteadyViewBadgeView;
+// Displays the badge view of the steady view.
+- (void)showSteadyViewBadgeView;
 
 - (void)setSteadyViewFaded:(BOOL)hidden;
 - (void)setEditViewFaded:(BOOL)hidden;
diff --git a/ios/chrome/browser/ui/orchestrator/omnibox_focus_orchestrator.mm b/ios/chrome/browser/ui/orchestrator/omnibox_focus_orchestrator.mm
index 3954161a..294453eb 100644
--- a/ios/chrome/browser/ui/orchestrator/omnibox_focus_orchestrator.mm
+++ b/ios/chrome/browser/ui/orchestrator/omnibox_focus_orchestrator.mm
@@ -87,10 +87,10 @@
   if (animated) {
     // Prepare for animation.
     [self.locationBarAnimatee offsetEditViewToMatchSteadyView];
-    // Hide leading button before the transform regardless of current displayed
+    // Hide badge view before the transform regardless of current displayed
     // state to prevent it from being visible outside of the location bar as the
     // steadView moves outside to the leading side of the location bar.
-    [self.locationBarAnimatee hideSteadyViewLeadingButton];
+    [self.locationBarAnimatee hideSteadyViewBadgeView];
     // Make edit view transparent, but not hidden.
     [self.locationBarAnimatee setEditViewHidden:NO];
     [self.locationBarAnimatee setEditViewFaded:YES];
@@ -160,8 +160,7 @@
   void (^cleanup)() = ^{
     [self.locationBarAnimatee setEditViewHidden:YES];
     [self.locationBarAnimatee setSteadyViewHidden:NO];
-    // Restore the leading button to the original displayed state.
-    [self.locationBarAnimatee showSteadyViewLeadingButtonIfNeeded];
+    [self.locationBarAnimatee showSteadyViewBadgeView];
     [self.locationBarAnimatee resetTransforms];
     [self.locationBarAnimatee setSteadyViewFaded:NO];
     [self.editViewAnimatee setLeadingIconFaded:NO];
diff --git a/ios/chrome/browser/ui/overlays/BUILD.gn b/ios/chrome/browser/ui/overlays/BUILD.gn
index c729e88..ea40159 100644
--- a/ios/chrome/browser/ui/overlays/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/BUILD.gn
@@ -53,7 +53,7 @@
   sources = [
     "overlay_request_coordinator.h",
     "overlay_request_coordinator.mm",
-    "overlay_ui_dismissal_delegate.h",
+    "overlay_request_coordinator_delegate.h",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h
index ccc7285..9cb45b2 100644
--- a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h
+++ b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.h
@@ -10,7 +10,7 @@
 #include "ios/chrome/browser/overlays/public/overlay_modality.h"
 
 class Browser;
-class OverlayUIDismissalDelegate;
+class OverlayRequestCoordinatorDelegate;
 @class OverlayRequestCoordinator;
 class OverlayRequest;
 
@@ -29,7 +29,7 @@
 // Creates a coordinator to show |request|'s overlay UI.
 - (OverlayRequestCoordinator*)
     newCoordinatorForRequest:(OverlayRequest*)request
-           dismissalDelegate:(OverlayUIDismissalDelegate*)dismissalDelegate
+                    delegate:(OverlayRequestCoordinatorDelegate*)delegate
           baseViewController:(UIViewController*)baseViewController;
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
index df9a5ad3..f44c089 100644
--- a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
@@ -40,16 +40,17 @@
 
 - (OverlayRequestCoordinator*)
     newCoordinatorForRequest:(OverlayRequest*)request
-           dismissalDelegate:(OverlayUIDismissalDelegate*)dismissalDelegate
+                    delegate:(OverlayRequestCoordinatorDelegate*)delegate
           baseViewController:(UIViewController*)baseViewController {
-  for (Class coordinatorClass in self
-           .supportedOverlayRequestCoordinatorClasses) {
+  NSArray<Class>* supportedClasses =
+      self.supportedOverlayRequestCoordinatorClasses;
+  for (Class coordinatorClass in supportedClasses) {
     if ([coordinatorClass supportsRequest:request]) {
       return [[coordinatorClass alloc]
           initWithBaseViewController:baseViewController
                              browser:self.browser
                              request:request
-                   dismissalDelegate:dismissalDelegate];
+                            delegate:delegate];
     }
   }
   NOTREACHED() << "Received unsupported request type.";
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
index 041c4b9..5a1a461 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.h
@@ -12,8 +12,8 @@
 #import "ios/chrome/browser/overlays/public/overlay_presentation_context.h"
 #import "ios/chrome/browser/overlays/public/overlay_user_data.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_ui_state.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
 
 @class OverlayRequestCoordinatorFactory;
 @class OverlayContainerCoordinator;
@@ -62,6 +62,7 @@
   bool IsActive() const override;
   void ShowOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request,
+                     OverlayPresentationCallback presentation_callback,
                      OverlayDismissalCallback dismissal_callback) override;
   void HideOverlayUI(OverlayPresenter* presenter,
                      OverlayRequest* request) override;
@@ -102,13 +103,15 @@
   };
 
   // Helper object that listens for UI dismissal events.
-  class OverlayDismissalHelper : public OverlayUIDismissalDelegate {
+  class OverlayRequestCoordinatorDelegateImpl
+      : public OverlayRequestCoordinatorDelegate {
    public:
-    OverlayDismissalHelper(
+    OverlayRequestCoordinatorDelegateImpl(
         OverlayPresentationContextImpl* presentation_context);
-    ~OverlayDismissalHelper() override;
+    ~OverlayRequestCoordinatorDelegateImpl() override;
 
     // OverlayUIDismissalDelegate:
+    void OverlayUIDidFinishPresentation(OverlayRequest* request) override;
     void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
 
    private:
@@ -119,8 +122,9 @@
   OverlayPresenter* presenter_ = nullptr;
   // The cleanup helper.
   BrowserShutdownHelper shutdown_helper_;
-  // The UI dismissal helper.
-  OverlayDismissalHelper ui_dismissal_helper_;
+  // The delegate used to intercept presentation/dismissal events from
+  // OverlayRequestCoordinators.
+  OverlayRequestCoordinatorDelegateImpl coordinator_delegate_;
   // The coordinator factory that provides the UI for the overlays at this
   // modality.
   OverlayRequestCoordinatorFactory* coordinator_factory_ = nil;
diff --git a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
index 74767b8..6150255 100644
--- a/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_presentation_context_impl.mm
@@ -45,7 +45,7 @@
     OverlayModality modality)
     : presenter_(OverlayPresenter::FromBrowser(browser, modality)),
       shutdown_helper_(browser, presenter_),
-      ui_dismissal_helper_(this),
+      coordinator_delegate_(this),
       coordinator_factory_([OverlayRequestCoordinatorFactory
           factoryForBrowser:browser
                    modality:modality]),
@@ -98,6 +98,7 @@
 void OverlayPresentationContextImpl::ShowOverlayUI(
     OverlayPresenter* presenter,
     OverlayRequest* request,
+    OverlayPresentationCallback presentation_callback,
     OverlayDismissalCallback dismissal_callback) {
   DCHECK_EQ(presenter_, presenter);
   // Create the UI state for |request| if necessary.
@@ -105,7 +106,7 @@
     states_[request] = std::make_unique<OverlayRequestUIState>(request);
   // Present the overlay UI and update the UI state.
   GetRequestUIState(request)->OverlayPresentionRequested(
-      std::move(dismissal_callback));
+      std::move(presentation_callback), std::move(dismissal_callback));
   SetRequest(request);
 }
 
@@ -202,7 +203,7 @@
       overlay_coordinator.baseViewController != container_view_controller) {
     overlay_coordinator = [coordinator_factory_
         newCoordinatorForRequest:request_
-               dismissalDelegate:&ui_dismissal_helper_
+                        delegate:&coordinator_delegate_
               baseViewController:container_view_controller];
     state->OverlayUIWillBePresented(overlay_coordinator);
   }
@@ -258,16 +259,20 @@
 
 #pragma mark OverlayDismissalHelper
 
-OverlayPresentationContextImpl::OverlayDismissalHelper::OverlayDismissalHelper(
-    OverlayPresentationContextImpl* presentation_context)
+OverlayPresentationContextImpl::OverlayRequestCoordinatorDelegateImpl::
+    OverlayRequestCoordinatorDelegateImpl(
+        OverlayPresentationContextImpl* presentation_context)
     : presentation_context_(presentation_context) {
   DCHECK(presentation_context_);
 }
 
-OverlayPresentationContextImpl::OverlayDismissalHelper::
-    ~OverlayDismissalHelper() = default;
+OverlayPresentationContextImpl::OverlayRequestCoordinatorDelegateImpl::
+    ~OverlayRequestCoordinatorDelegateImpl() = default;
 
-void OverlayPresentationContextImpl::OverlayDismissalHelper::
+void OverlayPresentationContextImpl::OverlayRequestCoordinatorDelegateImpl::
+    OverlayUIDidFinishPresentation(OverlayRequest* request) {}
+
+void OverlayPresentationContextImpl::OverlayRequestCoordinatorDelegateImpl::
     OverlayUIDidFinishDismissal(OverlayRequest* request) {
   DCHECK(request);
   DCHECK_EQ(presentation_context_->request_, request);
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h
index 02bd7f34..180e195 100644
--- a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h
+++ b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.h
@@ -7,7 +7,7 @@
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
-class OverlayUIDismissalDelegate;
+class OverlayRequestCoordinatorDelegate;
 class OverlayRequest;
 
 // Coordinator superclass used to present UI for an OverlayRequest.
@@ -20,8 +20,8 @@
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                    request:(OverlayRequest*)request
-                         dismissalDelegate:
-                             (OverlayUIDismissalDelegate*)dismissalDelegate
+                                  delegate:(OverlayRequestCoordinatorDelegate*)
+                                               delegate
     NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                               browserState:
@@ -30,12 +30,11 @@
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser NS_UNAVAILABLE;
 
-// The OverlayUIDismissalDelegate passed on initialization.  Used to communicate
-// when the overlay UI is finished being dismissed, which may occur after
-// |-stop| even if the overlay is stopped without animation.  This notifies
-// OverlayPresenter that the presentation context is clear to show the next
-// requested overlay.
-@property(nonatomic, readonly) OverlayUIDismissalDelegate* dismissalDelegate;
+// The OverlayRequestCoordinatorDelegate passed on initialization.  Used to
+// communicate when the overlay UI is finished being presented and dismissed.
+// Overlay UI presentation and dismissal may occur after |-start| and |-stop|,
+// even if the overlay is stopped without animation.
+@property(nonatomic, readonly) OverlayRequestCoordinatorDelegate* delegate;
 
 // The request used to configure the overlay UI.
 @property(nonatomic, readonly) OverlayRequest* request;
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm
index 0bde829..fdcd07b 100644
--- a/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_request_coordinator.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
 
 #include "base/logging.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -20,15 +20,15 @@
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                    request:(OverlayRequest*)request
-                         dismissalDelegate:
-                             (OverlayUIDismissalDelegate*)dismissalDelegate {
+                                  delegate:(OverlayRequestCoordinatorDelegate*)
+                                               delegate {
   DCHECK([[self class] supportsRequest:request]);
   self = [super initWithBaseViewController:viewController browser:browser];
   if (self) {
     _request = request;
     DCHECK(_request);
-    _dismissalDelegate = dismissalDelegate;
-    DCHECK(_dismissalDelegate);
+    _delegate = delegate;
+    DCHECK(_delegate);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h b/ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h
new file mode 100644
index 0000000..266b4f02
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h
@@ -0,0 +1,26 @@
+// 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_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
+
+class OverlayRequest;
+
+// Delegate class used to communicate overlay UI presentation events back to
+// OverlayPresenter.
+class OverlayRequestCoordinatorDelegate {
+ public:
+  OverlayRequestCoordinatorDelegate() = default;
+  virtual ~OverlayRequestCoordinatorDelegate() = default;
+
+  // Called to notify the delegate that the UI for |request| has finished being
+  // presented.
+  virtual void OverlayUIDidFinishPresentation(OverlayRequest* request) = 0;
+
+  // Called to notify the delegate that the UI for |request| is finished
+  // being dismissed.
+  virtual void OverlayUIDidFinishDismissal(OverlayRequest* request) = 0;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h
index 1e4a2a8..6abe86e4 100644
--- a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 
 #include "ios/chrome/browser/overlays/public/overlay_dismissal_callback.h"
+#include "ios/chrome/browser/overlays/public/overlay_presentation_callback.h"
 
 @class OverlayRequestCoordinator;
 class OverlayRequest;
@@ -21,9 +22,12 @@
 
   // Called when the OverlayPresenter requests the presentation of |request_|.
   // This may or may not correspond with an OverlayUIWasPresented() if the
-  // hierarchy is not ready for presentation (i.e. overlay presentation for a
-  // Browser whose UI is not currently on-screen).
-  void OverlayPresentionRequested(OverlayDismissalCallback callback);
+  // presentation context is inactive.  |presentation_callback| and
+  // |dismissal_callback| are stored in the state, and will be executed when
+  // OverlayUIWasPresented() and OverlayUIWasDismissed() are called.
+  void OverlayPresentionRequested(
+      OverlayPresentationCallback presentation_callback,
+      OverlayDismissalCallback dismissal_callback);
 
   // Notifies the state that the UI is about to be presented using
   // |coordinator|.
@@ -48,6 +52,7 @@
   OverlayRequest* request_ = nullptr;
   OverlayRequestCoordinator* coordinator_ = nil;
   bool has_ui_been_presented_ = false;
+  OverlayPresentationCallback presentation_callback_;
   OverlayDismissalReason dismissal_reason_ =
       OverlayDismissalReason::kUserInteraction;
   OverlayDismissalCallback dismissal_callback_;
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm
index 5073250..6f30a5e 100644
--- a/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state.mm
@@ -23,9 +23,14 @@
 }
 
 void OverlayRequestUIState::OverlayPresentionRequested(
-    OverlayDismissalCallback callback) {
+    OverlayPresentationCallback presentation_callback,
+    OverlayDismissalCallback dismissal_callback) {
+  DCHECK(presentation_callback_.is_null());
   DCHECK(dismissal_callback_.is_null());
-  dismissal_callback_ = std::move(callback);
+  DCHECK(!presentation_callback.is_null());
+  DCHECK(!dismissal_callback.is_null());
+  presentation_callback_ = std::move(presentation_callback);
+  dismissal_callback_ = std::move(dismissal_callback);
   // The default dismissal reason is kUserInteraction.  This is to avoid
   // additional bookkeeping for overlays dismissed by user interaction.
   // Overlays explicitly dismissed by OverlayPresenter set the reason to kHide
@@ -41,6 +46,7 @@
 
 void OverlayRequestUIState::OverlayUIWasPresented() {
   has_ui_been_presented_ = true;
+  std::move(presentation_callback_).Run();
 }
 
 void OverlayRequestUIState::OverlayUIWasDismissed() {
diff --git a/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm
index cbc9a1c..dad5aa0 100644
--- a/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_request_ui_state_unittest.mm
@@ -10,7 +10,7 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h"
 #import "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator.h"
-#include "ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
 #include "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -38,8 +38,10 @@
 // resets the default dismissal reason to kUserInteraction.
 TEST_F(OverlayRequestUIStateTest, OverlayPresentionRequested) {
   ASSERT_FALSE(state().has_callback());
-  state().OverlayPresentionRequested(base::BindOnce(^(OverlayDismissalReason){
-  }));
+  state().OverlayPresentionRequested(base::BindOnce(^{
+                                     }),
+                                     base::BindOnce(^(OverlayDismissalReason){
+                                     }));
   EXPECT_TRUE(state().has_callback());
   EXPECT_EQ(OverlayDismissalReason::kUserInteraction,
             state().dismissal_reason());
@@ -47,13 +49,13 @@
 
 // Tests that OverlayUIWillBePresented() stores the coordinator in the state.
 TEST_F(OverlayRequestUIStateTest, OverlayUIWillBePresented) {
-  FakeDismissalDelegate dismissal_delegate;
+  FakeOverlayRequestCoordinatorDelegate delegate;
   FakeOverlayRequestCoordinator* coordinator =
       [[FakeOverlayRequestCoordinator alloc]
           initWithBaseViewController:nil
                              browser:nullptr
                              request:request()
-                   dismissalDelegate:&dismissal_delegate];
+                            delegate:&delegate];
   state().OverlayUIWillBePresented(coordinator);
   EXPECT_EQ(coordinator, state().coordinator());
 }
@@ -61,23 +63,32 @@
 // Tests that OverlayUIWasPresented() correctly updates has_ui_been_presented().
 TEST_F(OverlayRequestUIStateTest, OverlayUIWasPresented) {
   ASSERT_FALSE(state().has_ui_been_presented());
+  __block bool presentation_callback_executed = false;
+  state().OverlayPresentionRequested(base::BindOnce(^{
+                                       presentation_callback_executed = true;
+                                     }),
+                                     base::BindOnce(^(OverlayDismissalReason){
+                                     }));
   state().OverlayUIWasPresented();
   EXPECT_TRUE(state().has_ui_been_presented());
+  EXPECT_TRUE(presentation_callback_executed);
 }
 
 // Tests that OverlayUIWasDismissed() executes the dismissal callback.
 TEST_F(OverlayRequestUIStateTest, OverlayUIWasDismissed) {
   __block bool dismissal_callback_executed = false;
-  state().OverlayPresentionRequested(base::BindOnce(^(OverlayDismissalReason) {
-    dismissal_callback_executed = true;
-  }));
-  FakeDismissalDelegate dismissal_delegate;
+  state().OverlayPresentionRequested(base::BindOnce(^{
+                                     }),
+                                     base::BindOnce(^(OverlayDismissalReason) {
+                                       dismissal_callback_executed = true;
+                                     }));
+  FakeOverlayRequestCoordinatorDelegate delegate;
   FakeOverlayRequestCoordinator* coordinator =
       [[FakeOverlayRequestCoordinator alloc]
           initWithBaseViewController:nil
                              browser:nullptr
                              request:request()
-                   dismissalDelegate:&dismissal_delegate];
+                            delegate:&delegate];
   state().OverlayUIWillBePresented(coordinator);
   state().OverlayUIWasPresented();
   state().OverlayUIWasDismissed();
diff --git a/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h b/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h
deleted file mode 100644
index ad84fa3..0000000
--- a/ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h
+++ /dev/null
@@ -1,21 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
-
-class OverlayRequest;
-
-// Delegate class used to communicate dismissal events back to OverlayPresenter.
-class OverlayUIDismissalDelegate {
- public:
-  OverlayUIDismissalDelegate() = default;
-  virtual ~OverlayUIDismissalDelegate() = default;
-
-  // Called to notify the delegate that the UI for |request|'s UI is finished
-  // being dismissed.
-  virtual void OverlayUIDidFinishDismissal(OverlayRequest* request) = 0;
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_OVERLAY_UI_DISMISSAL_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/test/BUILD.gn b/ios/chrome/browser/ui/overlays/test/BUILD.gn
index d5c019f5..a01a5d8 100644
--- a/ios/chrome/browser/ui/overlays/test/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/test/BUILD.gn
@@ -7,8 +7,8 @@
   sources = [
     "fake_overlay_request_coordinator.h",
     "fake_overlay_request_coordinator.mm",
-    "fake_overlay_ui_dismissal_delegate.cc",
-    "fake_overlay_ui_dismissal_delegate.h",
+    "fake_overlay_request_coordinator_delegate.cc",
+    "fake_overlay_request_coordinator_delegate.h",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.cc b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.cc
new file mode 100644
index 0000000..95d07bb
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.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 "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
+
+FakeOverlayRequestCoordinatorDelegate::FakeOverlayRequestCoordinatorDelegate() =
+    default;
+FakeOverlayRequestCoordinatorDelegate::
+    ~FakeOverlayRequestCoordinatorDelegate() = default;
+
+bool FakeOverlayRequestCoordinatorDelegate::HasUIBeenPresented(
+    OverlayRequest* request) const {
+  return states_.find(request) != states_.end() &&
+         states_.at(request) == PresentationState::kPresented;
+}
+
+bool FakeOverlayRequestCoordinatorDelegate::HasUIBeenDismissed(
+    OverlayRequest* request) const {
+  return states_.find(request) != states_.end() &&
+         states_.at(request) == PresentationState::kDismissed;
+}
+
+void FakeOverlayRequestCoordinatorDelegate::OverlayUIDidFinishPresentation(
+    OverlayRequest* request) {
+  states_[request] = PresentationState::kPresented;
+}
+
+void FakeOverlayRequestCoordinatorDelegate::OverlayUIDidFinishDismissal(
+    OverlayRequest* request) {
+  states_[request] = PresentationState::kDismissed;
+}
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h
new file mode 100644
index 0000000..920556f
--- /dev/null
+++ b/ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h
@@ -0,0 +1,34 @@
+// 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_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
+
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
+
+#include <map>
+
+// Fake implementation of OverlayRequestCoordinatorDelegate.
+class FakeOverlayRequestCoordinatorDelegate
+    : public OverlayRequestCoordinatorDelegate {
+ public:
+  FakeOverlayRequestCoordinatorDelegate();
+  ~FakeOverlayRequestCoordinatorDelegate() override;
+
+  // Whether the overlay UI for |request| has been presented.
+  bool HasUIBeenPresented(OverlayRequest* request) const;
+
+  // Whether the overlay UI for |request| has been dismissed.
+  bool HasUIBeenDismissed(OverlayRequest* request) const;
+
+  // OverlayRequestCoordinatorDelegate:
+  void OverlayUIDidFinishPresentation(OverlayRequest* request) override;
+  void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
+
+ private:
+  enum class PresentationState { kNotPresented, kPresented, kDismissed };
+  std::map<OverlayRequest*, PresentationState> states_;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_REQUEST_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc
deleted file mode 100644
index 88ee77f..0000000
--- a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h"
-
-FakeDismissalDelegate::FakeDismissalDelegate() = default;
-FakeDismissalDelegate::~FakeDismissalDelegate() = default;
-
-bool FakeDismissalDelegate::HasUIBeenDismissed(OverlayRequest* request) const {
-  return dismissed_requests_.find(request) != dismissed_requests_.end();
-}
-
-void FakeDismissalDelegate::OverlayUIDidFinishDismissal(
-    OverlayRequest* request) {
-  dismissed_requests_.insert(request);
-}
diff --git a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h b/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h
deleted file mode 100644
index 0334c4c9..0000000
--- a/ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h
+++ /dev/null
@@ -1,28 +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.
-
-#ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
-
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
-
-#include <set>
-
-// Fake implementation of OverlayUIDismissalDelegate.
-class FakeDismissalDelegate : public OverlayUIDismissalDelegate {
- public:
-  FakeDismissalDelegate();
-  ~FakeDismissalDelegate() override;
-
-  // Whether the overlay UI for |request| has been dismissed.
-  bool HasUIBeenDismissed(OverlayRequest* request) const;
-
-  // OverlayUIDismissalDelegate:
-  void OverlayUIDidFinishDismissal(OverlayRequest* request) override;
-
- private:
-  std::set<OverlayRequest*> dismissed_requests_;
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_TEST_FAKE_OVERLAY_UI_DISMISSAL_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.mm
index 10f1587..b996e58 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.mm
@@ -7,7 +7,7 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #include "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -80,8 +80,8 @@
                            if (!strongSelf)
                              return;
                            strongSelf.alertViewController = nil;
-                           strongSelf.dismissalDelegate
-                               ->OverlayUIDidFinishDismissal(weakSelf.request);
+                           strongSelf.delegate->OverlayUIDidFinishDismissal(
+                               weakSelf.request);
                          }];
   self.started = NO;
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.mm
index 35767fb..a3b63ac 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.mm
@@ -7,7 +7,7 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #include "ios/chrome/browser/overlays/public/web_content_area/http_auth_overlay.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -97,8 +97,8 @@
                            if (!strongSelf)
                              return;
                            strongSelf.alertViewController = nil;
-                           strongSelf.dismissalDelegate
-                               ->OverlayUIDidFinishDismissal(weakSelf.request);
+                           strongSelf.delegate->OverlayUIDidFinishDismissal(
+                               weakSelf.request);
                          }];
   self.started = NO;
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator_unittest.mm
index 611745b..c618bde 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_alert_overlay_coordinator_unittest.mm
@@ -47,5 +47,5 @@
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
     return !alert.presentingViewController;
   }));
-  EXPECT_TRUE(dismissal_delegate().HasUIBeenDismissed(request));
+  EXPECT_TRUE(delegate().HasUIBeenDismissed(request));
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.mm
index 59be3598..2e2e768 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator.mm
@@ -15,7 +15,7 @@
 #import "ios/chrome/browser/overlays/public/web_content_area/java_script_confirmation_overlay.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_mediator.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator+subclassing.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator_unittest.mm
index 1a317fb..6728f22 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_confirmation_overlay_coordinator_unittest.mm
@@ -48,5 +48,5 @@
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
     return !confirmation.presentingViewController;
   }));
-  EXPECT_TRUE(dismissal_delegate().HasUIBeenDismissed(request));
+  EXPECT_TRUE(delegate().HasUIBeenDismissed(request));
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator.mm
index 053bc2be..568e874 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator.mm
@@ -10,7 +10,7 @@
 #include "ios/chrome/browser/overlays/public/web_content_area/java_script_dialog_source.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator+subclassing.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_mediator.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -70,9 +70,14 @@
       UIModalTransitionStyleCrossDissolve;
   self.mediator = [self newMediator];
   self.mediator.consumer = self.alertViewController;
-  [self.baseViewController presentViewController:self.alertViewController
-                                        animated:animated
-                                      completion:nil];
+  __weak __typeof__(self) weakSelf = self;
+  [self.baseViewController
+      presentViewController:self.alertViewController
+                   animated:animated
+                 completion:^{
+                   weakSelf.delegate->OverlayUIDidFinishPresentation(
+                       weakSelf.request);
+                 }];
   self.started = YES;
 }
 
@@ -87,8 +92,8 @@
                            if (!strongSelf)
                              return;
                            strongSelf.alertViewController = nil;
-                           strongSelf.dismissalDelegate
-                               ->OverlayUIDidFinishDismissal(weakSelf.request);
+                           strongSelf.delegate->OverlayUIDidFinishDismissal(
+                               weakSelf.request);
                          }];
   self.started = NO;
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_coordinator_unittest.mm
index c84ed3d..eda2d53 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_coordinator_unittest.mm
@@ -46,5 +46,5 @@
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
     return !prompt.presentingViewController;
   }));
-  EXPECT_TRUE(dismissal_delegate().HasUIBeenDismissed(request));
+  EXPECT_TRUE(delegate().HasUIBeenDismissed(request));
 }
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_mediator.mm
index 2cfa332..b11f9f2 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_prompt_overlay_mediator.mm
@@ -12,7 +12,7 @@
 #import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_view_controller.h"
 #import "ios/chrome/browser/ui/elements/text_field_configuration.h"
-#import "ios/chrome/browser/ui/overlays/overlay_ui_dismissal_delegate.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_blocking_action.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/java_script_dialog_overlay_coordinator+subclassing.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.h b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.h
index 52acfac4..3368dba 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.h
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.h
@@ -11,7 +11,7 @@
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/overlays/public/overlay_request.h"
-#include "ios/chrome/browser/ui/overlays/test/fake_overlay_ui_dismissal_delegate.h"
+#include "ios/chrome/browser/ui/overlays/test/fake_overlay_request_coordinator_delegate.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/test/scoped_key_window.h"
@@ -29,8 +29,8 @@
   ~JavaScriptDialogOverlayCoordinatorTest() override;
 
   // Accessors:
-  const FakeDismissalDelegate& dismissal_delegate() const {
-    return dismissal_delegate_;
+  const FakeOverlayRequestCoordinatorDelegate& delegate() const {
+    return delegate_;
   }
 
   // Sets the request for the test.  Setting to a new value will create a
@@ -58,7 +58,7 @@
   std::unique_ptr<Browser> browser_;
   UIViewController* base_view_controller;
   std::unique_ptr<OverlayRequest> request_;
-  FakeDismissalDelegate dismissal_delegate_;
+  FakeOverlayRequestCoordinatorDelegate delegate_;
   OverlayRequestCoordinator* coordinator_;
 };
 
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.mm b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.mm
index 9e5254e..5deb3e3 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test/java_script_dialog_overlay_coordinator_test.mm
@@ -47,7 +47,7 @@
                                     initWithBrowser:browser_.get()
           supportedOverlayRequestCoordinatorClasses:coordinator_classes];
   coordinator_ = [factory newCoordinatorForRequest:request_.get()
-                                 dismissalDelegate:&dismissal_delegate_
+                                          delegate:&delegate_
                                 baseViewController:base_view_controller];
 }
 
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
index a1defd6..aaba345 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
@@ -270,15 +270,13 @@
     _addTabActionImageView = [[UIImageView alloc] init];
     _addTabActionImageView.image = [[UIImage imageNamed:kNewTabActionImage]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    _addTabActionImageView.tintColor =
-        [UIColor colorNamed:@"tab_toolbar_button_color"];
+    _addTabActionImageView.tintColor = [UIColor colorNamed:kToolbarButtonColor];
     [_addTabActionImageView sizeToFit];
     [self addSubview:_addTabActionImageView];
     _reloadActionImageView = [[UIImageView alloc] init];
     _reloadActionImageView.image = [[UIImage imageNamed:kReloadActionImage]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    _reloadActionImageView.tintColor =
-        [UIColor colorNamed:@"tab_toolbar_button_color"];
+    _reloadActionImageView.tintColor = [UIColor colorNamed:kToolbarButtonColor];
     [_reloadActionImageView sizeToFit];
     if (UseRTLLayout())
       [_reloadActionImageView setTransform:CGAffineTransformMakeScale(-1, 1)];
@@ -287,7 +285,7 @@
     _closeTabActionImageView.image = [[UIImage imageNamed:kCloseActionImage]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
     _closeTabActionImageView.tintColor =
-        [UIColor colorNamed:@"tab_toolbar_button_color"];
+        [UIColor colorNamed:kToolbarButtonColor];
     [_closeTabActionImageView sizeToFit];
     [self addSubview:_closeTabActionImageView];
 
@@ -320,7 +318,7 @@
     _addTabLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _addTabLabel.adjustsFontForContentSizeCategory = NO;
-    _addTabLabel.textColor = [UIColor colorNamed:@"tab_toolbar_button_color"];
+    _addTabLabel.textColor = [UIColor colorNamed:kToolbarButtonColor];
     _addTabLabel.text =
         l10n_util::GetNSString(IDS_IOS_OVERSCROLL_NEW_TAB_LABEL);
     [self addSubview:_addTabLabel];
@@ -332,7 +330,7 @@
     _reloadLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _reloadLabel.adjustsFontForContentSizeCategory = NO;
-    _reloadLabel.textColor = [UIColor colorNamed:@"tab_toolbar_button_color"];
+    _reloadLabel.textColor = [UIColor colorNamed:kToolbarButtonColor];
     _reloadLabel.text = l10n_util::GetNSString(IDS_IOS_OVERSCROLL_RELOAD_LABEL);
     [self addSubview:_reloadLabel];
     _closeTabLabel = [[UILabel alloc] init];
@@ -343,7 +341,7 @@
     _closeTabLabel.font =
         [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
     _closeTabLabel.adjustsFontForContentSizeCategory = NO;
-    _closeTabLabel.textColor = [UIColor colorNamed:@"tab_toolbar_button_color"];
+    _closeTabLabel.textColor = [UIColor colorNamed:kToolbarButtonColor];
     _closeTabLabel.text =
         l10n_util::GetNSString(IDS_IOS_OVERSCROLL_CLOSE_TAB_LABEL);
     [self addSubview:_closeTabLabel];
@@ -977,8 +975,7 @@
 
   // Fallback for iOS 12.
   if (self.incognito) {
-    UIColor* buttonColor =
-        [UIColor colorNamed:@"tab_toolbar_button_color_incognito"];
+    UIColor* buttonColor = [UIColor colorNamed:kToolbarButtonDarkColor];
     _addTabActionImageView.tintColor = buttonColor;
     _reloadActionImageView.tintColor = buttonColor;
     _closeTabActionImageView.tintColor = buttonColor;
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
index 582e51b..6d4aaef5 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_item.mm
@@ -90,15 +90,7 @@
   URLCell.metadataLabel.text = self.distillationSizeText;
   URLCell.cellUniqueIdentifier = base::SysUTF8ToNSString(self.entryURL.host());
   URLCell.accessibilityTraits |= UIAccessibilityTraitButton;
-  // If the background color specified by the styler is opaque, use it as the
-  // subview backround colors as well.
-  UIColor* backgroundColor = styler.tableViewBackgroundColor;
-  if (AreCGFloatsEqual(CGColorGetAlpha(backgroundColor.CGColor), 1.0)) {
-    URLCell.faviconContainerView.backgroundColor = backgroundColor;
-    URLCell.titleLabel.backgroundColor = backgroundColor;
-    URLCell.URLLabel.backgroundColor = backgroundColor;
-    URLCell.metadataLabel.backgroundColor = backgroundColor;
-  }
+
   if (styler.cellTitleColor)
     URLCell.titleLabel.textColor = styler.cellTitleColor;
   [URLCell.faviconView configureWithAttributes:self.attributes];
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
index 8b9a052..a1e754cc 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -65,15 +65,6 @@
   cell.metadataLabel.text = self.metadata;
   cell.cellUniqueIdentifier = self.uniqueIdentifier;
   cell.accessibilityTraits |= UIAccessibilityTraitButton;
-  // If the background color specified by the styler is opaque, use it as the
-  // subview backround colors as well.
-  UIColor* backgroundColor = styler.tableViewBackgroundColor;
-  if (AreCGFloatsEqual(CGColorGetAlpha(backgroundColor.CGColor), 1.0)) {
-    cell.faviconContainerView.backgroundColor = styler.tableViewBackgroundColor;
-    cell.titleLabel.backgroundColor = styler.tableViewBackgroundColor;
-    cell.URLLabel.backgroundColor = styler.tableViewBackgroundColor;
-    cell.metadataLabel.backgroundColor = styler.tableViewBackgroundColor;
-  }
 
   if (styler.cellTitleColor)
     cell.titleLabel.textColor = styler.cellTitleColor;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
index 0af59da..46c00fd 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
@@ -78,21 +78,6 @@
   EXPECT_FALSE(URLCell.metadataLabel.hidden);
 }
 
-TEST_F(TableViewURLItemTest, ConfigureCellWithStyler) {
-  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
-  TableViewURLCell* cell = [[[item cellClass] alloc] init];
-  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
-
-  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-  UIColor* testColor = UIColor.redColor;
-  styler.tableViewBackgroundColor = testColor;
-  [item configureCell:cell withStyler:styler];
-  EXPECT_NSEQ(testColor, cell.faviconContainerView.backgroundColor);
-  EXPECT_NSEQ(testColor, cell.titleLabel.backgroundColor);
-  EXPECT_NSEQ(testColor, cell.URLLabel.backgroundColor);
-  EXPECT_NSEQ(testColor, cell.metadataLabel.backgroundColor);
-}
-
 // Tests that the suppelemental URL text is appended to the hostname when there
 // is a title.
 TEST_F(TableViewURLItemTest, SupplementalURLTextWithTitle) {
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_styler.h b/ios/chrome/browser/ui/table_view/chrome_table_view_styler.h
index 376d5c21..c0f7980 100644
--- a/ios/chrome/browser/ui/table_view/chrome_table_view_styler.h
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_styler.h
@@ -9,9 +9,7 @@
 
 @interface ChromeTableViewStyler : NSObject
 
-// The background color for the table view and its cells. If this is set to an
-// opaque color, cells can choose to make themselves opaque and draw their own
-// background as a performance optimization.
+// The background color for the table view.
 @property(nonatomic, readwrite, strong) UIColor* tableViewBackgroundColor;
 // The background color for the cell. It overrides |tableViewBackgroundColor|
 // for the cell background if it is not nil.
diff --git a/ios/chrome/browser/ui/tabs/BUILD.gn b/ios/chrome/browser/ui/tabs/BUILD.gn
index 454bcbf67..8f7a5de 100644
--- a/ios/chrome/browser/ui/tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/tabs/BUILD.gn
@@ -121,7 +121,6 @@
     ":tabs",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
-    "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/third_party/earl_grey:earl_grey+link",
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm b/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
index 5d4865f2..ecbe1d92 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
@@ -8,7 +8,6 @@
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/ui/tabs/tab_view.h"
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -36,7 +35,7 @@
 // Test switching tabs using the tab strip.
 - (void)testTabStripSwitchTabs {
   // Only iPad has a tab strip.
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     return;
   }
 
diff --git a/ios/chrome/browser/ui/toolbar/BUILD.gn b/ios/chrome/browser/ui/toolbar/BUILD.gn
index 1b44b811..6d8d2a9 100644
--- a/ios/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/BUILD.gn
@@ -82,7 +82,6 @@
     "toolbar_progress_bar.mm",
   ]
   deps = [
-    "resources:tab_toolbar_shadow_color",
     "//base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/activity_services/requirements",
@@ -98,6 +97,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common:timing",
+    "//ios/chrome/common/colors",
     "//ios/chrome/common/ui_util",
     "//ios/third_party/material_components_ios",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
index be36564..0bb265dc 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
@@ -444,7 +444,7 @@
 
 // Tests that tapping a button cancels the focus on the omnibox.
 - (void)testCancelOmniboxEdit {
-  if (IsCompactWidth()) {
+  if ([ChromeEarlGrey isCompactWidth]) {
     EARL_GREY_TEST_SKIPPED(@"No button to tap in compact width.");
   }
 
diff --git a/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn b/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
index 971dc2a..fda04ab 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/buttons/BUILD.gn
@@ -25,10 +25,8 @@
     "toolbar_type.h",
   ]
   deps = [
-    "resources:tab_toolbar_button_color",
     "resources:tab_toolbar_button_color_highlighted",
     "resources:tab_toolbar_button_color_highlighted_incognito",
-    "resources:tab_toolbar_button_color_incognito",
     "resources:tab_toolbar_button_halo_color",
     "resources:tab_toolbar_button_halo_color_incognito",
     "resources:toolbar_back",
diff --git a/ios/chrome/browser/ui/toolbar/buttons/resources/BUILD.gn b/ios/chrome/browser/ui/toolbar/buttons/resources/BUILD.gn
index 0f5b2ee9..0d5e7e4 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/toolbar/buttons/resources/BUILD.gn
@@ -4,18 +4,6 @@
 
 import("//build/config/ios/asset_catalog.gni")
 
-colorset("tab_toolbar_button_color") {
-  sources = [
-    "tab_toolbar_button_color.colorset/Contents.json",
-  ]
-}
-
-colorset("tab_toolbar_button_color_incognito") {
-  sources = [
-    "tab_toolbar_button_color_incognito.colorset/Contents.json",
-  ]
-}
-
 colorset("tab_toolbar_button_color_highlighted") {
   sources = [
     "tab_toolbar_button_color_highlighted.colorset/Contents.json",
diff --git a/ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.mm b/ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.mm
index c3d9f8b..baff1ed 100644
--- a/ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.mm
+++ b/ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.mm
@@ -42,8 +42,8 @@
 
 - (UIColor*)buttonsTintColor {
   return color::IncognitoDynamicColor(
-      self.style == INCOGNITO, [UIColor colorNamed:@"tab_toolbar_button_color"],
-      [UIColor colorNamed:@"tab_toolbar_button_color_incognito"]);
+      self.style == INCOGNITO, [UIColor colorNamed:kToolbarButtonColor],
+      [UIColor colorNamed:kToolbarButtonDarkColor]);
 }
 
 - (UIColor*)buttonsTintColorHighlighted {
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
index 290f0e41..6e7cdb8b 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view.mm
@@ -17,6 +17,7 @@
 #import "ios/chrome/browser/ui/toolbar/toolbar_progress_bar.h"
 #import "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ui/gfx/ios/uikit_util.h"
 
@@ -278,8 +279,7 @@
 // Sets the separator up.
 - (void)setUpSeparator {
   self.separator = [[UIView alloc] init];
-  self.separator.backgroundColor =
-      [UIColor colorNamed:@"tab_toolbar_shadow_color"];
+  self.separator.backgroundColor = [UIColor colorNamed:kToolbarShadowColor];
   self.separator.translatesAutoresizingMaskIntoConstraints = NO;
   [self addSubview:self.separator];
 }
diff --git a/ios/chrome/browser/ui/toolbar/resources/BUILD.gn b/ios/chrome/browser/ui/toolbar/resources/BUILD.gn
deleted file mode 100644
index f614247f..0000000
--- a/ios/chrome/browser/ui/toolbar/resources/BUILD.gn
+++ /dev/null
@@ -1,11 +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.
-
-import("//build/config/ios/asset_catalog.gni")
-
-colorset("tab_toolbar_shadow_color") {
-  sources = [
-    "tab_toolbar_shadow_color.colorset/Contents.json",
-  ]
-}
diff --git a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
index 329a972..bcbb312 100644
--- a/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
+++ b/ios/chrome/browser/ui/toolbar/secondary_toolbar_view.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
 #import "ios/chrome/browser/ui/toolbar_container/toolbar_collapsing.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ui/gfx/ios/uikit_util.h"
 
@@ -125,8 +126,7 @@
   ];
 
   self.separator = [[UIView alloc] init];
-  self.separator.backgroundColor =
-      [UIColor colorNamed:@"tab_toolbar_shadow_color"];
+  self.separator.backgroundColor = [UIColor colorNamed:kToolbarShadowColor];
   self.separator.translatesAutoresizingMaskIntoConstraints = NO;
   [self addSubview:self.separator];
 
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 9d34780..830a5a8a 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -9,7 +9,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
diff --git a/ios/chrome/browser/ui/translate/BUILD.gn b/ios/chrome/browser/ui/translate/BUILD.gn
index 0bb7481a..16de7a7 100644
--- a/ios/chrome/browser/ui/translate/BUILD.gn
+++ b/ios/chrome/browser/ui/translate/BUILD.gn
@@ -57,11 +57,13 @@
     "//ios/chrome/browser/ui/fullscreen:ui",
     "//ios/chrome/browser/ui/infobars:infobars_ui",
     "//ios/chrome/browser/ui/toolbar/buttons",
+    "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/translate/resources:translate_dismiss",
     "//ios/chrome/browser/ui/translate/resources:translate_icon",
     "//ios/chrome/browser/ui/translate/resources:translate_options",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common:common_extension",
+    "//ios/chrome/common/colors:colors",
     "//ios/chrome/common/ui_util",
     "//ios/third_party/material_components_ios",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/translate/resources/BUILD.gn b/ios/chrome/browser/ui/translate/resources/BUILD.gn
index 2666207..bb950ea 100644
--- a/ios/chrome/browser/ui/translate/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/translate/resources/BUILD.gn
@@ -7,7 +7,6 @@
 imageset("translate_icon") {
   sources = [
     "translate_icon.imageset/Contents.json",
-    "translate_icon.imageset/translate_icon.png",
     "translate_icon.imageset/translate_icon@2x.png",
     "translate_icon.imageset/translate_icon@3x.png",
   ]
diff --git a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/Contents.json b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/Contents.json
index 8637523..66ca5d8 100644
--- a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/Contents.json
+++ b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/Contents.json
@@ -1,23 +1,18 @@
 {
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "translate_icon.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "translate_icon@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "translate_icon@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "translate_icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "translate_icon@3x.png",
+      "scale" : "3x"
     }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
 }
diff --git a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon.png b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon.png
deleted file mode 100644
index 11d7a57..0000000
--- a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@2x.png b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@2x.png
index 42ad442..b7bd9842 100644
--- a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@2x.png
+++ b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@3x.png b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@3x.png
index 76bba6c..09b11e2 100644
--- a/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@3x.png
+++ b/ios/chrome/browser/ui/translate/resources/translate_icon.imageset/translate_icon@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
index a4d20ec..db246f9 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
@@ -131,11 +131,10 @@
   [self addSubview:self.languagesScrollView];
 
   self.gradientLayer = [CAGradientLayer layer];
-  self.gradientLayer.colors =
-      [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor],
-                                (id)[[UIColor whiteColor] CGColor],
-                                (id)[[UIColor whiteColor] CGColor],
-                                (id)[[UIColor clearColor] CGColor], nil];
+  self.gradientLayer.colors = @[
+    (id)UIColor.clearColor.CGColor, (id)UIColor.whiteColor.CGColor,
+    (id)UIColor.whiteColor.CGColor, (id)UIColor.clearColor.CGColor
+  ];
   // The following two lines make the gradient horizontal.
   self.gradientLayer.startPoint = CGPointMake(0.0, 0.5);
   self.gradientLayer.endPoint = CGPointMake(1.0, 0.5);
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
index 3c0c09c9..b277769 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.h"
 #import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_view_delegate.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 #import "ios/chrome/common/highlight_button.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MaterialActivityIndicator.h"
@@ -81,8 +82,7 @@
   MDCActivityIndicator* activityIndicator = [[MDCActivityIndicator alloc] init];
   self.activityIndicator = activityIndicator;
   self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
-  self.activityIndicator.cycleColors =
-      @[ [[MDCPalette cr_bluePalette] tint500] ];
+  self.activityIndicator.cycleColors = @[ [UIColor colorNamed:kBlueColor] ];
   [self.activityIndicator setRadius:kActivityIndicatorRadius];
   [self addSubview:self.activityIndicator];
 
@@ -132,8 +132,8 @@
 // Returns the button's title color depending on the state.
 - (UIColor*)titleColor {
   return self.state == TranslateInfobarLanguageTabViewStateSelected
-             ? [[MDCPalette cr_bluePalette] tint500]
-             : [[MDCPalette greyPalette] tint600];
+             ? [UIColor colorNamed:kBlueColor]
+             : [UIColor colorNamed:kTextSecondaryColor];
 }
 
 // Returns the button's accessibility traits depending on the state.
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
index a4e4740..24c04766 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
+#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.h"
 #import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view_delegate.h"
 #import "ios/chrome/browser/ui/translate/translate_infobar_view_delegate.h"
@@ -19,9 +20,11 @@
 #import "ios/chrome/browser/ui/util/layout_guide_names.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
+#import "ui/gfx/ios/uikit_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -213,9 +216,22 @@
                                     a11yAnnoucement);
   }
 
-  self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
+  self.backgroundColor = [UIColor colorNamed:kBackgroundColor];
   id<LayoutGuideProvider> safeAreaLayoutGuide = self.safeAreaLayoutGuide;
 
+  UIView* separator = [[UIView alloc] init];
+  separator.translatesAutoresizingMaskIntoConstraints = NO;
+  separator.backgroundColor = [UIColor colorNamed:kToolbarShadowColor];
+
+  [self addSubview:separator];
+  CGFloat toolbarHeight = ui::AlignValueToUpperPixel(kToolbarSeparatorHeight);
+  [NSLayoutConstraint activateConstraints:@[
+    [separator.heightAnchor constraintEqualToConstant:toolbarHeight],
+    [self.topAnchor constraintEqualToAnchor:separator.bottomAnchor],
+    [self.leadingAnchor constraintEqualToAnchor:separator.leadingAnchor],
+    [self.trailingAnchor constraintEqualToAnchor:separator.trailingAnchor],
+  ]];
+
   // The Content view. Holds all the other subviews.
   UIView* contentView = [[UIView alloc] init];
   contentView.translatesAutoresizingMaskIntoConstraints = NO;
@@ -232,11 +248,11 @@
   ]];
 
   UIImage* icon = [[UIImage imageNamed:@"translate_icon"]
-      resizableImageWithCapInsets:UIEdgeInsetsZero
-                     resizingMode:UIImageResizingModeStretch];
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   UIImageView* iconView = [[UIImageView alloc] initWithImage:icon];
   self.iconView = iconView;
   self.iconView.translatesAutoresizingMaskIntoConstraints = NO;
+  self.iconView.tintColor = [UIColor colorNamed:kBlueColor];
   [contentView addSubview:self.iconView];
 
   TranslateInfobarLanguageTabStripView* languagesView =
diff --git a/ios/chrome/browser/web/visible_url_egtest.mm b/ios/chrome/browser/web/visible_url_egtest.mm
index 4c8dffc..19c076c 100644
--- a/ios/chrome/browser/web/visible_url_egtest.mm
+++ b/ios/chrome/browser/web/visible_url_egtest.mm
@@ -18,6 +18,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #include "ios/web/public/test/http_server/html_response_provider.h"
 #import "ios/web/public/test/http_server/http_server.h"
@@ -104,8 +105,7 @@
 // Spec of the last request URL that reached the server.
 @property(nonatomic, copy, readonly) NSString* lastRequestURLSpec;
 
-// Pauses response server and disables EG synchronization if |paused| is YES.
-// Pending navigation will not complete until server is unpaused.
+// Pauses response server.
 - (void)setServerPaused:(BOOL)paused;
 
 // Waits until |_responseProvider| receives a request with the given |URL|.
@@ -151,15 +151,6 @@
   [ChromeEarlGrey loadURL:_testURL2];
 }
 
-- (void)tearDown {
-  // This test case disables synchronization, so make sure that it is enabled
-  // if that test has failed and did not enable it back.
-  [[GREYConfiguration sharedInstance]
-          setValue:@YES
-      forConfigKey:kGREYConfigKeySynchronizationEnabled];
-  [super tearDown];
-}
-
 #pragma mark -
 #pragma mark Tests
 
@@ -174,19 +165,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the back button in the toolbar and verify that URL2 (committed URL) is
-  // displayed even though URL1 is a pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the back button in the toolbar and verify that URL2 (committed URL)
+    // is displayed even though URL1 is a pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -195,19 +191,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the forward button in the toolbar and verify that URL1 (committed URL)
-  // is displayed even though URL2 is a pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
-      performAction:grey_tap()];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the forward button in the toolbar and verify that URL1 (committed
+    // URL) is displayed even though URL2 is a pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
+        performAction:grey_tap()];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL2 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL2 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -224,13 +225,14 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
 
-  // Re-enable synchronization here to synchronize EarlGrey LongPress and Tap
-  // actions.
-  [[GREYConfiguration sharedInstance]
-          setValue:@(YES)
-      forConfigKey:kGREYConfigKeySynchronizationEnabled];
+  // Pauses response server and disables EG synchronization.
+  // Pending navigation will not complete until server is unpaused.
+  {
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
+  }
+
   // Go back in history and verify that URL2 (committed URL) is displayed even
   // though URL1 is a pending URL.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
@@ -240,17 +242,18 @@
   [[EarlGrey selectElementWithMatcher:grey_text(URL1Title)]
       performAction:grey_tap()];
 
-  [[GREYConfiguration sharedInstance]
-          setValue:@(NO)
-      forConfigKey:kGREYConfigKeySynchronizationEnabled];
+  {
+    // Disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
-
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -267,27 +270,30 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
+    [self setServerPaused:YES];
 
-  // Tap the back button, stop pending navigation and reload.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  // On iPhone Stop/Reload button is a part of tools menu, so open it.
-  if (![ChromeEarlGrey isIPadIdiom]) {
-    // Enable EG synchronization to make test wait for popover animations.
-    [[GREYConfiguration sharedInstance]
-            setValue:@YES
-        forConfigKey:kGREYConfigKeySynchronizationEnabled];
-    [ChromeEarlGreyUI openToolsMenu];
+    // Tap the back button, stop pending navigation and reload.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    // On iPhone Stop/Reload button is a part of tools menu, so open it.
+    if (![ChromeEarlGrey isIPadIdiom]) {
+      // Enable EG synchronization to make test wait for popover animations.
+      disabler.reset();
+      [ChromeEarlGreyUI openToolsMenu];
+    }
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::StopButton()]
+        performAction:grey_tap()];
+    [ChromeEarlGreyUI reload];
+
+    // Makes server respond.
+    [self setServerPaused:NO];
   }
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::StopButton()]
-      performAction:grey_tap()];
-  [ChromeEarlGreyUI reload];
-
-  // Make server respond and verify that page2 was reloaded, not page1.
-  [self setServerPaused:NO];
+  // Verifies that page2 was reloaded, not page1.
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -304,19 +310,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the back button on the page and verify that URL2 (committed URL) is
-  // displayed even though URL1 is a pending URL.
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the back button on the page and verify that URL2 (committed URL) is
+    // displayed even though URL1 is a pending URL.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -325,19 +336,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the forward button on the page and verify that URL1 (committed URL)
-  // is displayed even though URL2 is a pending URL.
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoForwardLink)];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the forward button on the page and verify that URL1 (committed URL)
+    // is displayed even though URL2 is a pending URL.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoForwardLink)];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL2 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL2 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -354,19 +370,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the go negative delta button on the page and verify that URL2
-  // (committed URL) is displayed even though URL1 is a pending URL.
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoNegativeDeltaLink)];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the go negative delta button on the page and verify that URL2
+    // (committed URL) is displayed even though URL1 is a pending URL.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoNegativeDeltaLink)];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -375,19 +396,24 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap go positive delta button on the page and verify that URL1 (committed
-  // URL) is displayed even though URL2 is a pending URL.
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoPositiveDeltaLink)];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap go positive delta button on the page and verify that URL1 (committed
+    // URL) is displayed even though URL2 is a pending URL.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoPositiveDeltaLink)];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL2],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL2 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL2 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -404,31 +430,34 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    std::unique_ptr<ScopedSynchronizationDisabler> disabler =
+        std::make_unique<ScopedSynchronizationDisabler>();
 
-  // Start reloading the page.
-  if (![ChromeEarlGrey isIPadIdiom]) {
-    // Enable EG synchronization to make test wait for popover animations.
-    [[GREYConfiguration sharedInstance]
-            setValue:@YES
-        forConfigKey:kGREYConfigKeySynchronizationEnabled];
-    [ChromeEarlGreyUI openToolsMenu];
+    [self setServerPaused:YES];
+
+    // Start reloading the page.
+    if (![ChromeEarlGrey isIPadIdiom]) {
+      // Enable EG synchronization to make test wait for popover animations.
+      disabler.reset();
+      [ChromeEarlGreyUI openToolsMenu];
+    }
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::ReloadButton()]
+        performAction:grey_tap()];
+
+    // Do not wait until reload is finished, tap the back button in the toolbar
+    // and verify that URL2 (committed URL) is displayed even though URL1 is a
+    // pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    // TODO(crbug.com/724560): Re-evaluate if necessary to check receiving URL1
+    // request here.
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
+
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
   }
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::ReloadButton()]
-      performAction:grey_tap()];
-
-  // Do not wait until reload is finished, tap the back button in the toolbar
-  // and verify that URL2 (committed URL) is displayed even though URL1 is a
-  // pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  // TODO(crbug.com/724560): Re-evaluate if necessary to check receiving URL1
-  // request here.
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
-
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -453,23 +482,29 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Start renderer initiated navigation.
-  [ChromeEarlGrey tapWebStateElementWithID:base::SysUTF8ToNSString(kPage3Link)];
+    // Start renderer initiated navigation.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kPage3Link)];
 
-  // Do not wait until renderer-initiated navigation is finished, tap the back
-  // button in the toolbar and verify that URL2 (committed URL) is displayed
-  // even though URL1 is a pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  // TODO(crbug.com/724560): Re-evaluate if necessary to check receiving URL1
-  // request here.
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Do not wait until renderer-initiated navigation is finished, tap the back
+    // button in the toolbar and verify that URL2 (committed URL) is displayed
+    // even though URL1 is a pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    // TODO(crbug.com/724560): Re-evaluate if necessary to check receiving URL1
+    // request here.
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -487,24 +522,30 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage2];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the back button in the toolbar and verify that URL2 (committed URL) is
-  // displayed even though URL1 is a pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
-             @"Last request URL: %@", self.lastRequestURLSpec);
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the back button in the toolbar and verify that URL2 (committed URL)
+    // is displayed even though URL1 is a pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    GREYAssert([self waitForServerToReceiveRequestWithURL:_testURL1],
+               @"Last request URL: %@", self.lastRequestURLSpec);
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Interrupt back navigation with renderer initiated navigation.
-  [ChromeEarlGrey tapWebStateElementWithID:base::SysUTF8ToNSString(kPage3Link)];
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Interrupt back navigation with renderer initiated navigation.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kPage3Link)];
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL2.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage3];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL3.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -524,22 +565,27 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage3];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the back button twice in the toolbar and verify that URL3 (committed
-  // URL) is displayed even though URL1 is a pending URL.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  // Server will receive only one request either for |_testURL2| or for
-  // |_testURL1| depending on load timing and then will pause. So there is no
-  // need to wait for particular request.
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL3.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the back button twice in the toolbar and verify that URL3 (committed
+    // URL) is displayed even though URL1 is a pending URL.
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+        performAction:grey_tap()];
+    // Server will receive only one request either for |_testURL2| or for
+    // |_testURL1| depending on load timing and then will pause. So there is no
+    // need to wait for particular request.
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL3.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
       assertWithMatcher:grey_notNil()];
@@ -590,22 +636,27 @@
   // verify omnibox state before server starts responding.
   GREYAssert(PurgeCachedWebViewPages(), @"Pages were not purged");
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage3];
-  [self setServerPaused:YES];
+  {
+    // Pauses response server and disables EG synchronization.
+    // Pending navigation will not complete until server is unpaused.
+    ScopedSynchronizationDisabler disabler;
+    [self setServerPaused:YES];
 
-  // Tap the back button twice on the page and verify that URL3 (committed URL)
-  // is displayed even though URL1 is a pending URL.
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
-  [ChromeEarlGrey
-      tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
-  // Server will receive only one request either for |_testURL2| or for
-  // |_testURL1| depending on load timing and then will pause. So there is no
-  // need to wait for particular request.
-  [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL3.GetContent())]
-      assertWithMatcher:grey_notNil()];
+    // Tap the back button twice on the page and verify that URL3 (committed
+    // URL) is displayed even though URL1 is a pending URL.
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
+    [ChromeEarlGrey
+        tapWebStateElementWithID:base::SysUTF8ToNSString(kGoBackLink)];
+    // Server will receive only one request either for |_testURL2| or for
+    // |_testURL1| depending on load timing and then will pause. So there is no
+    // need to wait for particular request.
+    [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL3.GetContent())]
+        assertWithMatcher:grey_notNil()];
 
-  // Make server respond so URL1 becomes committed.
-  [self setServerPaused:NO];
+    // Make server respond so URL1 becomes committed.
+    [self setServerPaused:NO];
+  }
   // TODO(crbug.com/866406): fix the test to have documented behavior.
   [ChromeEarlGrey waitForWebStateContainingText:kTestPage1];
   [[EarlGrey selectElementWithMatcher:OmniboxText(_testURL1.GetContent())]
@@ -620,14 +671,6 @@
 }
 
 - (void)setServerPaused:(BOOL)paused {
-  // Disable EG synchronization if server is paused so the framework does not
-  // wait until the tab loading spinner or progress bar indicator becomes idle
-  // (which will not happen until server responds and the navigation is
-  // finished).
-  [[GREYConfiguration sharedInstance]
-          setValue:@(!paused)
-      forConfigKey:kGREYConfigKeySynchronizationEnabled];
-
   _responseProvider->set_paused(paused);
 }
 
diff --git a/ios/chrome/common/colors/resources/BUILD.gn b/ios/chrome/common/colors/resources/BUILD.gn
index 3557e64cc7..5d5393e 100644
--- a/ios/chrome/common/colors/resources/BUILD.gn
+++ b/ios/chrome/common/colors/resources/BUILD.gn
@@ -32,6 +32,9 @@
     ":textfield_background_dark_color",
     ":textfield_placeholder_color",
     ":textfield_placeholder_dark_color",
+    ":toolbar_button_color",
+    ":toolbar_button_dark_color",
+    ":toolbar_shadow_color",
   ]
 }
 
@@ -59,6 +62,18 @@
   ]
 }
 
+colorset("close_button_color") {
+  sources = [
+    "close_button_color.colorset/Contents.json",
+  ]
+}
+
+colorset("close_button_dark_color") {
+  sources = [
+    "close_button_dark_color.colorset/Contents.json",
+  ]
+}
+
 colorset("disabled_tint_color") {
   sources = [
     "disabled_tint_color.colorset/Contents.json",
@@ -178,14 +193,21 @@
     "textfield_background_dark_color.colorset/Contents.json",
   ]
 }
-colorset("close_button_color") {
+
+colorset("toolbar_button_color") {
   sources = [
-    "close_button_color.colorset/Contents.json",
+    "toolbar_button_color.colorset/Contents.json",
   ]
 }
 
-colorset("close_button_dark_color") {
+colorset("toolbar_button_dark_color") {
   sources = [
-    "close_button_dark_color.colorset/Contents.json",
+    "toolbar_button_dark_color.colorset/Contents.json",
+  ]
+}
+
+colorset("toolbar_shadow_color") {
+  sources = [
+    "toolbar_shadow_color.colorset/Contents.json",
   ]
 }
diff --git a/ios/chrome/browser/ui/toolbar/buttons/resources/tab_toolbar_button_color.colorset/Contents.json b/ios/chrome/common/colors/resources/toolbar_button_color.colorset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/toolbar/buttons/resources/tab_toolbar_button_color.colorset/Contents.json
rename to ios/chrome/common/colors/resources/toolbar_button_color.colorset/Contents.json
diff --git a/ios/chrome/browser/ui/toolbar/buttons/resources/tab_toolbar_button_color_incognito.colorset/Contents.json b/ios/chrome/common/colors/resources/toolbar_button_dark_color.colorset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/toolbar/buttons/resources/tab_toolbar_button_color_incognito.colorset/Contents.json
rename to ios/chrome/common/colors/resources/toolbar_button_dark_color.colorset/Contents.json
diff --git a/ios/chrome/browser/ui/toolbar/resources/tab_toolbar_shadow_color.colorset/Contents.json b/ios/chrome/common/colors/resources/toolbar_shadow_color.colorset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/toolbar/resources/tab_toolbar_shadow_color.colorset/Contents.json
rename to ios/chrome/common/colors/resources/toolbar_shadow_color.colorset/Contents.json
diff --git a/ios/chrome/common/colors/semantic_color_names.h b/ios/chrome/common/colors/semantic_color_names.h
index bb4b2bd3..1c84a92 100644
--- a/ios/chrome/common/colors/semantic_color_names.h
+++ b/ios/chrome/common/colors/semantic_color_names.h
@@ -24,6 +24,10 @@
 extern NSString* const kTextSecondaryColor;
 extern NSString* const kTextfieldBackgroundColor;
 extern NSString* const kTextfieldPlaceholderColor;
+// Color used for buttons on a toolbar.
+extern NSString* const kToolbarButtonColor;
+// Color used for a shadow/separator next to a toolbar.
+extern NSString* const kToolbarShadowColor;
 
 // Standard Colors
 
@@ -48,6 +52,7 @@
 extern NSString* const kTextSecondaryDarkColor;
 extern NSString* const kTextfieldBackgroundDarkColor;
 extern NSString* const kTextfieldPlaceholderDarkColor;
+extern NSString* const kToolbarButtonDarkColor;
 
 extern NSString* const kBlueDarkColor;
 extern NSString* const kGreenDarkColor;
diff --git a/ios/chrome/common/colors/semantic_color_names.mm b/ios/chrome/common/colors/semantic_color_names.mm
index 4a612ab..cbb7098 100644
--- a/ios/chrome/common/colors/semantic_color_names.mm
+++ b/ios/chrome/common/colors/semantic_color_names.mm
@@ -22,6 +22,8 @@
 NSString* const kTextSecondaryColor = @"text_secondary_color";
 NSString* const kTextfieldBackgroundColor = @"textfield_background_color";
 NSString* const kTextfieldPlaceholderColor = @"textfield_placeholder_color";
+NSString* const kToolbarButtonColor = @"toolbar_button_color";
+NSString* const kToolbarShadowColor = @"toolbar_shadow_color";
 
 #pragma mark - Standard Colors
 NSString* const kBlueColor = @"blue_color";
@@ -40,6 +42,7 @@
     @"textfield_background_dark_color";
 NSString* const kTextfieldPlaceholderDarkColor =
     @"textfield_placeholder_dark_color";
+NSString* const kToolbarButtonDarkColor = @"toolbar_button_dark_color";
 
 NSString* const kBlueDarkColor = @"blue_dark_color";
 NSString* const kGreenDarkColor = @"green_dark_color";
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 7cf6f02..28093ca 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -43,6 +43,10 @@
 // Returns YES if running on an iPad.
 - (BOOL)isIPadIdiom;
 
+// Returns YES if the main application window's rootViewController has a compact
+// horizontal size class.
+- (BOOL)isCompactWidth;
+
 #pragma mark - History Utilities (EG2)
 // Clears browsing history. Raises an EarlGrey exception if history is not
 // cleared within a timeout.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 7ccbf94..91d3df4 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -66,6 +66,19 @@
   return idiom == UIUserInterfaceIdiomPad;
 }
 
+- (BOOL)isCompactWidth {
+#if defined(CHROME_EARL_GREY_1)
+  UIUserInterfaceSizeClass horizontalSpace =
+      [[[[UIApplication sharedApplication] keyWindow] traitCollection]
+          horizontalSizeClass];
+#elif defined(CHROME_EARL_GREY_2)
+  UIUserInterfaceSizeClass horizontalSpace =
+      [[[[GREY_REMOTE_CLASS_IN_APP(UIApplication) sharedApplication] keyWindow]
+          traitCollection] horizontalSizeClass];
+#endif
+  return horizontalSpace == UIUserInterfaceSizeClassCompact;
+}
+
 #pragma mark - History Utilities (EG2)
 
 - (void)clearBrowsingHistory {
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm
index 611d9db..757ad20 100644
--- a/ios/chrome/test/earl_grey2/smoke_egtest.mm
+++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -210,4 +210,15 @@
                  @"SlimNavigationManager should be enabled");
 }
 
+// Tests isCompactWidth method in chrome_earl_grey.h.
+- (void)testisCompactWidth {
+  BOOL expectedIsCompactWidth =
+      [[[[GREY_REMOTE_CLASS_IN_APP(UIApplication) sharedApplication] keyWindow]
+          traitCollection] horizontalSizeClass] ==
+      UIUserInterfaceSizeClassCompact;
+  GREYAssertTrue([ChromeEarlGrey isCompactWidth] == expectedIsCompactWidth,
+                 @"isCompactWidth should return %@",
+                 expectedIsCompactWidth ? @"YES" : @"NO");
+}
+
 @end
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index dbef8f3..e4bff13d 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -108,7 +108,9 @@
   virtual GeolocationUpdaterProvider* GetGeolocationUpdaterProvider();
   // Returns risk data used in Wallet requests.
   virtual std::string GetRiskData();
-  // Creates and returns a new styled text field with the given |frame|.
+  // Creates and returns a new styled text field.
+  virtual UITextField* CreateStyledTextField() const NS_RETURNS_RETAINED;
+  // Deprecated.
   virtual UITextField<TextFieldStyling>* CreateStyledTextField(
       CGRect frame) const NS_RETURNS_RETAINED;
 
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index 1cf0fd5..6c5c9ab 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -68,6 +68,10 @@
   return std::string();
 }
 
+UITextField* ChromeBrowserProvider::CreateStyledTextField() const {
+  return nil;
+}
+
 UITextField<TextFieldStyling>* ChromeBrowserProvider::CreateStyledTextField(
     CGRect frame) const {
   return nil;
diff --git a/ios/public/provider/chrome/browser/test_chrome_browser_provider.h b/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
index e589774..a4a5b75 100644
--- a/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
@@ -26,6 +26,7 @@
   void SetChromeIdentityServiceForTesting(
       std::unique_ptr<ChromeIdentityService> service) override;
   ChromeIdentityService* GetChromeIdentityService() override;
+  UITextField* CreateStyledTextField() const override NS_RETURNS_RETAINED;
   UITextField<TextFieldStyling>* CreateStyledTextField(
       CGRect frame) const override NS_RETURNS_RETAINED;
   VoiceSearchProvider* GetVoiceSearchProvider() const override;
diff --git a/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm b/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
index c2bad88..b34b2cd9 100644
--- a/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
@@ -67,6 +67,10 @@
   return chrome_identity_service_.get();
 }
 
+UITextField* TestChromeBrowserProvider::CreateStyledTextField() const {
+  return [[UITextField alloc] initWithFrame:CGRectZero];
+}
+
 UITextField<TextFieldStyling>* TestChromeBrowserProvider::CreateStyledTextField(
     CGRect frame) const {
   return [[TestStyledTextField alloc] initWithFrame:frame];
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 3a22fdee..2034e1bd 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -375,3 +375,13 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_enable_lifo_write_scheduler,
           false)
+
+// When true, remove obsolete functionality intended to test IETF QUIC recovery.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_sent_packet_manager_cleanup,
+          false)
+
+// If true, QUIC will track max ack height in BandwidthSampler.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_track_ack_height_in_bandwidth_sampler,
+          false)
diff --git a/pdf/draw_utils/coordinates.cc b/pdf/draw_utils/coordinates.cc
index 4a62020..ef760be 100644
--- a/pdf/draw_utils/coordinates.cc
+++ b/pdf/draw_utils/coordinates.cc
@@ -55,39 +55,6 @@
   return two_up_insets;
 }
 
-pp::Rect GetLeftFillRect(const pp::Rect& page_rect,
-                         const PageInsetSizes& inset_sizes,
-                         int bottom_separator) {
-  DCHECK_GE(page_rect.x(), inset_sizes.left);
-
-  return pp::Rect(0, page_rect.y() - inset_sizes.top,
-                  page_rect.x() - inset_sizes.left,
-                  page_rect.height() + inset_sizes.top + inset_sizes.bottom +
-                      bottom_separator);
-}
-
-pp::Rect GetRightFillRect(const pp::Rect& page_rect,
-                          const PageInsetSizes& inset_sizes,
-                          int doc_width,
-                          int bottom_separator) {
-  int right_gap_x = page_rect.right() + inset_sizes.right;
-  DCHECK_GE(doc_width, right_gap_x);
-
-  return pp::Rect(right_gap_x, page_rect.y() - inset_sizes.top,
-                  doc_width - right_gap_x,
-                  page_rect.height() + inset_sizes.top + inset_sizes.bottom +
-                      bottom_separator);
-}
-
-pp::Rect GetBottomFillRect(const pp::Rect& page_rect,
-                           const PageInsetSizes& inset_sizes,
-                           int bottom_separator) {
-  return pp::Rect(page_rect.x() - inset_sizes.left,
-                  page_rect.bottom() + inset_sizes.bottom,
-                  page_rect.width() + inset_sizes.left + inset_sizes.right,
-                  bottom_separator);
-}
-
 pp::Rect GetRectForSingleView(const pp::Size& rect_size,
                               const pp::Size& document_size,
                               const PageInsetSizes& page_insets) {
@@ -121,6 +88,39 @@
       page_height + inset_sizes.top + inset_sizes.bottom + bottom_separator);
 }
 
+pp::Rect GetLeftFillRect(const pp::Rect& page_rect,
+                         const PageInsetSizes& inset_sizes,
+                         int bottom_separator) {
+  DCHECK_GE(page_rect.x(), inset_sizes.left);
+
+  return pp::Rect(0, page_rect.y() - inset_sizes.top,
+                  page_rect.x() - inset_sizes.left,
+                  page_rect.height() + inset_sizes.top + inset_sizes.bottom +
+                      bottom_separator);
+}
+
+pp::Rect GetRightFillRect(const pp::Rect& page_rect,
+                          const PageInsetSizes& inset_sizes,
+                          int doc_width,
+                          int bottom_separator) {
+  int right_gap_x = page_rect.right() + inset_sizes.right;
+  DCHECK_GE(doc_width, right_gap_x);
+
+  return pp::Rect(right_gap_x, page_rect.y() - inset_sizes.top,
+                  doc_width - right_gap_x,
+                  page_rect.height() + inset_sizes.top + inset_sizes.bottom +
+                      bottom_separator);
+}
+
+pp::Rect GetBottomFillRect(const pp::Rect& page_rect,
+                           const PageInsetSizes& inset_sizes,
+                           int bottom_separator) {
+  return pp::Rect(page_rect.x() - inset_sizes.left,
+                  page_rect.bottom() + inset_sizes.bottom,
+                  page_rect.width() + inset_sizes.left + inset_sizes.right,
+                  bottom_separator);
+}
+
 pp::Rect GetLeftRectForTwoUpView(const pp::Size& rect_size,
                                  const pp::Point& position,
                                  const PageInsetSizes& page_insets) {
diff --git a/pdf/draw_utils/coordinates.h b/pdf/draw_utils/coordinates.h
index 1a77b49..f6a4115 100644
--- a/pdf/draw_utils/coordinates.h
+++ b/pdf/draw_utils/coordinates.h
@@ -57,31 +57,6 @@
     const PageInsetSizes& single_view_insets,
     int horizontal_separator);
 
-// TODO (chinsenj): move Get*FillRect() functions to bottom of coordinates.h.
-// Given |page_rect| in document coordinates, |inset_sizes|, and
-// |bottom_separator|, return a pp::Rect object representing the gap on the
-// left side of the page created by insetting the page. I.e. the difference,
-// on the left side, between the initial |page_rect| and the |page_rect| inset
-// with |inset_sizes| (current value of |page_rect|).
-// The x coordinate of |page_rect| must be greater than or equal to
-// |inset_sizes.left|.
-pp::Rect GetLeftFillRect(const pp::Rect& page_rect,
-                         const PageInsetSizes& inset_sizes,
-                         int bottom_separator);
-
-// Same as GetLeftFillRect(), but for the right side of |page_rect| and also
-// depends on the |doc_width|. Additionally, |doc_width| must be greater than or
-// equal to the sum of |page_rect.right| and |inset_sizes.right|.
-pp::Rect GetRightFillRect(const pp::Rect& page_rect,
-                          const PageInsetSizes& inset_sizes,
-                          int doc_width,
-                          int bottom_separator);
-
-// Same as GetLeftFillRect(), but for the bottom side of |page_rect|.
-pp::Rect GetBottomFillRect(const pp::Rect& page_rect,
-                           const PageInsetSizes& inset_sizes,
-                           int bottom_separator);
-
 // Given |rect_size| and |document_size| create a horizontally centered
 // pp::Rect placed at the bottom of the current document, and then inset it with
 // |page_insets|.
@@ -108,6 +83,30 @@
                             int doc_width,
                             int bottom_separator);
 
+// Given |page_rect| in document coordinates, |inset_sizes|, and
+// |bottom_separator|, return a pp::Rect object representing the gap on the
+// left side of the page created by insetting the page. I.e. the difference,
+// on the left side, between the initial |page_rect| and the |page_rect| inset
+// with |inset_sizes| (current value of |page_rect|).
+// The x coordinate of |page_rect| must be greater than or equal to
+// |inset_sizes.left|.
+pp::Rect GetLeftFillRect(const pp::Rect& page_rect,
+                         const PageInsetSizes& inset_sizes,
+                         int bottom_separator);
+
+// Same as GetLeftFillRect(), but for the right side of |page_rect| and also
+// depends on the |doc_width|. Additionally, |doc_width| must be greater than or
+// equal to the sum of |page_rect.right| and |inset_sizes.right|.
+pp::Rect GetRightFillRect(const pp::Rect& page_rect,
+                          const PageInsetSizes& inset_sizes,
+                          int doc_width,
+                          int bottom_separator);
+
+// Same as GetLeftFillRect(), but for the bottom side of |page_rect|.
+pp::Rect GetBottomFillRect(const pp::Rect& page_rect,
+                           const PageInsetSizes& inset_sizes,
+                           int bottom_separator);
+
 // Given |rect_size|, create a pp::Rect where the top-right corner lies at
 // |position|, and then inset it with the corresponding values of |page_insets|,
 // i.e. inset on left side with |page_insets.left|.
diff --git a/pdf/draw_utils/coordinates_unittest.cc b/pdf/draw_utils/coordinates_unittest.cc
index ed221b8..1618f65 100644
--- a/pdf/draw_utils/coordinates_unittest.cc
+++ b/pdf/draw_utils/coordinates_unittest.cc
@@ -126,6 +126,63 @@
       GetPageInsetsForTwoUpView(1, 4, kSingleViewInsets, kHorizontalSeparator));
 }
 
+TEST(CoordinateTest, GetRectForSingleView) {
+  // Test portrait pages.
+  CompareRect({55, 503, 190, 390},
+              GetRectForSingleView({200, 400}, {300, 500}, kSingleViewInsets));
+  CompareRect({55, 603, 90, 330},
+              GetRectForSingleView({100, 340}, {200, 600}, kSingleViewInsets));
+
+  // Test landscape pages.
+  CompareRect({5, 1003, 490, 440},
+              GetRectForSingleView({500, 450}, {500, 1000}, kSingleViewInsets));
+  CompareRect({30, 1503, 640, 190},
+              GetRectForSingleView({650, 200}, {700, 1500}, kSingleViewInsets));
+}
+
+TEST(CoordinateTest, GetScreenRect) {
+  const pp::Rect rect(10, 20, 200, 300);
+
+  // Test various zooms with the position at the origin.
+  CompareRect({10, 20, 200, 300}, GetScreenRect(rect, {0, 0}, 1));
+  CompareRect({15, 30, 300, 450}, GetScreenRect(rect, {0, 0}, 1.5));
+  CompareRect({5, 10, 100, 150}, GetScreenRect(rect, {0, 0}, 0.5));
+
+  // Test various zooms with the position elsewhere.
+  CompareRect({-390, -10, 200, 300}, GetScreenRect(rect, {400, 30}, 1));
+  CompareRect({-385, 0, 300, 450}, GetScreenRect(rect, {400, 30}, 1.5));
+  CompareRect({-395, -20, 100, 150}, GetScreenRect(rect, {400, 30}, 0.5));
+
+  // Test various zooms with a negative position.
+  CompareRect({-90, 70, 200, 300}, GetScreenRect(rect, {100, -50}, 1));
+  CompareRect({-85, 80, 300, 450}, GetScreenRect(rect, {100, -50}, 1.5));
+  CompareRect({-95, 60, 100, 150}, GetScreenRect(rect, {100, -50}, 0.5));
+
+  // Test an empty rect always outputs an empty rect.
+  const pp::Rect empty_rect;
+  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 1));
+  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 1.5));
+  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 0.5));
+}
+
+TEST(CoordinateTest, GetSurroundingRect) {
+  constexpr int kDocWidth = 1000;
+
+  // Test various position, sizes, and document width.
+  CompareRect({0, 97, 1000, 314},
+              GetSurroundingRect(100, 300, kSingleViewInsets, kDocWidth,
+                                 kBottomSeparator));
+  CompareRect({0, 37, 1000, 214},
+              GetSurroundingRect(40, 200, kSingleViewInsets, kDocWidth,
+                                 kBottomSeparator));
+  CompareRect({0, 197, 1000, 514},
+              GetSurroundingRect(200, 500, kSingleViewInsets, kDocWidth,
+                                 kBottomSeparator));
+  CompareRect(
+      {0, -103, 200, 314},
+      GetSurroundingRect(-100, 300, kSingleViewInsets, 200, kBottomSeparator));
+}
+
 TEST(CoordinateTest, GetLeftFillRect) {
   // Testing various rectangles with different positions and sizes.
   pp::Rect page_rect(10, 20, 400, 500);
@@ -194,63 +251,6 @@
                                                    kBottomSeparator));
 }
 
-TEST(CoordinateTest, GetRectForSingleView) {
-  // Test portrait pages.
-  CompareRect({55, 503, 190, 390},
-              GetRectForSingleView({200, 400}, {300, 500}, kSingleViewInsets));
-  CompareRect({55, 603, 90, 330},
-              GetRectForSingleView({100, 340}, {200, 600}, kSingleViewInsets));
-
-  // Test landscape pages.
-  CompareRect({5, 1003, 490, 440},
-              GetRectForSingleView({500, 450}, {500, 1000}, kSingleViewInsets));
-  CompareRect({30, 1503, 640, 190},
-              GetRectForSingleView({650, 200}, {700, 1500}, kSingleViewInsets));
-}
-
-TEST(CoordinateTest, GetScreenRect) {
-  const pp::Rect rect(10, 20, 200, 300);
-
-  // Test various zooms with the position at the origin.
-  CompareRect({10, 20, 200, 300}, GetScreenRect(rect, {0, 0}, 1));
-  CompareRect({15, 30, 300, 450}, GetScreenRect(rect, {0, 0}, 1.5));
-  CompareRect({5, 10, 100, 150}, GetScreenRect(rect, {0, 0}, 0.5));
-
-  // Test various zooms with the position elsewhere.
-  CompareRect({-390, -10, 200, 300}, GetScreenRect(rect, {400, 30}, 1));
-  CompareRect({-385, 0, 300, 450}, GetScreenRect(rect, {400, 30}, 1.5));
-  CompareRect({-395, -20, 100, 150}, GetScreenRect(rect, {400, 30}, 0.5));
-
-  // Test various zooms with a negative position.
-  CompareRect({-90, 70, 200, 300}, GetScreenRect(rect, {100, -50}, 1));
-  CompareRect({-85, 80, 300, 450}, GetScreenRect(rect, {100, -50}, 1.5));
-  CompareRect({-95, 60, 100, 150}, GetScreenRect(rect, {100, -50}, 0.5));
-
-  // Test an empty rect always outputs an empty rect.
-  const pp::Rect empty_rect;
-  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 1));
-  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 1.5));
-  CompareRect({-20, -500, 0, 0}, GetScreenRect(empty_rect, {20, 500}, 0.5));
-}
-
-TEST(CoordinateTest, GetSurroundingRect) {
-  constexpr int kDocWidth = 1000;
-
-  // Test various position, sizes, and document width.
-  CompareRect({0, 97, 1000, 314},
-              GetSurroundingRect(100, 300, kSingleViewInsets, kDocWidth,
-                                 kBottomSeparator));
-  CompareRect({0, 37, 1000, 214},
-              GetSurroundingRect(40, 200, kSingleViewInsets, kDocWidth,
-                                 kBottomSeparator));
-  CompareRect({0, 197, 1000, 514},
-              GetSurroundingRect(200, 500, kSingleViewInsets, kDocWidth,
-                                 kBottomSeparator));
-  CompareRect(
-      {0, -103, 200, 314},
-      GetSurroundingRect(-100, 300, kSingleViewInsets, 200, kBottomSeparator));
-}
-
 TEST(CoordinateTest, GetLeftRectForTwoUpView) {
   CompareRect({105, 103, 194, 390},
               GetLeftRectForTwoUpView({200, 400}, {300, 100}, kLeftInsets));
@@ -281,34 +281,5 @@
               GetRightRectForTwoUpView({0, 0}, {100, 0}, kRightInsets));
 }
 
-TEST(CoordinateTest, TwoUpViewLayout) {
-  pp::Point position(1066, 0);
-
-  // Test layout when the widest page is on the left.
-  CompareRect({245, 3, 820, 1056},
-              GetLeftRectForTwoUpView({826, 1066}, position, kLeftInsets));
-  CompareRect({1067, 3, 1060, 816},
-              GetRightRectForTwoUpView({1066, 826}, position, kRightInsets));
-
-  position.set_y(1066);
-  CompareRect({245, 1069, 820, 1056},
-              GetLeftRectForTwoUpView({826, 1066}, position, kLeftInsets));
-  CompareRect({1067, 1069, 820, 890},
-              GetRightRectForTwoUpView({826, 900}, position, kRightInsets));
-
-  // Test layout when the widest page is on the right.
-  position.set_y(0);
-  CompareRect({5, 3, 1060, 816},
-              GetLeftRectForTwoUpView({1066, 826}, position, kLeftInsets));
-  CompareRect({1067, 3, 820, 1056},
-              GetRightRectForTwoUpView({826, 1066}, position, kRightInsets));
-
-  position.set_y(1066);
-  CompareRect({245, 1069, 820, 890},
-              GetLeftRectForTwoUpView({826, 900}, position, kLeftInsets));
-  CompareRect({1067, 1069, 820, 1056},
-              GetRightRectForTwoUpView({826, 1066}, position, kRightInsets));
-}
-
 }  // namespace draw_utils
 }  // namespace chrome_pdf
diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc
index 2563ae6..17c363f 100644
--- a/printing/print_settings_conversion.cc
+++ b/printing/print_settings_conversion.cc
@@ -25,21 +25,19 @@
 
 namespace {
 
-void GetCustomMarginsFromJobSettings(const base::Value& settings,
-                                     PageMargins* page_size_margins) {
+// Note: If this code crashes, then the caller has passed in invalid |settings|.
+// Fix the caller, instead of trying to avoid the crash here.
+PageMargins GetCustomMarginsFromJobSettings(const base::Value& settings) {
+  PageMargins margins_in_points;
   const base::Value* custom_margins = settings.FindKey(kSettingMarginsCustom);
-  if (!custom_margins) {
-    NOTREACHED();
-    return;
-  }
-  page_size_margins->top =
-      custom_margins->FindIntKey(kSettingMarginTop).value_or(0);
-  page_size_margins->bottom =
-      custom_margins->FindIntKey(kSettingMarginBottom).value_or(0);
-  page_size_margins->left =
-      custom_margins->FindIntKey(kSettingMarginLeft).value_or(0);
-  page_size_margins->right =
-      custom_margins->FindIntKey(kSettingMarginRight).value_or(0);
+  margins_in_points.top = custom_margins->FindIntKey(kSettingMarginTop).value();
+  margins_in_points.bottom =
+      custom_margins->FindIntKey(kSettingMarginBottom).value();
+  margins_in_points.left =
+      custom_margins->FindIntKey(kSettingMarginLeft).value();
+  margins_in_points.right =
+      custom_margins->FindIntKey(kSettingMarginRight).value();
+  return margins_in_points;
 }
 
 void SetMarginsToJobSettings(const std::string& json_path,
@@ -133,12 +131,8 @@
   }
   settings->set_margin_type(static_cast<MarginType>(margin_type));
 
-  if (margin_type == CUSTOM_MARGINS) {
-    PageMargins margins_in_points;
-    margins_in_points.Clear();
-    GetCustomMarginsFromJobSettings(job_settings, &margins_in_points);
-    settings->SetCustomMargins(margins_in_points);
-  }
+  if (margin_type == CUSTOM_MARGINS)
+    settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
 
   PageRanges new_ranges;
   const base::Value* page_range_array =
diff --git a/services/tracing/public/cpp/perfetto/perfetto_producer.cc b/services/tracing/public/cpp/perfetto/perfetto_producer.cc
index ee87551..9d0a0f88 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_producer.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_producer.cc
@@ -28,7 +28,14 @@
 std::unique_ptr<perfetto::TraceWriter> PerfettoProducer::CreateTraceWriter(
     perfetto::BufferID target_buffer) {
   DCHECK(GetSharedMemoryArbiter());
-  return GetSharedMemoryArbiter()->CreateTraceWriter(target_buffer);
+  // Chromium uses BufferExhaustedPolicy::kDrop to avoid stalling trace writers
+  // when the chunks in the SMB are exhausted. Stalling could otherwise lead to
+  // deadlocks in chromium, because a stalled mojo IPC thread could prevent
+  // CommitRequest messages from reaching the perfetto service.
+  auto smb_exhausted_policy =
+      perfetto::SharedMemoryArbiter::BufferExhaustedPolicy::kDrop;
+  return GetSharedMemoryArbiter()->CreateTraceWriter(target_buffer,
+                                                     smb_exhausted_policy);
 }
 
 PerfettoTaskRunner* PerfettoProducer::task_runner() {
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 8afe51f..a2e6dae 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -539,7 +539,15 @@
   std::unique_ptr<perfetto::StartupTraceWriter> trace_writer;
   uint32_t session_id = session_id_.load(std::memory_order_relaxed);
   if (startup_writer_registry_) {
-    trace_writer = startup_writer_registry_->CreateUnboundTraceWriter();
+    // Chromium uses BufferExhaustedPolicy::kDrop to avoid stalling trace
+    // writers when the chunks in the SMB are exhausted. Stalling could
+    // otherwise lead to deadlocks in chromium, because a stalled mojo IPC
+    // thread could prevent CommitRequest messages from reaching the perfetto
+    // service.
+    auto smb_exhausted_policy =
+        perfetto::SharedMemoryArbiter::BufferExhaustedPolicy::kDrop;
+    trace_writer = startup_writer_registry_->CreateUnboundTraceWriter(
+        smb_exhausted_policy);
   } else if (producer_) {
     trace_writer = std::make_unique<perfetto::StartupTraceWriter>(
         producer_->CreateTraceWriter(target_buffer_));
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 8429987..b1b98b6 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -14573,7 +14573,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14588,7 +14588,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14603,7 +14603,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14618,7 +14618,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14633,7 +14633,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14648,7 +14648,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14663,7 +14663,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14678,7 +14678,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14693,7 +14693,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14708,7 +14708,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14724,7 +14724,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14739,7 +14739,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14754,7 +14754,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14769,7 +14769,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -14790,7 +14790,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14805,7 +14805,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14823,7 +14823,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14838,7 +14838,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14853,7 +14853,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14868,7 +14868,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14883,7 +14883,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14898,7 +14898,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14913,7 +14913,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14928,7 +14928,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14943,7 +14943,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 6
@@ -14964,7 +14964,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14984,7 +14984,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -14999,7 +14999,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15014,7 +15014,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15029,7 +15029,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15044,7 +15044,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15059,7 +15059,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15074,7 +15074,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15089,7 +15089,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15104,7 +15104,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15119,7 +15119,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15134,7 +15134,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15149,7 +15149,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15164,7 +15164,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15179,7 +15179,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15194,7 +15194,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15209,7 +15209,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15224,7 +15224,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15239,7 +15239,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15254,7 +15254,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15269,7 +15269,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15284,7 +15284,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15299,7 +15299,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 3
@@ -15320,7 +15320,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15335,7 +15335,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15350,7 +15350,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15365,7 +15365,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15380,7 +15380,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15395,7 +15395,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15410,7 +15410,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15425,7 +15425,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15440,7 +15440,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15455,7 +15455,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15470,7 +15470,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15485,7 +15485,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15500,7 +15500,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15515,7 +15515,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15530,7 +15530,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15545,7 +15545,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15560,7 +15560,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15575,7 +15575,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15590,7 +15590,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15605,7 +15605,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15620,7 +15620,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15635,7 +15635,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15650,7 +15650,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15665,7 +15665,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15680,7 +15680,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15695,7 +15695,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15710,7 +15710,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15725,7 +15725,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15740,7 +15740,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15755,7 +15755,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15770,7 +15770,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15785,7 +15785,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15800,7 +15800,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15815,7 +15815,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15830,7 +15830,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15845,7 +15845,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15860,7 +15860,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15875,7 +15875,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15890,7 +15890,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15905,7 +15905,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15920,7 +15920,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15935,7 +15935,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15951,7 +15951,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         },
@@ -15970,7 +15970,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -15989,7 +15989,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16005,7 +16005,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16021,7 +16021,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16037,7 +16037,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16053,7 +16053,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16069,7 +16069,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16085,7 +16085,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16101,7 +16101,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16127,7 +16127,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 10
@@ -16161,7 +16161,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16177,7 +16177,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false
@@ -16198,7 +16198,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -16220,7 +16220,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "idempotent": false,
@@ -16243,7 +16243,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16277,7 +16277,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16293,7 +16293,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ]
         }
@@ -16315,7 +16315,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04"
+              "os": "Ubuntu-16.04"
             }
           ],
           "shards": 12
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 694e35f..062c400 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -24567,7 +24567,8 @@
       {
         "args": [
           "../../tools/perf/run_benchmark",
-          "--benchmarks=rendering.desktop"
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
         ],
         "experiment_percentage": 100,
         "isolate_name": "rendering_representative_perf_tests",
@@ -25572,7 +25573,8 @@
       {
         "args": [
           "../../tools/perf/run_benchmark",
-          "--benchmarks=rendering.desktop"
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
         ],
         "experiment_percentage": 100,
         "isolate_name": "rendering_representative_perf_tests",
@@ -27474,7 +27476,8 @@
       {
         "args": [
           "../../tools/perf/run_benchmark",
-          "--benchmarks=rendering.desktop"
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
         ],
         "experiment_percentage": 100,
         "isolate_name": "rendering_representative_perf_tests",
@@ -28444,7 +28447,8 @@
       {
         "args": [
           "../../tools/perf/run_benchmark",
-          "--benchmarks=rendering.desktop"
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
         ],
         "experiment_percentage": 100,
         "isolate_name": "rendering_representative_perf_tests",
@@ -30682,7 +30686,8 @@
       {
         "args": [
           "../../tools/perf/run_benchmark",
-          "--benchmarks=rendering.desktop"
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
         ],
         "experiment_percentage": 100,
         "isolate_name": "rendering_representative_perf_tests",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 494a181..6166511 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1330,6 +1330,35 @@
       },
     },
   },
+  'rendering_representative_perf_tests': {
+    'modifications': {
+      'Win10 FYI x64 Exp Release (Intel HD 630)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
+      'Win10 FYI x64 Exp Release (NVIDIA)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
+      'Win10 FYI x64 Release (Intel HD 630)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
+      'Win10 FYI x64 Release (Intel UHD 630)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
+      'Win10 FYI x64 Release (NVIDIA)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
+    },
+  },
   'sandbox_linux_unittests': {
     'modifications': {
       'Lollipop Phone Tester': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index fd5371e..c76948f 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1810,7 +1810,7 @@
       },
       'linux-chromium-tests-staging-tests': {
         'mixins': [
-          'linux-trusty',
+          'linux-xenial',
         ],
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
diff --git a/third_party/.gitignore b/third_party/.gitignore
index e1b200c7..c21d7d8 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -13,7 +13,6 @@
 /android_build_tools/art/profman
 /android_build_tools/bundletool/*.jar
 /android_ndk/
-/android_sdk/androidx_browser/src
 /android_sdk/public/
 /android_sdk/sources/
 /android_protobuf/src
diff --git a/third_party/android_sdk/androidx_browser/BUILD.gn b/third_party/android_sdk/androidx_browser/BUILD.gn
deleted file mode 100644
index 6077716..0000000
--- a/third_party/android_sdk/androidx_browser/BUILD.gn
+++ /dev/null
@@ -1,54 +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.
-
-import("//build/config/android/rules.gni")
-
-android_resources("androidx_browser_resources") {
-  resource_dirs = [ "src/browser/src/main/res" ]
-  custom_package = "android.support.customtabs"
-}
-
-android_library("androidx_browser_java") {
-  java_files = [
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageService.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageServiceConnection.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsServiceConnection.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsSession.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageBackend.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsService.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java",
-    "./src/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/NotificationApiHelperForM.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/NotificationApiHelperForO.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java",
-    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityBuilder.java",
-
-  ]
-  deps = [
-    ":androidx_browser_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
-    "//third_party/android_deps:com_android_support_support_annotations_java",
-    "//third_party/android_deps:com_android_support_support_compat_java",
-  ]
-  srcjar_deps = [ ":androidx_browser_service_aidl" ]
-  android_manifest_for_lint = "src/browser/src/main/AndroidManifest.xml"
-  chromium_code = false
-}
-
-android_aidl("androidx_browser_service_aidl") {
-  interface_file = "common.aidl"
-
-  sources = [
-    "./src/browser/src/main/aidl/android/support/customtabs/ICustomTabsService.aidl",
-    "./src/browser/src/main/aidl/android/support/customtabs/IPostMessageService.aidl",
-    "./src/browser/src/main/aidl/android/support/customtabs/trusted/ITrustedWebActivityService.aidl",
-    "./src/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl",
-  ]
-}
diff --git a/third_party/android_sdk/androidx_browser/LICENSE b/third_party/android_sdk/androidx_browser/LICENSE
deleted file mode 100644
index 67db858..0000000
--- a/third_party/android_sdk/androidx_browser/LICENSE
+++ /dev/null
@@ -1,175 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
diff --git a/third_party/android_sdk/androidx_browser/OWNERS b/third_party/android_sdk/androidx_browser/OWNERS
deleted file mode 100644
index f728cfa..0000000
--- a/third_party/android_sdk/androidx_browser/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-lizeb@chromium.org
-peconn@chromium.org
-yusufo@chromium.org
-
-per-file *.aidl=set noparent
-per-file *.aidl=file://ipc/SECURITY_OWNERS
diff --git a/third_party/android_sdk/androidx_browser/README.chromium b/third_party/android_sdk/androidx_browser/README.chromium
deleted file mode 100644
index 27f4bb86..0000000
--- a/third_party/android_sdk/androidx_browser/README.chromium
+++ /dev/null
@@ -1,28 +0,0 @@
-Name: AndroidX Browser
-Short Name: AndroidX Browser
-URL: https://chromium.googlesource.com/external/gob/android/platform/frameworks/support/browser
-Version: 226da6c0dfb265404d3a67305802ab70a9ca6c80
-License: Apache 2.0
-Security Critical: yes
-License Android Compatible: yes
-
-Description:
-This is a copy of the files from androidx.browser to be used in Chromium.
-This covers Custom Tabs and Trusted Web Activities. The original code (not the
-Chromium hosted modified copy) can be found at:
-https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/browser/
-
-Local Modifications:
-Since Chromium does not yet rely on AndroidX (it still relies on the Android
-Support Library), various parts of androidx.browser have been omitted:
-- Browser Actions, which rely on androidx.concurrent.futures.ResolvableFuture.
-  Chromium has dropped support for Browser Actions so we don't need this code.
-- Tests, which rely on androidx.testutils.PollingCheck. New development on
-  the code in androidx.browser should happen in the AndroidX repository
-  (and will be automatically copied to the Chromium one).
-
-In addition, we don't compile with the included AndroidManifest.xml because
-in the AndroidX the minSdkVersion is set in build.gradle. Chromium's tooling
-can't read this and expects the minSdkVersion to be present in the manifest
-(which conversely Android's tooling complains about). If we omit the
-AndroidManifest, Chromium's tooling chooses a sensible default.
diff --git a/third_party/android_sdk/androidx_browser/common.aidl b/third_party/android_sdk/androidx_browser/common.aidl
deleted file mode 100644
index 0eb2a8a..0000000
--- a/third_party/android_sdk/androidx_browser/common.aidl
+++ /dev/null
@@ -1,8 +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.
-
-interface android.support.customtabs.ICustomTabsService;
-interface android.support.customtabs.ICustomTabsCallback;
-interface android.support.customtabs.IPostMessageService;
-interface android.support.customtabs.trusted.ITrustedWebActivityService;
diff --git a/third_party/blink/common/indexeddb/indexed_db_default_mojom_traits.cc b/third_party/blink/common/indexeddb/indexed_db_default_mojom_traits.cc
index 702edac..8679107 100644
--- a/third_party/blink/common/indexeddb/indexed_db_default_mojom_traits.cc
+++ b/third_party/blink/common/indexeddb/indexed_db_default_mojom_traits.cc
@@ -80,16 +80,15 @@
       return blink::mojom::IDBKeyDataDataView::Tag::DATE;
     case blink::mojom::IDBKeyType::Number:
       return blink::mojom::IDBKeyDataDataView::Tag::NUMBER;
-    case blink::mojom::IDBKeyType::Invalid:
-      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID;
-    case blink::mojom::IDBKeyType::Null:
-      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NULL;
+    case blink::mojom::IDBKeyType::None:
+      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE;
 
     // Not used, fall through to NOTREACHED.
-    case blink::mojom::IDBKeyType::Min:;
+    case blink::mojom::IDBKeyType::Invalid:  // Only used in blink.
+    case blink::mojom::IDBKeyType::Min:;     // Only used in the browser.
   }
   NOTREACHED();
-  return blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID;
+  return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE;
 }
 
 // static
@@ -125,11 +124,8 @@
       *out =
           blink::IndexedDBKey(data.number(), blink::mojom::IDBKeyType::Number);
       return true;
-    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID:
-      *out = blink::IndexedDBKey(blink::mojom::IDBKeyType::Invalid);
-      return true;
-    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_NULL:
-      *out = blink::IndexedDBKey(blink::mojom::IDBKeyType::Null);
+    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE:
+      *out = blink::IndexedDBKey(blink::mojom::IDBKeyType::None);
       return true;
   }
 
diff --git a/third_party/blink/common/indexeddb/indexeddb_key.cc b/third_party/blink/common/indexeddb/indexeddb_key.cc
index e2fd1e4..99c243e 100644
--- a/third_party/blink/common/indexeddb/indexeddb_key.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_key.cc
@@ -34,11 +34,11 @@
 }  // namespace
 
 IndexedDBKey::IndexedDBKey()
-    : type_(mojom::IDBKeyType::Null), size_estimate_(kOverheadSize) {}
+    : type_(mojom::IDBKeyType::None), size_estimate_(kOverheadSize) {}
 
 IndexedDBKey::IndexedDBKey(mojom::IDBKeyType type)
     : type_(type), size_estimate_(kOverheadSize) {
-  DCHECK(type == mojom::IDBKeyType::Null || type == mojom::IDBKeyType::Invalid);
+  DCHECK(type == mojom::IDBKeyType::None || type == mojom::IDBKeyType::Invalid);
 }
 
 IndexedDBKey::IndexedDBKey(double number, mojom::IDBKeyType type)
@@ -71,7 +71,7 @@
 IndexedDBKey& IndexedDBKey::operator=(const IndexedDBKey& other) = default;
 
 bool IndexedDBKey::IsValid() const {
-  if (type_ == mojom::IDBKeyType::Invalid || type_ == mojom::IDBKeyType::Null)
+  if (type_ == mojom::IDBKeyType::Invalid || type_ == mojom::IDBKeyType::None)
     return false;
 
   if (type_ == blink::mojom::IDBKeyType::Array) {
@@ -114,7 +114,7 @@
     case mojom::IDBKeyType::Number:
       return Compare(number_, other.number_);
     case mojom::IDBKeyType::Invalid:
-    case mojom::IDBKeyType::Null:
+    case mojom::IDBKeyType::None:
     case mojom::IDBKeyType::Min:
     default:
       NOTREACHED();
diff --git a/third_party/blink/common/indexeddb/indexeddb_key_unittest.cc b/third_party/blink/common/indexeddb/indexeddb_key_unittest.cc
index 50a9685..17aec06 100644
--- a/third_party/blink/common/indexeddb/indexeddb_key_unittest.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_key_unittest.cc
@@ -23,7 +23,7 @@
   keys.push_back(IndexedDBKey());
   estimates.push_back(16u);  // Overhead.
 
-  keys.push_back(IndexedDBKey(mojom::IDBKeyType::Null));
+  keys.push_back(IndexedDBKey(mojom::IDBKeyType::None));
   estimates.push_back(16u);
 
   double number = 3.14159;
diff --git a/third_party/blink/perf_tests/xss_auditor/large-post-many-events.html b/third_party/blink/perf_tests/xss_auditor/large-post-many-events.html
deleted file mode 100644
index c26181f..0000000
--- a/third_party/blink/perf_tests/xss_auditor/large-post-many-events.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1"> 
-<title>WebKit test - use for detect speed differences when submitting the form</title> 
-<style type="text/css">
-BODY { background-color: #F0F0F0 }
-
-BODY, TABLE, TD {
-  font-family: Verdana, Arial, Helvetica, sans-serif;
-  font-size: 12px;
-}
-.funcDesc {  background-color:#9F8E1C; color:#000000;  /* background-color:#ffffbb; */ }
-.t { font-family:courier; }
-.tro { font-family:courier; background-color: #EBEBE4; }
-.ro { background-color: #EBEBE4; }
-#tableHeaders th { background-color:#F0F0F0; }
-#tableRowstd { background-color:#F0F0F0; }
-</style>
-<script>
-window.onload = function() {
-    document.getElementById("score").textContent = window.performance.timing.domContentLoadedEventStart - window.performance.timing.responseStart;
-}
-</script>
-</head>
-<body>
-
-<form id="frmName" name="frmName" method="post" action="large-post-many-events.html">
-
-<div style="margin:0 auto; width:600px; background-color:#bbb; padding:20px;">
-Score (smaller is better): <span id="score">Running...</span>
-</div>
-
-<input type="submit" value="Request Page Again">
-<br><br>
-
-
-<div id="divMainTable" style="padding:0; border:0; overflow:auto; height:450px;">
-<table id="tableRows" border="0" cellpadding="3" cellspacing="1" style="background-color:black; width:;">
-
-
-<script>
-for (var i=0; i < 400; ++i) {
-    document.write("<tr><td><a href=\"changed-name.php?ID=" + i + "\" target=\"_blank\">ChangedName, ChangedName</a></td> <td width=\"80px\">22331133</td> <td>06.02.2010</td> <td ondblclick=\"tR('" + i + "_1865_5721','')\">&nbsp;&nbsp;22:15&nbsp;&nbsp;</td> <td><input type=\"text\"  name=\"frmI1_" + i + "_1865_5721\" onblur=\"chkT(this);\" size=\"5\" class=\"t\" value=\"00:00\"></td> <td><input type=\"text\"  name=\"frmU1_" + i + "_1865_5721\" onblur=\"chkT(this);\" size=\"5\" class=\"t\" value=\"00:00\"></td> <td><input type=\"text\"  name=\"frmI2_" + i + "_1865_5721\" onblur=\"chkT(this);\" size=\"5\" class=\"t\" value=\"00:00\"></td> <td><input type=\"text\"  name=\"frmU2_" + i + "_1865_5721\" onblur=\"chkT(this);\" size=\"5\" class=\"t\" value=\"00:00\"></td> <td><input type=\"text\"  name=\"frmLU_" + i + "_1865_5721\" onblur=\"chkT(this);\" size=\"5\" class=\"t\" value=\"00:00\"></td> <td> <select name=\"frmOK_" + i + "_1865_5721\" onblur=\"checkFlag(this)\" onchange=\"tOK(this)\" > <option value=\"0\" selected=\"selected\">Nei</option> <option value=\"1\">Ja</option></td> </select> <td><a href=\"changed-another-name.php?ID=1865\">Changed name</a></td> <td>Changed name</td> <td>&nbsp;&nbsp;&nbsp;BLS&nbsp;&nbsp;&nbsp;</td> <td align=\"center\">&nbsp;&nbsp;&nbsp;201&nbsp;&nbsp;&nbsp;</td> <td><div style=\"display:none;\"><input type=\"checkbox\" id=\"funcFlag_" + i + "_1865_5721\" value=\"\" name=\"funcFlag[]\"></div> <select name=\"frmUarr_" + i + "_1865_5721\" onchange=\"chkVFunc(this)\" style=\"background-color:#FFFFFF;\"> <option value=\"5719\" style=\"background-color:#FFFFFF;\">Changed1 (22:15 - 05:00)</option> <option value=\"5720\" style=\"background-color:#FFFFFF;\">Changed2 (22:15 - 05:00)</option> <option value=\"5721\" selected=\"selected\" style=\"background-color:#FFFFFF;\">Changed3 (22:15 - 05:00)</option> <option value=\"5787\" style=\"background-color:#FF8800\">Changed4 (22:15 - 22:30)</option> <option class=\"funcDesc\" value=\"0\" disabled=\"disabled\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Changed text here as well</option> <option class=\"funcDesc\" value=\"0\" disabled=\"disabled\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Changed text here also</option> </select><input type=\"hidden\" id=\"frmUarr_" + i + "_1865_5721_h\" value=\"5721\"></td> <td align=\"center\" > <select name=\"frmJobb_" + i + "_1865_5721\" title=\"JA\" onchange=\"checkFlag(this)\"> <option value=\"1\">Chan</option> <option value=\"2\" disabled=\"disabled\">Chan</option> <option value=\"3\" selected=\"selected\">Chan</option> </select></td> <td><input class=\"ro\" type=\"text\" readonly=\"readonly\" onblur=\"checkFlag(this)\" name=\"frmKommArb_" + i + "_1865_5721\" value=\"\" size=\"20\"></td> <td><input class=\"ro\" type=\"text\" readonly=\"readonly\" onblur=\"checkFlag(this)\" name=\"frmKommKru_" + i + "_1865_5721\" value=\"\" size=\"20\"></td> <td><input type=\"text\" onblur=\"checkFlag(this)\" name=\"frmKomm_" + i + "_1865_5721\" value=\"\" size=\"20\"></td> <td align=\"center\" style=\"\"><input type=\"checkbox\" onclick=\"checkUncheckOpptatt(this,this.value)\" id=\"O_" + i + "_1\" name=\"frmAnsOppt[]\" value=\"" + i + "_2010-02-06\" style=\"margin-left:20px; margin-right:20px;\"></td> </tr>");
-}
-</script>
-</table>
-</form>
-</div>
-</body>
-</html>
diff --git a/third_party/blink/perf_tests/xss_auditor/large-post-many-inline-scripts-and-events.html b/third_party/blink/perf_tests/xss_auditor/large-post-many-inline-scripts-and-events.html
deleted file mode 100644
index 9eaa93f..0000000
--- a/third_party/blink/perf_tests/xss_auditor/large-post-many-inline-scripts-and-events.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<form action="resources/target-for-large-post-many-inline-scripts-and-events.html" method=POST>
-<input name="a" type="hidden">
-<input type=submit>
-</form>
-<script>
-var val = "";
-for (i = 0; i < 200000; ++i)
-    val += Math.random();
-document.getElementsByName("a")[0].value = val;
-</script>
diff --git a/third_party/blink/perf_tests/xss_auditor/resources/target-for-large-post-many-inline-scripts-and-events.html b/third_party/blink/perf_tests/xss_auditor/resources/target-for-large-post-many-inline-scripts-and-events.html
deleted file mode 100644
index 37ada235..0000000
--- a/third_party/blink/perf_tests/xss_auditor/resources/target-for-large-post-many-inline-scripts-and-events.html
+++ /dev/null
@@ -1,1287 +0,0 @@
-<script>
-window.onload = function() {
-    document.getElementById("score").textContent = window.performance.timing.domContentLoadedEventStart - window.performance.timing.responseStart;
-}
-</script>
-Score (smaller is better): <span id="score"></span><br>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<a href="javascript:return '0'"></a>
-.<img onclick="alert('0')">
-<script src="data:text/plain,'%30'"></script>
-<script>\x30</script>
-<br>Done.
diff --git a/third_party/blink/public/common/indexeddb/indexed_db_default_mojom_traits.h b/third_party/blink/public/common/indexeddb/indexed_db_default_mojom_traits.h
index bc83906..35ad7e1 100644
--- a/third_party/blink/public/common/indexeddb/indexed_db_default_mojom_traits.h
+++ b/third_party/blink/public/common/indexeddb/indexed_db_default_mojom_traits.h
@@ -103,8 +103,8 @@
   static bool other_invalid(const blink::IndexedDBKey& key) {
     return key.type() == blink::mojom::IDBKeyType::Invalid;
   }
-  static bool other_null(const blink::IndexedDBKey& key) {
-    return key.type() == blink::mojom::IDBKeyType::Null;
+  static bool other_none(const blink::IndexedDBKey& key) {
+    return key.type() == blink::mojom::IDBKeyType::None;
   }
 };
 
diff --git a/third_party/blink/public/common/indexeddb/indexeddb_key_range.h b/third_party/blink/public/common/indexeddb/indexeddb_key_range.h
index 0756d6c..cf284b2 100644
--- a/third_party/blink/public/common/indexeddb/indexeddb_key_range.h
+++ b/third_party/blink/public/common/indexeddb/indexeddb_key_range.h
@@ -31,8 +31,8 @@
   bool IsEmpty() const;
 
  private:
-  blink::IndexedDBKey lower_ = blink::IndexedDBKey(mojom::IDBKeyType::Null);
-  blink::IndexedDBKey upper_ = blink::IndexedDBKey(mojom::IDBKeyType::Null);
+  blink::IndexedDBKey lower_ = blink::IndexedDBKey(mojom::IDBKeyType::None);
+  blink::IndexedDBKey upper_ = blink::IndexedDBKey(mojom::IDBKeyType::None);
   bool lower_open_ = false;
   bool upper_open_ = false;
 };
diff --git a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
index 286d9381..8d4161ac 100644
--- a/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
+++ b/third_party/blink/public/mojom/indexeddb/indexeddb.mojom
@@ -47,12 +47,13 @@
   Date,
   Number,
 
-  // Null is used to represent the lack of a key, e.g. when a key range
+  // None is used to represent the lack of a key, e.g. when a key range
   // has no upper/lower bound.
-  Null,
+  // TODO(jsbell): Consider using Optional<> instead.
+  None,
 
   // Min is used to encode the lower bound of a construct in leveldb, e.g.
-  // all entries in a store are from [{id, min}, {id+1, min}). It should
+  // all entries in a store are in [{id, min}, {id+1, min}). It should
   // only appear in browser code.
   Min,
 };
@@ -63,10 +64,8 @@
   mojo_base.mojom.String16 string;
   double date;
   double number;
-  // TODO(jsbell): These types should be cleaned up end-to-end, leaving only the
-  // dataful options above.
-  bool other_invalid;
-  bool other_null;
+  // TODO(jsbell): Consider using Optional<> instead.
+  bool other_none;
 };
 
 // Defined as a structure so that it can by typemapped with StructTraits.
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index cfe5f74..51beb6d 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -275,7 +275,6 @@
   virtual void SetWebGLErrorsToConsoleEnabled(bool) = 0;
   virtual void SetWebSecurityEnabled(bool) = 0;
   virtual void SetWideViewportQuirkEnabled(bool) = 0;
-  virtual void SetXSSAuditorEnabled(bool) = 0;
   virtual void SetMediaControlsEnabled(bool) = 0;
   virtual void SetDoNotUpdateSelectionOnMutatingSelectionRange(bool) = 0;
   virtual void SetLowPriorityIframesThreshold(WebEffectiveConnectionType) = 0;
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index bbd2c2f..ff1a21a 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -95,7 +95,7 @@
     case mojom::IDBKeyType::Min:
       NOTREACHED();
       return v8::Local<v8::Value>();
-    case mojom::IDBKeyType::Null:
+    case mojom::IDBKeyType::None:
       return v8::Null(isolate);
     case mojom::IDBKeyType::Number:
       return v8::Number::New(isolate, key->Number());
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 5a870d06..c6a9d3a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -304,7 +304,7 @@
     // Some of these elements are handled with other adjustments above.
     if (IsA<HTMLBRElement>(element) || IsHTMLWBRElement(element) ||
         IsHTMLMeterElement(element) || IsHTMLProgressElement(element) ||
-        IsHTMLCanvasElement(element) || IsHTMLMediaElement(element) ||
+        IsA<HTMLCanvasElement>(element) || IsHTMLMediaElement(element) ||
         IsHTMLInputElement(element) || IsHTMLTextAreaElement(element) ||
         IsHTMLSelectElement(element)) {
       style.SetDisplay(EDisplay::kNone);
@@ -446,7 +446,7 @@
   TouchAction inherited_action = parent_style.GetEffectiveTouchAction();
 
   bool is_replaced_canvas =
-      element && IsHTMLCanvasElement(element) &&
+      element && IsA<HTMLCanvasElement>(element) &&
       element->GetDocument().GetFrame() &&
       element->GetDocument().CanExecuteScripts(kNotAboutToExecuteScript);
   bool is_non_replaced_inline_elements =
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0240e3e..b6d4e4d4 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7217,7 +7217,7 @@
 HTMLDialogElement* Document::ActiveModalDialog() const {
   for (auto it = top_layer_elements_.rbegin(); it != top_layer_elements_.rend();
        ++it) {
-    if (auto* dialog = ToHTMLDialogElementOrNull(*it->Get()))
+    if (auto* dialog = DynamicTo<HTMLDialogElement>(*it->Get()))
       return dialog;
   }
 
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index d41cee01..8aef31c 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -1690,7 +1690,7 @@
     return nullptr;
 
   if (layout_object->IsCanvas()) {
-    return ToHTMLCanvasElement(const_cast<Node&>(node))
+    return To<HTMLCanvasElement>(const_cast<Node&>(node))
         .Snapshot(kFrontBuffer, kPreferNoAcceleration);
   }
 
@@ -1712,7 +1712,7 @@
   if (IsSVGImageElement(node))
     return To<SVGElement>(node).ImageSourceURL();
   if (IsHTMLEmbedElement(node) || IsHTMLObjectElement(node) ||
-      IsHTMLCanvasElement(node))
+      IsA<HTMLCanvasElement>(node))
     return To<HTMLElement>(node).ImageSourceURL();
   return AtomicString();
 }
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 962530bb..fea6b695 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -335,10 +335,6 @@
   settings_->SetJavaScriptCanAccessClipboard(enabled);
 }
 
-void WebSettingsImpl::SetXSSAuditorEnabled(bool enabled) {
-  settings_->SetXSSAuditorEnabled(enabled);
-}
-
 void WebSettingsImpl::SetTextTrackKindUserPreference(
     TextTrackKindUserPreference preference) {
   settings_->SetTextTrackKindUserPreference(
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 3f1b4cec..6bb7eab 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -196,7 +196,6 @@
   void SetWebGLErrorsToConsoleEnabled(bool) override;
   void SetWebSecurityEnabled(bool) override;
   void SetWideViewportQuirkEnabled(bool) override;
-  void SetXSSAuditorEnabled(bool) override;
   void SetMediaControlsEnabled(bool) override;
   void SetDoNotUpdateSelectionOnMutatingSelectionRange(bool) override;
   void SetLowPriorityIframesThreshold(WebEffectiveConnectionType) override;
diff --git a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
index 3725c34..7d58873 100644
--- a/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
+++ b/third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.cc
@@ -200,8 +200,7 @@
                    // inconsistent.
                    : document->CompleteURLWithOverride(
                          report_endpoint, document->FallbackBaseURL());
-    PingLoader::SendViolationReport(
-        frame, url, report, PingLoader::kContentSecurityPolicyViolationReport);
+    PingLoader::SendViolationReport(frame, url, report);
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index 72d9508..5f113450 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -172,11 +172,6 @@
     },
 
     {
-      name: "xssAuditorEnabled",
-      initial: false,
-    },
-
-    {
       name: "preferCompositingToLCDTextEnabled",
       initial: false,
       invalidate: "AcceleratedCompositing",
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index fc42401f3..fcbb75f 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2303,7 +2303,7 @@
 
 void WebLocalFrameImpl::CopyImageAt(const WebPoint& pos_in_viewport) {
   HitTestResult result = HitTestResultForVisualViewportPos(pos_in_viewport);
-  if (!IsHTMLCanvasElement(result.InnerNodeOrImageMapImage()) &&
+  if (!IsA<HTMLCanvasElement>(result.InnerNodeOrImageMapImage()) &&
       result.AbsoluteImageURL().IsEmpty()) {
     // There isn't actually an image at these coordinates.  Might be because
     // the window scrolled while the context menu was open or because the page
@@ -2325,7 +2325,7 @@
 void WebLocalFrameImpl::SaveImageAt(const WebPoint& pos_in_viewport) {
   Node* node = HitTestResultForVisualViewportPos(pos_in_viewport)
                    .InnerNodeOrImageMapImage();
-  if (!node || !(IsHTMLCanvasElement(*node) || IsHTMLImageElement(*node)))
+  if (!node || !(IsA<HTMLCanvasElement>(*node) || IsHTMLImageElement(*node)))
     return;
 
   String url = To<Element>(*node).ImageSourceURL();
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 5cb19194..047102b 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -286,7 +286,7 @@
     return false;
 
   // |pending| is not a dialog element.
-  if (IsHTMLDialogElement(pending))
+  if (IsA<HTMLDialogElement>(pending))
     return false;
 
   // The fullscreen element ready check for |pending| returns false.
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc b/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
index 1cd5df9..0ddd7b0 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
@@ -45,7 +45,7 @@
   GetDocument().documentElement()->SetInnerHTMLFromString(
       "<body><canvas id='c'></canvas></body>");
   UpdateAllLifecyclePhasesForTest();
-  canvas_element_ = ToHTMLCanvasElement(GetDocument().getElementById("c"));
+  canvas_element_ = To<HTMLCanvasElement>(GetDocument().getElementById("c"));
   String canvas_type("2d");
   CanvasContextCreationAttributesCore attributes;
   attributes.alpha = true;
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 340695d..1c89d714 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
@@ -377,7 +377,7 @@
 
 void HTMLFormElement::SubmitDialog(FormSubmission* form_submission) {
   for (Node* node = this; node; node = node->ParentOrShadowHostNode()) {
-    if (auto* dialog = ToHTMLDialogElementOrNull(*node)) {
+    if (auto* dialog = DynamicTo<HTMLDialogElement>(*node)) {
       dialog->close(form_submission->Result());
       return;
     }
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index e1697bd..180fcd3 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -50,7 +50,7 @@
   // currently specified.  This may change at any time.
   // See crbug/383230 and https://github.com/whatwg/html/issues/2393 .
   for (Node* node = FlatTreeTraversal::FirstChild(*dialog); node; node = next) {
-    next = IsHTMLDialogElement(*node)
+    next = IsA<HTMLDialogElement>(*node)
                ? FlatTreeTraversal::NextSkippingChildren(*node, dialog)
                : FlatTreeTraversal::Next(*node, dialog);
 
diff --git a/third_party/blink/renderer/core/html/parser/html_token.h b/third_party/blink/renderer/core/html/parser/html_token.h
index 3176a0d..49fa9fb1 100644
--- a/third_party/blink/renderer/core/html/parser/html_token.h
+++ b/third_party/blink/renderer/core/html/parser/html_token.h
@@ -378,12 +378,6 @@
     return nullptr;
   }
 
-  // Used by the XSSAuditor to nuke XSS-laden attributes.
-  void EraseValueOfAttribute(wtf_size_t i) {
-    DCHECK(type_ == kStartTag || type_ == kEndTag);
-    attributes_[i].ClearValue();
-  }
-
   /* Character Tokens */
 
   // Starting a character token works slightly differently than starting
@@ -433,13 +427,6 @@
     or_all_data_ |= character;
   }
 
-  // Only for XSSAuditor
-  void EraseCharacters() {
-    DCHECK_EQ(type_, kCharacter);
-    data_.clear();
-    or_all_data_ = 0;
-  }
-
  private:
   TokenType type_;
   Attribute::Range range_;  // Always starts at zero.
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc
index c468f46..1ce9059 100644
--- a/third_party/blink/renderer/core/input/event_handler_test.cc
+++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -1019,8 +1019,7 @@
       "<canvas style='width: 100px; height: 100px' id='first' "
       "onpointermove='return;'>");
 
-  HTMLCanvasElement& canvas =
-      ToHTMLCanvasElement(*GetDocument().getElementById("first"));
+  auto& canvas = To<HTMLCanvasElement>(*GetDocument().getElementById("first"));
 
   ASSERT_FALSE(chrome_client_->ReceivedRequestForUnbufferedInput());
 
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index 1ccbd3b9..1528667 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -188,9 +188,9 @@
     return WebInputEventResult::kNotHandled;
 
   if (event_type == event_type_names::kPointerdown) {
-    Node* node = target->ToNode();
-    if (node && IsHTMLCanvasElement(*node) &&
-        ToHTMLCanvasElement(*node).NeedsUnbufferedInputEvents()) {
+    auto* html_canvas_element = DynamicTo<HTMLCanvasElement>(target->ToNode());
+    if (html_canvas_element &&
+        html_canvas_element->NeedsUnbufferedInputEvents()) {
       frame_->GetChromeClient().RequestUnbufferedInputEvents(frame_);
     }
   }
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 4a27f2e..bd9e931 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -262,7 +262,7 @@
     if (!layout_object)
       continue;
 
-    if (IsHTMLCanvasElement(element) || IsHTMLEmbedElement(element) ||
+    if (IsA<HTMLCanvasElement>(element) || IsHTMLEmbedElement(element) ||
         IsHTMLImageElement(element) || IsHTMLObjectElement(element) ||
         IsHTMLPictureElement(element) || element->IsSVGElement() ||
         IsHTMLVideoElement(element)) {
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 78aa529..233682d 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -537,7 +537,7 @@
 
   UpdateAfterLayout();
 
-  if (IsHTMLDialogElement(GetNode()) && IsOutOfFlowPositioned())
+  if (IsA<HTMLDialogElement>(GetNode()) && IsOutOfFlowPositioned())
     PositionDialog();
 
   ClearNeedsLayout();
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
index 2847ccf..bf09cf44 100644
--- a/third_party/blink/renderer/core/layout/layout_html_canvas.cc
+++ b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
@@ -49,7 +49,7 @@
 }
 
 void LayoutHTMLCanvas::CanvasSizeChanged() {
-  IntSize canvas_size = ToHTMLCanvasElement(GetNode())->Size();
+  IntSize canvas_size = To<HTMLCanvasElement>(GetNode())->Size();
   LayoutSize zoomed_size(canvas_size.Width() * StyleRef().EffectiveZoom(),
                          canvas_size.Height() * StyleRef().EffectiveZoom());
 
@@ -82,7 +82,7 @@
 
 void LayoutHTMLCanvas::InvalidatePaint(
     const PaintInvalidatorContext& context) const {
-  auto* element = ToHTMLCanvasElement(GetNode());
+  auto* element = To<HTMLCanvasElement>(GetNode());
   if (element->IsDirty())
     element->DoDeferredPaintInvalidation();
 
@@ -90,7 +90,7 @@
 }
 
 CompositingReasons LayoutHTMLCanvas::AdditionalCompositingReasons() const {
-  if (ToHTMLCanvasElement(GetNode())->ShouldBeDirectComposited())
+  if (To<HTMLCanvasElement>(GetNode())->ShouldBeDirectComposited())
     return CompositingReason::kCanvas;
   return CompositingReason::kNone;
 }
@@ -98,7 +98,7 @@
 void LayoutHTMLCanvas::StyleDidChange(StyleDifference diff,
                                       const ComputedStyle* old_style) {
   LayoutReplaced::StyleDidChange(diff, old_style);
-  ToHTMLCanvasElement(GetNode())->StyleDidChange(old_style, StyleRef());
+  To<HTMLCanvasElement>(GetNode())->StyleDidChange(old_style, StyleRef());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index 50493e5..0ee7f7b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -284,13 +284,13 @@
 base::Optional<LayoutUnit> ComputeAbsoluteDialogYPosition(
     const LayoutObject& dialog,
     LayoutUnit height) {
-  if (!IsHTMLDialogElement(dialog.GetNode()))
+  auto* dialog_node = DynamicTo<HTMLDialogElement>(dialog.GetNode());
+  if (!dialog_node)
     return base::nullopt;
 
   // This code implements <dialog> static-position spec.
   //
   // https://html.spec.whatwg.org/C/#the-dialog-element
-  HTMLDialogElement* dialog_node = ToHTMLDialogElement(dialog.GetNode());
   if (dialog_node->GetCenteringMode() == HTMLDialogElement::kNotCentered)
     return base::nullopt;
 
diff --git a/third_party/blink/renderer/core/loader/ping_loader.cc b/third_party/blink/renderer/core/loader/ping_loader.cc
index 4c1cd9a..b259e246 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader.cc
@@ -241,18 +241,10 @@
 
 void PingLoader::SendViolationReport(LocalFrame* frame,
                                      const KURL& report_url,
-                                     scoped_refptr<EncodedFormData> report,
-                                     ViolationReportType type) {
+                                     scoped_refptr<EncodedFormData> report) {
   ResourceRequest request(report_url);
   request.SetHttpMethod(http_names::kPOST);
-  switch (type) {
-    case kContentSecurityPolicyViolationReport:
-      request.SetHTTPContentType("application/csp-report");
-      break;
-    case kXSSAuditorViolationReport:
-      request.SetHTTPContentType("application/xss-auditor-report");
-      break;
-  }
+  request.SetHTTPContentType("application/csp-report");
   request.SetKeepalive(true);
   request.SetHttpBody(std::move(report));
   request.SetCredentialsMode(network::mojom::CredentialsMode::kSameOrigin);
diff --git a/third_party/blink/renderer/core/loader/ping_loader.h b/third_party/blink/renderer/core/loader/ping_loader.h
index 50ed1c3..b76c4818 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.h
+++ b/third_party/blink/renderer/core/loader/ping_loader.h
@@ -62,18 +62,12 @@
   STATIC_ONLY(PingLoader);
 
  public:
-  enum ViolationReportType {
-    kContentSecurityPolicyViolationReport,
-    kXSSAuditorViolationReport
-  };
-
   static void SendLinkAuditPing(LocalFrame*,
                                 const KURL& ping_url,
                                 const KURL& destination_url);
   static void SendViolationReport(LocalFrame*,
                                   const KURL& report_url,
-                                  scoped_refptr<EncodedFormData> report,
-                                  ViolationReportType);
+                                  scoped_refptr<EncodedFormData> report);
 
   // The last argument is guaranteed to be set to the size of payload if
   // these method return true. If these method returns false, the value
diff --git a/third_party/blink/renderer/core/loader/ping_loader_test.cc b/third_party/blink/renderer/core/loader/ping_loader_test.cc
index 5ff8a3d2..f519b4d 100644
--- a/third_party/blink/renderer/core/loader/ping_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader_test.cc
@@ -127,8 +127,7 @@
   url_test_helpers::RegisterMockedURLLoad(
       ping_url, test::CoreTestDataPath("bar.html"), "text/html");
   PingLoader::SendViolationReport(&GetFrame(), ping_url,
-                                  EncodedFormData::Create(),
-                                  PingLoader::kXSSAuditorViolationReport);
+                                  EncodedFormData::Create());
   Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   const ResourceRequest& request = client_->PingRequest();
   ASSERT_FALSE(request.IsNull());
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 7424cfb..bc50b0e 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -245,7 +245,7 @@
     data.alt_text = html_element->AltText();
   }
 
-  if (IsHTMLCanvasElement(result.InnerNode())) {
+  if (IsA<HTMLCanvasElement>(result.InnerNode())) {
     data.media_type = WebContextMenuData::kMediaTypeCanvas;
     data.has_image_contents = true;
   } else if (!result.AbsoluteImageURL().IsEmpty()) {
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 6ba5f94..0def054 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
@@ -102,7 +102,7 @@
 
 static inline bool IsTextureLayerCanvas(const LayoutObject& layout_object) {
   if (layout_object.IsCanvas()) {
-    HTMLCanvasElement* canvas = ToHTMLCanvasElement(layout_object.GetNode());
+    auto* canvas = To<HTMLCanvasElement>(layout_object.GetNode());
     if (canvas->SurfaceLayerBridge())
       return false;
     if (CanvasRenderingContext* context = canvas->RenderingContext())
@@ -113,7 +113,7 @@
 
 static inline bool IsSurfaceLayerCanvas(const LayoutObject& layout_object) {
   if (layout_object.IsCanvas()) {
-    HTMLCanvasElement* canvas = ToHTMLCanvasElement(layout_object.GetNode());
+    auto* canvas = To<HTMLCanvasElement>(layout_object.GetNode());
     return canvas->SurfaceLayerBridge();
   }
   return false;
@@ -121,7 +121,7 @@
 
 static inline bool IsCompositedCanvas(const LayoutObject& layout_object) {
   if (layout_object.IsCanvas()) {
-    HTMLCanvasElement* canvas = ToHTMLCanvasElement(layout_object.GetNode());
+    auto* canvas = To<HTMLCanvasElement>(layout_object.GetNode());
     if (canvas->SurfaceLayerBridge())
       return true;
     if (CanvasRenderingContext* context = canvas->RenderingContext())
@@ -448,7 +448,7 @@
   bool should_check_children = !foreground_layer_.get();
   if (IsTextureLayerCanvas(GetLayoutObject())) {
     CanvasRenderingContext* context =
-        ToHTMLCanvasElement(GetLayoutObject().GetNode())->RenderingContext();
+        To<HTMLCanvasElement>(GetLayoutObject().GetNode())->RenderingContext();
     cc::Layer* layer = context ? context->CcLayer() : nullptr;
     // Determine whether the external texture layer covers the whole graphics
     // layer. This may not be the case if there are box decorations or
@@ -875,7 +875,7 @@
           /*prevent_contents_opaque_changes=*/true);
     } else if (layout_object.IsCanvas()) {
       graphics_layer_->SetContentsToCcLayer(
-          ToHTMLCanvasElement(layout_object.GetNode())->ContentsCcLayer(),
+          To<HTMLCanvasElement>(layout_object.GetNode())->ContentsCcLayer(),
           /*prevent_contents_opaque_changes=*/false);
       layer_config_changed = true;
     }
@@ -1954,7 +1954,7 @@
 
   if (has_painted_content && IsTextureLayerCanvas(GetLayoutObject())) {
     CanvasRenderingContext* context =
-        ToHTMLCanvasElement(GetLayoutObject().GetNode())->RenderingContext();
+        To<HTMLCanvasElement>(GetLayoutObject().GetNode())->RenderingContext();
     // Content layer may be null if context is lost.
     if (cc::Layer* content_layer = context->CcLayer()) {
       Color bg_color(Color::kTransparent);
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index 0d911dc2..05c8f34a 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -36,8 +36,7 @@
   PhysicalRect paint_rect = layout_html_canvas_.ReplacedContentRect();
   paint_rect.Move(paint_offset);
 
-  HTMLCanvasElement* canvas =
-      ToHTMLCanvasElement(layout_html_canvas_.GetNode());
+  auto* canvas = To<HTMLCanvasElement>(layout_html_canvas_.GetNode());
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     if (auto* layer = canvas->ContentsCcLayer()) {
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc b/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
index 3cdbd06c..b2f2f20 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
@@ -78,8 +78,7 @@
   // Not using SetBodyInnerHTML() because we need to test before document
   // lifecyle update.
   GetDocument().body()->SetInnerHTMLFromString("<canvas width=300 height=200>");
-  HTMLCanvasElement* element =
-      ToHTMLCanvasElement(GetDocument().body()->firstChild());
+  auto* element = To<HTMLCanvasElement>(GetDocument().body()->firstChild());
   CanvasContextCreationAttributesCore attributes;
   attributes.alpha = true;
   CanvasRenderingContext* context =
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 6eb704a..8e8a7fc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -207,7 +207,7 @@
     return ax::mojom::Role::kImage;
   }
 
-  if (IsHTMLCanvasElement(node))
+  if (IsA<HTMLCanvasElement>(node))
     return ax::mojom::Role::kCanvas;
 
   if (css_box && css_box->IsLayoutView())
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 5658e0c9..467b6ffc 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -652,7 +652,7 @@
   if (GetNode()->HasTagName(kAddressTag))
     return ax::mojom::Role::kGenericContainer;
 
-  if (IsHTMLDialogElement(*GetNode()))
+  if (IsA<HTMLDialogElement>(*GetNode()))
     return ax::mojom::Role::kDialog;
 
   // The HTML element should not be exposed as an element. That's what the
@@ -1246,7 +1246,7 @@
   if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kModal, modal))
     return modal;
 
-  if (GetNode() && IsHTMLDialogElement(*GetNode()))
+  if (GetNode() && IsA<HTMLDialogElement>(*GetNode()))
     return To<Element>(GetNode())->IsInTopLayer();
 
   return false;
@@ -1267,7 +1267,7 @@
   if (IsDetached())
     return false;
   Node* node = this->GetNode();
-  return IsHTMLCanvasElement(node) && node->hasChildren();
+  return IsA<HTMLCanvasElement>(node) && node->hasChildren();
 }
 
 int AXNodeObject::HeadingLevel() const {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
index 2986a4d..9e7f31e 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
@@ -64,7 +64,7 @@
   GetDocument().documentElement()->SetInnerHTMLFromString(
       "<body><canvas id='c'></canvas></body>");
   UpdateAllLifecyclePhasesForTest();
-  canvas_element_ = ToHTMLCanvasElement(GetDocument().getElementById("c"));
+  canvas_element_ = To<HTMLCanvasElement>(GetDocument().getElementById("c"));
 }
 
 TEST_F(CanvasRenderingContext2DAPITest, SetShadowColor_Clamping) {
@@ -310,8 +310,7 @@
     padding:10px; margin:5px;'>
     <button id='button'></button></canvas>
   )HTML");
-  HTMLCanvasElement* canvas =
-      ToHTMLCanvasElement(document.getElementById("canvas"));
+  auto* canvas = To<HTMLCanvasElement>(document.getElementById("canvas"));
 
   String canvas_type("2d");
   CanvasContextCreationAttributesCore attributes;
@@ -327,8 +326,7 @@
   AXContext ax_context(GetDocument());
 
   Element* button_element = GetDocument().getElementById("button");
-  HTMLCanvasElement* canvas =
-      ToHTMLCanvasElement(GetDocument().getElementById("canvas"));
+  auto* canvas = To<HTMLCanvasElement>(GetDocument().getElementById("canvas"));
   CanvasRenderingContext2D* context =
       static_cast<CanvasRenderingContext2D*>(canvas->RenderingContext());
 
@@ -357,8 +355,7 @@
   AXContext ax_context(GetDocument());
 
   Element* button_element = GetDocument().getElementById("button");
-  HTMLCanvasElement* canvas =
-      ToHTMLCanvasElement(GetDocument().getElementById("canvas"));
+  auto* canvas = To<HTMLCanvasElement>(GetDocument().getElementById("canvas"));
   CanvasRenderingContext2D* context =
       static_cast<CanvasRenderingContext2D*>(canvas->RenderingContext());
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 2f1d4ae6..b2d723c 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -208,7 +208,7 @@
 
   SetHtmlInnerHTML(
       "<body><canvas id='c'></canvas><canvas id='d'></canvas></body>");
-  canvas_element_ = ToHTMLCanvasElement(GetElementById("c"));
+  canvas_element_ = To<HTMLCanvasElement>(GetElementById("c"));
 
   full_image_data_ = ImageData::Create(IntSize(10, 10));
   partial_image_data_ = ImageData::Create(IntSize(2, 2));
@@ -548,7 +548,7 @@
 }
 
 TEST_F(CanvasRenderingContext2DTest, ImageResourceLifetime) {
-  auto* canvas = ToHTMLCanvasElement(
+  auto* canvas = To<HTMLCanvasElement>(
       GetDocument().CreateRawElement(html_names::kCanvasTag));
   canvas->SetSize(IntSize(40, 40));
   ImageBitmap* image_bitmap_derived = nullptr;
@@ -610,8 +610,8 @@
   EXPECT_EQ(1u, GetGlobalAcceleratedContextCount());
 
   // Creating a different accelerated image buffer
-  HTMLCanvasElement* anotherCanvas =
-      ToHTMLCanvasElement(GetDocument().getElementById("d"));
+  auto* anotherCanvas =
+      To<HTMLCanvasElement>(GetDocument().getElementById("d"));
   CanvasContextCreationAttributesCore attributes;
   anotherCanvas->GetCanvasRenderingContext("2d", attributes);
   IntSize size2(10, 5);
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
index f6f9d78..64ad351e 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
@@ -51,7 +51,7 @@
   void SetUp() override {
     PageTestBase::SetUp();
     SetHtmlInnerHTML("<body><canvas id='c'></canvas></body>");
-    canvas_element_ = ToHTMLCanvasElement(GetElementById("c"));
+    canvas_element_ = To<HTMLCanvasElement>(GetElementById("c"));
   }
 
   HTMLCanvasElement& canvas_element() const { return *canvas_element_; }
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
index f2cb656..e1c9e9ae 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
@@ -71,7 +71,7 @@
       WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
   PageTestBase::SetUp();
   SetHtmlInnerHTML("<body><canvas id='c'></canvas></body>");
-  HTMLCanvasElement* canvas_element = ToHTMLCanvasElement(GetElementById("c"));
+  auto* canvas_element = To<HTMLCanvasElement>(GetElementById("c"));
 
   DummyExceptionStateForTesting exception_state;
   offscreen_canvas_ = HTMLCanvasElementModule::transferControlToOffscreen(
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
index c63514a..b32cc37 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
@@ -269,9 +269,9 @@
   const IDBKey* current_primary_key = IdbPrimaryKey();
 
   if (!key)
-    key = IDBKey::CreateNull();
+    key = IDBKey::CreateNone();
 
-  if (key->GetType() != mojom::IDBKeyType::Null) {
+  if (key->GetType() != mojom::IDBKeyType::None) {
     DCHECK(key_);
     if (direction_ == mojom::IDBCursorDirection::Next ||
         direction_ == mojom::IDBCursorDirection::NextNoDuplicate) {
@@ -299,7 +299,7 @@
   }
 
   if (!primary_key)
-    primary_key = IDBKey::CreateNull();
+    primary_key = IDBKey::CreateNone();
 
   // FIXME: We're not using the context from when continue was called, which
   // means the callback will be on the original context openCursor was called
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key.cc b/third_party/blink/renderer/modules/indexeddb/idb_key.cc
index 7ac9f4d..9a3ac0c0 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_key.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_key.cc
@@ -50,13 +50,13 @@
 // static
 std::unique_ptr<IDBKey> IDBKey::Clone(const IDBKey* rkey) {
   if (!rkey)
-    return IDBKey::CreateNull();
+    return IDBKey::CreateNone();
 
   switch (rkey->GetType()) {
     case mojom::IDBKeyType::Invalid:
       return IDBKey::CreateInvalid();
-    case mojom::IDBKeyType::Null:
-      return IDBKey::CreateNull();
+    case mojom::IDBKeyType::None:
+      return IDBKey::CreateNone();
     case mojom::IDBKeyType::Array: {
       IDBKey::KeyArray lkey_array;
       const auto& rkey_array = rkey->Array();
@@ -83,11 +83,11 @@
 IDBKey::IDBKey()
     : type_(mojom::IDBKeyType::Invalid), size_estimate_(kIDBKeyOverheadSize) {}
 
-// Must be Invalid or Null.
+// Must be Invalid or None.
 IDBKey::IDBKey(mojom::IDBKeyType type)
     : type_(type), size_estimate_(kIDBKeyOverheadSize) {
   DCHECK(type_ == mojom::IDBKeyType::Invalid ||
-         type_ == mojom::IDBKeyType::Null);
+         type_ == mojom::IDBKeyType::None);
 }
 
 // Must be Number or Date.
@@ -168,7 +168,7 @@
 
     // These values cannot be compared to each other.
     case mojom::IDBKeyType::Invalid:
-    case mojom::IDBKeyType::Null:
+    case mojom::IDBKeyType::None:
     case mojom::IDBKeyType::Min:
       NOTREACHED();
       return 0;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key.h b/third_party/blink/renderer/modules/indexeddb/idb_key.h
index 73dc8eba..02f0da3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_key.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_key.h
@@ -59,8 +59,8 @@
     return base::WrapUnique(new IDBKey());
   }
 
-  static std::unique_ptr<IDBKey> CreateNull() {
-    return base::WrapUnique(new IDBKey(mojom::IDBKeyType::Null));
+  static std::unique_ptr<IDBKey> CreateNone() {
+    return base::WrapUnique(new IDBKey(mojom::IDBKeyType::None));
   }
 
   static std::unique_ptr<IDBKey> CreateNumber(double number) {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value.h b/third_party/blink/renderer/modules/indexeddb/idb_value.h
index e8f39091..8bf8eac 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_value.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_value.h
@@ -51,8 +51,8 @@
   // Injects a primary key into a value coming from the backend.
   void SetInjectedPrimaryKey(std::unique_ptr<IDBKey> primary_key,
                              IDBKeyPath primary_key_path) {
-    // If the given key is type Null, ignore it.
-    if (primary_key && primary_key->GetType() == mojom::IDBKeyType::Null)
+    // If the given key is type None, ignore it.
+    if (primary_key && primary_key->GetType() == mojom::IDBKeyType::None)
       primary_key.reset();
     primary_key_ = std::move(primary_key);
     key_path_ = std::move(primary_key_path);
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
index 60ba099..8196754 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
@@ -84,8 +84,6 @@
     GetTag(const std::unique_ptr<blink::IDBKey>& key) {
   DCHECK(key.get());
   switch (key->GetType()) {
-    case blink::mojom::IDBKeyType::Invalid:
-      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID;
     case blink::mojom::IDBKeyType::Array:
       return blink::mojom::IDBKeyDataDataView::Tag::KEY_ARRAY;
     case blink::mojom::IDBKeyType::Binary:
@@ -96,14 +94,15 @@
       return blink::mojom::IDBKeyDataDataView::Tag::DATE;
     case blink::mojom::IDBKeyType::Number:
       return blink::mojom::IDBKeyDataDataView::Tag::NUMBER;
-    case blink::mojom::IDBKeyType::Null:
-      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NULL;
+    case blink::mojom::IDBKeyType::None:
+      return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE;
 
     // Not used, fall through to NOTREACHED.
-    case blink::mojom::IDBKeyType::Min:;
+    case blink::mojom::IDBKeyType::Invalid:  // Only used in blink.
+    case blink::mojom::IDBKeyType::Min:;     // Only used in the browser.
   }
   NOTREACHED();
-  return blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID;
+  return blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE;
 }
 
 // static
@@ -139,11 +138,8 @@
     case blink::mojom::IDBKeyDataDataView::Tag::NUMBER:
       *out = blink::IDBKey::CreateNumber(data.number());
       return true;
-    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_INVALID:
-      *out = blink::IDBKey::CreateInvalid();
-      return true;
-    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_NULL:
-      *out = blink::IDBKey::CreateNull();
+    case blink::mojom::IDBKeyDataDataView::Tag::OTHER_NONE:
+      *out = blink::IDBKey::CreateNone();
       return true;
   }
 
@@ -361,8 +357,8 @@
     blink::mojom::blink::IDBKeyRangePtr,
     const blink::IDBKeyRange*>::Convert(const blink::IDBKeyRange* input) {
   if (!input) {
-    std::unique_ptr<blink::IDBKey> lower = blink::IDBKey::CreateNull();
-    std::unique_ptr<blink::IDBKey> upper = blink::IDBKey::CreateNull();
+    std::unique_ptr<blink::IDBKey> lower = blink::IDBKey::CreateNone();
+    std::unique_ptr<blink::IDBKey> upper = blink::IDBKey::CreateNone();
     return blink::mojom::blink::IDBKeyRange::New(
         std::move(lower), std::move(upper), false /* lower_open */,
         false /* upper_open */);
@@ -379,8 +375,8 @@
 TypeConverter<blink::mojom::blink::IDBKeyRangePtr,
               blink::IDBKeyRange*>::Convert(blink::IDBKeyRange* input) {
   if (!input) {
-    std::unique_ptr<blink::IDBKey> lower = blink::IDBKey::CreateNull();
-    std::unique_ptr<blink::IDBKey> upper = blink::IDBKey::CreateNull();
+    std::unique_ptr<blink::IDBKey> lower = blink::IDBKey::CreateNone();
+    std::unique_ptr<blink::IDBKey> upper = blink::IDBKey::CreateNone();
     return blink::mojom::blink::IDBKeyRange::New(
         std::move(lower), std::move(upper), false /* lower_open */,
         false /* upper_open */);
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h
index 44cd49e..5a94ff71 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h
@@ -110,8 +110,8 @@
   static bool other_invalid(const std::unique_ptr<blink::IDBKey>& key) {
     return key->GetType() == blink::mojom::IDBKeyType::Invalid;
   }
-  static bool other_null(const std::unique_ptr<blink::IDBKey>& key) {
-    return key->GetType() == blink::mojom::IDBKeyType::Null;
+  static bool other_none(const std::unique_ptr<blink::IDBKey>& key) {
+    return key->GetType() == blink::mojom::IDBKeyType::None;
   }
 };
 
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
index d0848d2..0aec3ce 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
@@ -100,8 +100,8 @@
   DCHECK(key && primary_key);
   std::unique_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
 
-  if (key->GetType() == mojom::IDBKeyType::Null &&
-      primary_key->GetType() == mojom::IDBKeyType::Null) {
+  if (key->GetType() == mojom::IDBKeyType::None &&
+      primary_key->GetType() == mojom::IDBKeyType::None) {
     // No key(s), so this would qualify for a prefetch.
     ++continue_count_;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
index 478acf1..7b9a06c 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
@@ -109,7 +109,7 @@
 
 class WebIDBCursorImplTest : public testing::Test {
  public:
-  WebIDBCursorImplTest() : null_key_(IDBKey::CreateNull()) {
+  WebIDBCursorImplTest() : null_key_(IDBKey::CreateNone()) {
     mojom::blink::IDBCursorAssociatedPtr ptr;
     mock_cursor_ = std::make_unique<MockCursorImpl>(
         mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr));
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 3fe7946..8935fb37 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -129,8 +129,7 @@
     : thread_(CurrentThread()),
       persistent_region_(std::make_unique<PersistentRegion>()),
       weak_persistent_region_(std::make_unique<PersistentRegion>()),
-      start_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
-      end_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
+      start_of_stack_(reinterpret_cast<Address*>(WTF::GetStackStart())),
       gc_state_(kNoGCScheduled),
       gc_phase_(GCPhase::kNone),
       reason_for_scheduled_gc_(BlinkGC::GCReason::kForcedGCForTesting),
@@ -280,10 +279,10 @@
 
 NO_SANITIZE_ADDRESS
 void ThreadState::VisitAsanFakeStackForPointer(MarkingVisitor* visitor,
-                                               Address ptr) {
+                                               Address ptr,
+                                               Address* start_of_stack,
+                                               Address* end_of_stack) {
 #if defined(ADDRESS_SANITIZER)
-  Address* start = reinterpret_cast<Address*>(start_of_stack_);
-  Address* end = reinterpret_cast<Address*>(end_of_stack_);
   Address* fake_frame_start = nullptr;
   Address* fake_frame_end = nullptr;
   Address* maybe_fake_frame = reinterpret_cast<Address*>(ptr);
@@ -293,7 +292,8 @@
                                    reinterpret_cast<void**>(&fake_frame_end)));
   if (real_frame_for_fake_frame) {
     // This is a fake frame from the asan fake stack.
-    if (real_frame_for_fake_frame > end && start > real_frame_for_fake_frame) {
+    if (real_frame_for_fake_frame > end_of_stack &&
+        start_of_stack > real_frame_for_fake_frame) {
       // The real stack address for the asan fake frame is
       // within the stack range that we need to scan so we need
       // to visit the values in the fake frame.
@@ -301,7 +301,7 @@
         heap_->CheckAndMarkPointer(visitor, *p);
     }
   }
-#endif
+#endif  // ADDRESS_SANITIZER
 }
 
 // Stack scanning may overrun the bounds of local objects and/or race with
@@ -309,18 +309,15 @@
 NO_SANITIZE_ADDRESS
 NO_SANITIZE_HWADDRESS
 NO_SANITIZE_THREAD
-void ThreadState::VisitStack(MarkingVisitor* visitor) {
+void ThreadState::VisitStack(MarkingVisitor* visitor, Address* end_of_stack) {
   DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack);
 
-  Address* start = reinterpret_cast<Address*>(start_of_stack_);
-  Address* end = reinterpret_cast<Address*>(end_of_stack_);
-
   // Ensure that current is aligned by address size otherwise the loop below
   // will read past start address.
   Address* current = reinterpret_cast<Address*>(
-      reinterpret_cast<intptr_t>(end) & ~(sizeof(Address) - 1));
+      reinterpret_cast<intptr_t>(end_of_stack) & ~(sizeof(Address) - 1));
 
-  for (; current < start; ++current) {
+  for (; current < start_of_stack_; ++current) {
     Address ptr = *current;
 #if defined(MEMORY_SANITIZER)
     // |ptr| may be uninitialized by design. Mark it as initialized to keep
@@ -331,7 +328,7 @@
     __msan_unpoison(&ptr, sizeof(ptr));
 #endif
     heap_->CheckAndMarkPointer(visitor, ptr);
-    VisitAsanFakeStackForPointer(visitor, ptr);
+    VisitAsanFakeStackForPointer(visitor, ptr, start_of_stack_, end_of_stack);
   }
 }
 
@@ -1124,16 +1121,18 @@
 using PushAllRegistersCallback = void (*)(ThreadState*, intptr_t*);
 extern "C" void PushAllRegisters(ThreadState*, PushAllRegistersCallback);
 
-static void DidPushRegisters(ThreadState* state, intptr_t* stack_end) {
-  state->RecordStackEnd(stack_end);
+// static
+void ThreadState::VisitStackAfterPushingRegisters(ThreadState* state,
+                                                  intptr_t* end_of_stack) {
+  state->VisitStack(static_cast<MarkingVisitor*>(state->CurrentVisitor()),
+                    reinterpret_cast<Address*>(end_of_stack));
 }
 
 void ThreadState::PushRegistersAndVisitStack() {
   DCHECK(CheckThread());
   DCHECK(IsGCForbidden());
   DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack);
-  PushAllRegisters(this, DidPushRegisters);
-  VisitStack(static_cast<MarkingVisitor*>(CurrentVisitor()));
+  PushAllRegisters(this, ThreadState::VisitStackAfterPushingRegisters);
 }
 
 void ThreadState::AddObserver(BlinkGCObserver* observer) {
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 69bb0a65..aec5fc7 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -327,10 +327,6 @@
 
   void SafePoint(BlinkGC::StackState);
 
-  void RecordStackEnd(intptr_t* end_of_stack) { end_of_stack_ = end_of_stack; }
-
-  void PushRegistersAndVisitStack();
-
   // A region of non-weak PersistentNodes allocated on the given thread.
   PersistentRegion* GetPersistentRegion() const {
     return persistent_region_.get();
@@ -342,13 +338,6 @@
     return weak_persistent_region_.get();
   }
 
-  // Visit local thread stack and trace all pointers conservatively.
-  void VisitStack(MarkingVisitor*);
-
-  // Visit the asan fake stack frame corresponding to a slot on the
-  // real machine stack if there is one.
-  void VisitAsanFakeStackForPointer(MarkingVisitor*, Address);
-
   // Visit all non-weak persistents allocated on this thread.
   void VisitPersistents(Visitor*);
 
@@ -453,6 +442,12 @@
   // construct ThreadState in it using placement new.
   static uint8_t main_thread_state_storage_[];
 
+  // Callback executed directly after pushing all callee-saved registers.
+  // |end_of_stack| denotes the end of the stack that can hold references to
+  // managed objects.
+  static void VisitStackAfterPushingRegisters(ThreadState*,
+                                              intptr_t* end_of_stack);
+
   ThreadState();
   ~ThreadState() override;
 
@@ -510,6 +505,21 @@
   bool MarkPhaseAdvanceMarking(base::TimeTicks deadline);
   void VerifyMarking(BlinkGC::MarkingType);
 
+  // Visit the stack after pushing registers onto the stack.
+  void PushRegistersAndVisitStack();
+
+  // Visit local thread stack and trace all pointers conservatively. Never call
+  // directly but always call through |PushRegistersAndVisitStack|.
+  void VisitStack(MarkingVisitor*, Address*);
+
+  // Visit the asan fake stack frame corresponding to a slot on the real machine
+  // stack if there is one. Never call directly but always call through
+  // |PushRegistersAndVisitStack|.
+  void VisitAsanFakeStackForPointer(MarkingVisitor*,
+                                    Address,
+                                    Address*,
+                                    Address*);
+
   // ShouldForceConservativeGC
   // implements the heuristics that are used to determine when to collect
   // garbage.
@@ -576,8 +586,10 @@
   base::PlatformThreadId thread_;
   std::unique_ptr<PersistentRegion> persistent_region_;
   std::unique_ptr<PersistentRegion> weak_persistent_region_;
-  intptr_t* start_of_stack_;
-  intptr_t* end_of_stack_;
+
+  // Start of the stack which is the boundary until conservative stack scanning
+  // needs to search for managed pointers.
+  Address* start_of_stack_;
 
   bool in_atomic_pause_ = false;
   bool sweep_forbidden_ = false;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_error.cc b/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
index bee7c5e..8451bf1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
@@ -42,10 +42,6 @@
     "information.";
 }  // namespace
 
-int ResourceError::BlockedByXSSAuditorErrorCode() {
-  return net::ERR_BLOCKED_BY_XSS_AUDITOR;
-}
-
 ResourceError ResourceError::CancelledError(const KURL& url) {
   return ResourceError(net::ERR_ABORTED, url, base::nullopt);
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_error.h b/third_party/blink/renderer/platform/loader/fetch/resource_error.h
index d08439d..61cbc35 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_error.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_error.h
@@ -94,9 +94,6 @@
 
   static bool Compare(const ResourceError&, const ResourceError&);
 
-  // Net error code getters are here to avoid unpreferred header inclusion.
-  static int BlockedByXSSAuditorErrorCode();
-
  private:
   void InitializeDescription();
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index f0cc716..020202c 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -139516,6 +139516,9 @@
    "css/css-lists/nested-marker-ref.html": [
     []
    ],
+   "css/css-lists/parsing/counter-set-valid-expected.txt": [
+    []
+   ],
    "css/css-lists/parsing/list-style-image-computed.sub-expected.txt": [
     []
    ],
@@ -200712,6 +200715,12 @@
      {}
     ]
    ],
+   "css/css-animations/parsing/animation-name-computed.html": [
+    [
+     "css/css-animations/parsing/animation-name-computed.html",
+     {}
+    ]
+   ],
    "css/css-animations/parsing/animation-name-invalid.html": [
     [
      "css/css-animations/parsing/animation-name-invalid.html",
@@ -205326,6 +205335,42 @@
      {}
     ]
    ],
+   "css/css-lists/parsing/counter-increment-invalid.html": [
+    [
+     "css/css-lists/parsing/counter-increment-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/counter-increment-valid.html": [
+    [
+     "css/css-lists/parsing/counter-increment-valid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/counter-reset-invalid.html": [
+    [
+     "css/css-lists/parsing/counter-reset-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/counter-reset-valid.html": [
+    [
+     "css/css-lists/parsing/counter-reset-valid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/counter-set-invalid.html": [
+    [
+     "css/css-lists/parsing/counter-set-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-lists/parsing/counter-set-valid.html": [
+    [
+     "css/css-lists/parsing/counter-set-valid.html",
+     {}
+    ]
+   ],
    "css/css-lists/parsing/list-style-computed.sub.html": [
     [
      "css/css-lists/parsing/list-style-computed.sub.html",
@@ -320618,7 +320663,7 @@
    "support"
   ],
   "README.md": [
-   "5054889dc167880a75bc1b9bd3a49abed666b78f",
+   "919676ee21356e5c6b0efb3c88957a7baa3289bb",
    "support"
   ],
   "WebCryptoAPI/META.yml": [
@@ -344645,6 +344690,10 @@
    "be8a83798908a6771b935e38fe7a8608be9821ed",
    "testharness"
   ],
+  "css/css-animations/parsing/animation-name-computed.html": [
+   "a20aa7d99896b9ac6e9a5e7e319f278207327c8e",
+   "testharness"
+  ],
   "css/css-animations/parsing/animation-name-invalid-expected.txt": [
    "a6d072671edbf2b2528754cf15f469eb7ce08d41",
    "support"
@@ -348426,11 +348475,11 @@
    "testharness"
   ],
   "css/css-box/inheritance-expected.txt": [
-   "bb0e07753e649ffe3a0fbda7ba93985ef14bcf6a",
+   "c21b16bcf873d6bb4732f2bd980a7daa656eb367",
    "support"
   ],
   "css/css-box/inheritance.html": [
-   "5047b8b1df07cb1c774b5f579101d69b2482058a",
+   "22443bf9a94e169a8fce1bd6cfeeead4a12b0ba3",
    "testharness"
   ],
   "css/css-box/parsing/clear-computed.html": [
@@ -366626,7 +366675,7 @@
    "reftest"
   ],
   "css/css-layout-api/constraints-data-sab-failure.https.html": [
-   "eb652d9a552ed0b4466245f5efba78835e55cf20",
+   "c03d35074aa6322057045de60fc7fb73d89c2456",
    "reftest"
   ],
   "css/css-layout-api/constraints-data.https.html": [
@@ -366842,7 +366891,7 @@
    "reftest"
   ],
   "css/css-layout-api/fragment-data-sab-failure.https.html": [
-   "59a9f835728e767b82cb8f147b856161914c6bf2",
+   "5d3619b3647df885005083504fe8d4dd81b2c6a8",
    "reftest"
   ],
   "css/css-layout-api/fragment-data.https.html": [
@@ -367249,6 +367298,34 @@
    "9627ce936ae570325b430a1ac673cd66ae7d4252",
    "reftest"
   ],
+  "css/css-lists/parsing/counter-increment-invalid.html": [
+   "262a38d76b57bb786268b42e8a9dc3df5b343f40",
+   "testharness"
+  ],
+  "css/css-lists/parsing/counter-increment-valid.html": [
+   "277269000a21118a530f97ca5c2cb1061258933c",
+   "testharness"
+  ],
+  "css/css-lists/parsing/counter-reset-invalid.html": [
+   "7a603fffc0d86226e6d56bd0b38c2069593191c4",
+   "testharness"
+  ],
+  "css/css-lists/parsing/counter-reset-valid.html": [
+   "a71572289e41375e360b27a7ea7583538a2627d7",
+   "testharness"
+  ],
+  "css/css-lists/parsing/counter-set-invalid.html": [
+   "9cc5be7c928305e647f97cdc360249dcba19b263",
+   "testharness"
+  ],
+  "css/css-lists/parsing/counter-set-valid-expected.txt": [
+   "a56230af582117f3b0e159399bb86a49e410b1fd",
+   "support"
+  ],
+  "css/css-lists/parsing/counter-set-valid.html": [
+   "3a4a16af92690ae1cc462b707c2e059146c0fcc7",
+   "testharness"
+  ],
   "css/css-lists/parsing/list-style-computed.sub.html": [
    "611fae5bf93636cd4d3402e91b1cd47148303617",
    "testharness"
@@ -444566,7 +444643,7 @@
    "support"
   ],
   "interfaces/web-nfc.idl": [
-   "f082a1e8952cbe6cad0e7e67fa8e7486135f90ba",
+   "d5fe1c085a2e3a127211c48cca97feb2410349a9",
    "support"
   ],
   "interfaces/web-share.idl": [
@@ -478070,7 +478147,7 @@
    "support"
   ],
   "tools/certs/README.md": [
-   "63119ba7f6c8e8f4a48b99eff62dbe1da0e61089",
+   "62c9d24685af8c00151b8fc804e6edca6d6c36d5",
    "support"
   ],
   "tools/certs/cacert.key": [
diff --git a/third_party/blink/web_tests/external/wpt/README.md b/third_party/blink/web_tests/external/wpt/README.md
index 5054889..919676e 100644
--- a/third_party/blink/web_tests/external/wpt/README.md
+++ b/third_party/blink/web_tests/external/wpt/README.md
@@ -180,76 +180,6 @@
 line endings, as it will cause lint errors. For git, please set
 `git config core.autocrlf false` in your working tree.
 
-Certificates
-============
-
-By default pre-generated certificates for the web-platform.test domain
-are provided in [`tools/certs`](tools/certs). If you wish to generate new
-certificates for any reason it's possible to use OpenSSL when starting
-the server, or starting a test run, by providing the
-`--ssl-type=openssl` argument to the `wpt serve` or `wpt run`
-commands.
-
-If you installed OpenSSL in such a way that running `openssl` at a
-command line doesn't work, you also need to adjust the path to the
-OpenSSL binary. This can be done by adding a section to `config.json`
-like:
-
-```
-"ssl": {"openssl": {"binary": "/path/to/openssl"}}
-```
-
-On Windows using OpenSSL typically requires installing an OpenSSL distribution.
-[Shining Light](https://slproweb.com/products/Win32OpenSSL.html)
-provide a convenient installer that is known to work, but requires a
-little extra setup, i.e.:
-
-Run the installer for Win32_OpenSSL_v1.1.0b (30MB). During installation,
-change the default location for where to Copy OpenSSL Dlls from the
-System directory to the /bin directory.
-
-After installation, ensure that the path to OpenSSL (typically,
-this will be `C:\OpenSSL-Win32\bin`) is in your `%Path%`
-[Environment Variable](http://www.computerhope.com/issues/ch000549.htm).
-If you forget to do this part, you will most likely see a 'File Not Found'
-error when you start wptserve.
-
-Finally, set the path value in the server configuration file to the
-default OpenSSL configuration file location. To do this create a file
-called `config.json`.  Then add the OpenSSL configuration below,
-ensuring that the key `ssl/openssl/base_conf_path` has a value that is
-the path to the OpenSSL config file (typically this will be
-`C:\\OpenSSL-Win32\\bin\\openssl.cfg`):
-
-```
-{
-  "ssl": {
-    "type": "openssl",
-    "encrypt_after_connect": false,
-    "openssl": {
-      "openssl_binary": "openssl",
-      "base_path: "_certs",
-      "force_regenerate": false,
-      "base_conf_path": "C:\\OpenSSL-Win32\\bin\\openssl.cfg"
-    },
-  },
-}
-```
-
-### Trusting Root CA
-
-To prevent browser SSL warnings when running HTTPS tests locally, the
-web-platform-tests Root CA file `cacert.pem` in [tools/certs](tools/certs)
-must be added as a trusted certificate in your OS/browser.
-
-**NOTE**: The CA should not be installed in any browser profile used
-outside of tests, since it may be used to generate fake
-certificates. For browsers that use the OS certificate store, tests
-should therefore not be run manually outside a dedicated OS instance
-(e.g. a VM). To avoid this problem when running tests in Chrome or
-Firefox use `wpt run`, which disables certificate checks and therefore
-doesn't require the root CA to be trusted.
-
 Publication
 ===========
 
@@ -339,36 +269,6 @@
 
 [lint-tool]: https://web-platform-tests.org/writing-tests/lint-tool.html
 
-Adding command-line scripts ("tools" subdirs)
----------------------------------------------
-
-Sometimes you may want to add a script to the repository that's meant
-to be used from the command line, not from a browser (e.g., a script
-for generating test files). If you want to ensure (e.g., for security
-reasons) that such scripts won't be handled by the HTTP server, but
-will instead only be usable from the command line, then place them in
-either:
-
-* the `tools` subdir at the root of the repository, or
-
-* the `tools` subdir at the root of any top-level directory in the
-  repository which contains the tests the script is meant to be used
-  with
-
-Any files in those `tools` directories won't be handled by the HTTP
-server; instead the server will return a 404 if a user navigates to
-the URL for a file within them.
-
-If you want to add a script for use with a particular set of tests but
-there isn't yet any `tools` subdir at the root of a top-level
-directory in the repository containing those tests, you can create a
-`tools` subdir at the root of that top-level directory and place your
-scripts there.
-
-For example, if you wanted to add a script for use with tests in the
-`notifications` directory, create the `notifications/tools` subdir and
-put your script there.
-
 Test Review
 ===========
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-layout-api/constraints-data-sab-failure.https.html b/third_party/blink/web_tests/external/wpt/css/css-layout-api/constraints-data-sab-failure.https.html
index eb652d9a..c03d3507 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-layout-api/constraints-data-sab-failure.https.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-layout-api/constraints-data-sab-failure.https.html
@@ -35,14 +35,9 @@
     let childFragment = null;
 
     try {
-      // We need SABs to be enabled to properly run this test.
-      if (typeof SharedArrayBuffer !== 'undefined') {
-        childFragment = yield child.layoutNextFragment({
-          data: { sab: new SharedArrayBuffer(4) }
-        });
-      } else {
-        throw Error();
-      }
+      childFragment = yield child.layoutNextFragment({
+        data: { sab: new SharedArrayBuffer(4) }
+      });
     } catch(e) {
       // Success! The structured cloning algorithm should have thrown an error.
       childFragment = yield child.layoutNextFragment({});
diff --git a/third_party/blink/web_tests/external/wpt/css/css-layout-api/fragment-data-sab-failure.https.html b/third_party/blink/web_tests/external/wpt/css/css-layout-api/fragment-data-sab-failure.https.html
index 59a9f83..5d3619b3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-layout-api/fragment-data-sab-failure.https.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-layout-api/fragment-data-sab-failure.https.html
@@ -34,11 +34,7 @@
   *layout(children, edges, constraints, styleMap) {
     const childFragments = yield children.map(child => child.layoutNextFragment());
 
-    if (typeof SharedArrayBuffer !== 'undefined') {
-      return {autoBlockSize: 0, childFragments, data: {sab: new SharedArrayBuffer(4) }};
-    } else {
-      throw Error();
-    }
+    return {autoBlockSize: 0, childFragments, data: {sab: new SharedArrayBuffer(4) }};
   }
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-invalid.html
new file mode 100644
index 0000000..262a38d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-invalid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-increment with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-increment">
+<meta name="assert" content="counter-increment supports only the grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value('counter-increment', 'none chapter');
+test_invalid_value('counter-increment', '3');
+test_invalid_value('counter-increment', '99 imagenum');
+test_invalid_value('counter-increment', 'section -1, imagenum 99');
+test_invalid_value('counter-increment', 'section 3.14');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-valid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-valid.html
new file mode 100644
index 0000000..2772690
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-increment-valid.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-increment with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-increment">
+<meta name="assert" content="counter-increment supports the full grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value('counter-increment', 'none');
+test_valid_value('counter-increment', 'chapter', 'chapter 1');
+test_valid_value('counter-increment', 'section -1');
+test_valid_value('counter-increment', 'first -1 second third 99', 'first -1 second 1 third 99');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-invalid.html
new file mode 100644
index 0000000..7a603fff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-invalid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-reset with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
+<meta name="assert" content="counter-reset supports only the grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value('counter-reset', 'none chapter');
+test_invalid_value('counter-reset', '3');
+test_invalid_value('counter-reset', '99 imagenum');
+test_invalid_value('counter-reset', 'section -1, imagenum 99');
+test_invalid_value('counter-reset', 'section 3.14');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-valid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-valid.html
new file mode 100644
index 0000000..a715722
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-reset-valid.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-reset with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
+<meta name="assert" content="counter-reset supports the full grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value('counter-reset', 'none');
+test_valid_value('counter-reset', 'chapter', 'chapter 0');
+test_valid_value('counter-reset', 'section -1');
+test_valid_value('counter-reset', 'first -1 second third 99', 'first -1 second 0 third 99');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-invalid.html
new file mode 100644
index 0000000..9cc5be7c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-invalid.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-set with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-set">
+<meta name="assert" content="counter-set supports only the grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value('counter-set', 'none chapter');
+test_invalid_value('counter-set', '3');
+test_invalid_value('counter-set', '99 imagenum');
+test_invalid_value('counter-set', 'section -1, imagenum 99');
+test_invalid_value('counter-set', 'section 3.14');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid-expected.txt
new file mode 100644
index 0000000..a56230a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL e.style['counter-set'] = "none" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['counter-set'] = "chapter" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['counter-set'] = "section -1" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['counter-set'] = "first -1 second third 99" should set the property value assert_not_equals: property should be set got disallowed value ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid.html
new file mode 100644
index 0000000..3a4a16a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/counter-set-valid.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Lists: parsing counter-set with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-lists-3/#propdef-counter-set">
+<meta name="assert" content="counter-set supports the full grammar '[ <counter-name> <integer>? ]+ | none'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value('counter-set', 'none');
+test_valid_value('counter-set', 'chapter', 'chapter 0');
+test_valid_value('counter-set', 'section -1');
+test_valid_value('counter-set', 'first -1 second third 99', 'first -1 second 0 third 99');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl b/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
index f082a1e..d5fe1c0 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/web-nfc.idl
@@ -33,13 +33,7 @@
   NDEFRecordData data;
 };
 
-enum NDEFRecordType {
-  "empty",
-  "text",
-  "url",
-  "json",
-  "opaque"
-};
+typedef DOMString NDEFRecordType;
 
 typedef (DOMString or ArrayBuffer or NDEFMessageInit) NDEFMessageSource;
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/certs/README.md b/third_party/blink/web_tests/external/wpt/tools/certs/README.md
index 63119ba..62c9d24 100644
--- a/third_party/blink/web_tests/external/wpt/tools/certs/README.md
+++ b/third_party/blink/web_tests/external/wpt/tools/certs/README.md
@@ -1,11 +1,76 @@
-To enable https://web-platform.test:8443/, add cacert.pem to your browser as Certificate Authority.
+# WPT Test Certificates
+
+The web-platform-tests project maintains a set of SSL certificates to allow
+contributors to execute tests requiring HTTPS locally.
+
+## Trusting Root CA
+
+To prevent browser SSL warnings when running HTTPS tests locally, the
+web-platform-tests Root CA file `cacert.pem` in the `tools/certs/` directory
+must be added as a trusted certificate in your OS/browser.
 
 For Firefox, go to about:preferences and search for "certificates".
 
-For browsers that use the Certificate Authorities of the underlying OS, such as Chrome and Safari,
-you need to adjust the OS. For macOS, go to Keychain Access and add the certificate under
-**login**.
+For browsers that use the Certificate Authorities of the underlying OS, such as
+Chrome and Safari, you need to adjust the OS. For macOS, go to Keychain Access
+and add the certificate under **login**.
 
-### Updating these certs
+**NOTE**: The CA should not be installed in any browser profile used
+outside of tests, since it may be used to generate fake
+certificates. For browsers that use the OS certificate store, tests
+should therefore not be run manually outside a dedicated OS instance
+(e.g. a VM). To avoid this problem when running tests in Chrome or
+Firefox, use `wpt run`, which disables certificate checks and therefore
+doesn't require the root CA to be trusted.
 
-From the root, run `./wpt serve --config tools/certs/config.json` and terminate it after it has started up.
+## Regenerating certificates
+
+If you wish to generate new certificates for any reason, it's possible to use
+OpenSSL when starting the server, or starting a test run, by providing the
+`--ssl-type=openssl` argument to the `wpt serve` or `wpt run` commands.
+
+If you installed OpenSSL in such a way that running `openssl` at a
+command line doesn't work, you also need to adjust the path to the
+OpenSSL binary. This can be done by adding a section to `config.json`
+like:
+
+```
+"ssl": {"openssl": {"binary": "/path/to/openssl"}}
+```
+
+On Windows using OpenSSL typically requires installing an OpenSSL distribution.
+[Shining Light](https://slproweb.com/products/Win32OpenSSL.html)
+provide a convenient installer that is known to work, but requires a
+little extra setup, i.e.:
+
+Run the installer for Win32_OpenSSL_v1.1.0b (30MB). During installation,
+change the default location for where to Copy OpenSSL Dlls from the
+System directory to the /bin directory.
+
+After installation, ensure that the path to OpenSSL (typically,
+this will be `C:\OpenSSL-Win32\bin`) is in your `%Path%`
+[Environment Variable](http://www.computerhope.com/issues/ch000549.htm).
+If you forget to do this part, you will most likely see a 'File Not Found'
+error when you start wptserve.
+
+Finally, set the path value in the server configuration file to the
+default OpenSSL configuration file location. To do this, create a file
+called `config.json`.  Then add the OpenSSL configuration below,
+ensuring that the key `ssl/openssl/base_conf_path` has a value that is
+the path to the OpenSSL config file (typically this will be
+`C:\\OpenSSL-Win32\\bin\\openssl.cfg`):
+
+```
+{
+  "ssl": {
+    "type": "openssl",
+    "encrypt_after_connect": false,
+    "openssl": {
+      "openssl_binary": "openssl",
+      "base_path: "_certs",
+      "force_regenerate": false,
+      "base_conf_path": "C:\\OpenSSL-Win32\\bin\\openssl.cfg"
+    },
+  },
+}
+```
diff --git a/third_party/custom_tabs_client/README.chromium b/third_party/custom_tabs_client/README.chromium
index 0bf829b..95d433cc 100644
--- a/third_party/custom_tabs_client/README.chromium
+++ b/third_party/custom_tabs_client/README.chromium
@@ -3,7 +3,7 @@
 URL: https://chromium.googlesource.com/external/github.com/GoogleChrome/custom-tabs-client
 Version: unknown
 License: Apache 2.0
-Security Critical: yes
+Security Critical: no
 License Android Compatible: yes
 
 Description:
@@ -16,11 +16,4 @@
 The example applicaton also presents how to use Browser Actions, including
 creating request intent and adding custom items.
 
-The actual code that Chromium builds from is in
-//third_party/android_sdk/androidx_browser, this subdirectory is kept around
-for the example app (the custom_tabs_client_example_apk target).
-
-TODO(peconn): Get rid of src/customtabs and depend instead on
-androidx_browser.
-
 Local Modifications: None
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 58d8f488..33216ae 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: 4f87f10f5d5a589dbfd10815222d5d37fe267f06
+Revision: f4372718f4cabd57efeab02f5726ed147f29768f
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/android/customtabs_benchmark/BUILD.gn b/tools/android/customtabs_benchmark/BUILD.gn
index e8dec60..27cf5e0 100644
--- a/tools/android/customtabs_benchmark/BUILD.gn
+++ b/tools/android/customtabs_benchmark/BUILD.gn
@@ -21,6 +21,6 @@
   deps = [
     ":customtabs_benchmark_apk_resources",
     "//third_party/android_deps:android_support_v4_java",
-    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
+    "//third_party/custom_tabs_client:custom_tabs_support_java",
   ]
 }
diff --git a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
index b16f4c13..3e205ec8 100644
--- a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
+++ b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
@@ -18,6 +18,11 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.support.customtabs.CustomTabsCallback;
+import android.support.customtabs.CustomTabsClient;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsServiceConnection;
+import android.support.customtabs.CustomTabsSession;
 import android.support.v4.app.BundleCompat;
 import android.util.Log;
 import android.view.View;
@@ -34,12 +39,6 @@
 import java.util.Random;
 import java.util.Set;
 
-import androidx.browser.customtabs.CustomTabsCallback;
-import androidx.browser.customtabs.CustomTabsClient;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsServiceConnection;
-import androidx.browser.customtabs.CustomTabsSession;
-
 /** Activity used to benchmark Custom Tabs PLT.
  *
  * This activity contains benchmark code for two modes:
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index 17f6778..e9af384 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -114,8 +114,10 @@
     <classpathentry kind="src" path="third_party/android_data_chart/java/src"/>
     <classpathentry kind="src" path="third_party/android_protobuf/src/java/src/main/java" including="com/google/protobuf/nano/*"/>
     <classpathentry kind="src" path="third_party/android_swipe_refresh/java/src"/>
-    <classpathentry kind="src" path="third_party/androidx_browser/src/browser/src"/>
     <classpathentry kind="src" path="third_party/cacheinvalidation/src/java"/>
+    <classpathentry kind="src" path="third_party/custom_tabs_client/src/customtabs/src"/>
+    <classpathentry kind="src" path="third_party/custom_tabs_client/src/Application/src/main/java"/>
+    <classpathentry kind="src" path="third_party/custom_tabs_client/src/shared/src/main/java"/>
     <classpathentry kind="src" path="third_party/gif_player/src"/>
     <classpathentry kind="src" path="third_party/junit/src/src/main/java"/>
     <classpathentry kind="src" path="third_party/mockito/src/src/main/java"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 13e7390..732a4aa 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -16957,8 +16957,10 @@
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
-    Number of open guest browser windows at the same time. Recorded when a new
-    browser is created.
+    Number of open guest windows at the same time. Recorded when a new guest
+    window is created. Please note that this metric double counts the lower
+    numbers, meaning that if user opens three windows (without closing any in
+    between), then buckets 1, 2, and 3 will all be incremented in turn.
   </summary>
 </histogram>
 
@@ -16966,8 +16968,10 @@
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
-    Number of open incognito browser windows at the same time. Recorded when a
-    new browser is created.
+    Number of open incognito windows at the same time. Recorded when a new
+    incognito window is created. Please note that this metric double counts the
+    lower numbers, meaning that if user opens three windows (without closing any
+    in between), then buckets 1, 2, and 3 will all be incremented in turn.
   </summary>
 </histogram>
 
@@ -130630,8 +130634,34 @@
 </histogram>
 
 <histogram
+    name="SmartLock.Performance.AuthenticationToReceiveFirstRemoteStatusDuration.Unlock"
+    units="ms" expires_after="2020-07-23">
+<!-- Name completed by histogram_suffixes name="SmartLockStatusTypes" -->
+
+  <owner>hansberry@chromium.org</owner>
+  <owner>jhawkins@chromium.org</owner>
+  <summary>
+    The duration of time between when Smart Lock successfully establishes a
+    secure channel connection to the host device, and receives the initial
+    remote status from it -- this informs if the device can be unlocked on the
+    first remote status (i.e., if the Smart Lock icon is yellow or green).
+
+    Suffixed by the type of remote status which was the first to be received.
+    View the base histogram to see results for all remote status types
+    aggregated together, and suffixed histograms for the results of just that
+    particular remote status type.
+
+    See SmartLock.GetRemoteStatus.Unlock for the success rate of fetching the
+    remote status from the host.
+  </summary>
+</histogram>
+
+<histogram
     name="SmartLock.Performance.AuthenticationToReceiveUnlockableRemoteStatus.Duration.Unlock"
     units="ms" expires_after="2020-02-01">
+  <obsolete>
+    Removed 2019/08.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <summary>
     The duration of time between when Smart Lock successfully establishes a
@@ -130642,8 +130672,64 @@
 </histogram>
 
 <histogram
+    name="SmartLock.Performance.ShowLockScreenToShowFirstStatusToUserDuration.Unlock"
+    units="ms" expires_after="2020-07-23">
+<!-- Name completed by histogram_suffixes name="SmartLockStatusTypes" -->
+
+  <owner>hansberry@chromium.org</owner>
+  <owner>jhawkins@chromium.org</owner>
+  <summary>
+    The duration of time between when the user locks their screen or wakes their
+    device (either opening a clamshell device or waking up a tablet), and when
+    the user is first provided a visible indication of Smart Lock's status (the
+    Smart Lock icon presents as either yellow or green, with a tooltip
+    explaining the status).
+
+    Suffixed by the type of status which the user is first presented with. View
+    the base histogram to see results for all status types aggregated together,
+    and suffixed histograms for the results of just that particular remote
+    status type.
+
+    See SmartLock.GetRemoteStatus.Unlock for the success rate of fetching the
+    remote status from the host.
+  </summary>
+</histogram>
+
+<histogram
+    name="SmartLock.Performance.StartScanToReceiveFirstRemoteStatusDuration.Unlock"
+    units="ms" expires_after="2020-07-23">
+<!-- Name completed by histogram_suffixes name="SmartLockStatusTypes" -->
+
+  <owner>hansberry@chromium.org</owner>
+  <owner>jhawkins@chromium.org</owner>
+  <summary>
+    The duration of time between when Smart Lock begins to try to find the host
+    device, and receives the initial remote status from it -- this informs if
+    the device can be unlocked on the first remote status (i.e., if the Smart
+    Lock icon is yellow or green).
+
+    Suffixed by the type of remote status which was the first to be received.
+    View the base histogram to see results for all remote status types
+    aggregated together, and suffixed histograms for the results of just that
+    particular remote status type.
+
+    See
+    MultiDevice.SecureChannel.BLE.Performance.StartScanToAuthenticationDuration.Background
+    and
+    SmartLock.Performance.AuthenticationToReceiveFirstRemoteStatus.Unlock.Duration
+    for breakdowns of this metric.
+
+    See SmartLock.GetRemoteStatus.Unlock for the success rate of fetching the
+    remote status from the host.
+  </summary>
+</histogram>
+
+<histogram
     name="SmartLock.Performance.StartScanToReceiveUnlockableRemoteStatus.Duration.Unlock"
     units="ms" expires_after="2020-02-01">
+  <obsolete>
+    Removed 2019/08.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <summary>
     The duration of time between when Smart Lock begins to try to find the host
@@ -138056,7 +138142,12 @@
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
-    Number of open tabs in each guest window. Recorded when a new tab is opened.
+    Number of open tabs in each guest window. Recorded once a new tab in a guest
+    window is opened and adds one to the bucket of number of tabs in that
+    particular window (it does not count the total number of tabs in all open
+    guest windows). Please note that this metric double counts the lower
+    numbers, meaning that if user opens three tabs (without closing any in
+    between), then buckets 1, 2, and 3 will all be incremented in turn.
   </summary>
 </histogram>
 
@@ -138064,8 +138155,12 @@
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
-    Number of open tabs in each incognito window. Recorded when a new tab is
-    opened.
+    Number of open tabs in each incognito window. Recorded once a new tab is
+    opened in an incognito window and adds one to the bucket of number of tabs
+    in that particular window (it does not count the total number of tabs in all
+    open incognito windows). Please note that this metric double counts the
+    lower numbers, meaning that if user opens three tabs (without closing any in
+    between), then buckets 1, 2, and 3 will all be incremented in turn.
   </summary>
 </histogram>
 
@@ -143674,7 +143769,7 @@
   </summary>
 </histogram>
 
-<histogram name="UMA.LowEntropySource3Value" expires_after="2019-08-30">
+<histogram name="UMA.LowEntropySource3Value" expires_after="2020-08-30">
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <summary>
@@ -143684,7 +143779,7 @@
   </summary>
 </histogram>
 
-<histogram name="UMA.LowEntropySourceValue" expires_after="2019-08-30">
+<histogram name="UMA.LowEntropySourceValue" expires_after="2020-03-01">
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <summary>
@@ -169692,6 +169787,17 @@
   <affected-histogram name="Skia.DrawScaleFactor"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="SmartLockStatusTypes" separator=".">
+  <suffix name="Other"/>
+  <suffix name="Unlockable"/>
+  <affected-histogram
+      name="SmartLock.Performance.AuthenticationToReceiveFirstRemoteStatusDuration.Unlock"/>
+  <affected-histogram
+      name="SmartLock.Performance.ShowLockScreenToShowFirstStatusToUserDuration.Unlock"/>
+  <affected-histogram
+      name="SmartLock.Performance.StartScanToReceiveFirstRemoteStatusDuration.Unlock"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="SmoothnessSequenceTypes" separator=".">
   <suffix name="CompositorAnimation" label="Compositor-driven animation"/>
   <suffix name="MainThreadAnimation" label="Main-thread driven animation"/>
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index 0d2e5c1f..ad2efc1 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -1793,6 +1793,8 @@
       return "supportsTextLocation";
     case ax::mojom::BoolAttribute::kIsLineBreakingObject:
       return "isLineBreakingObject";
+    case ax::mojom::BoolAttribute::kIsPageBreakingObject:
+      return "isPageBreakingObject";
   }
 
   return "";
@@ -1831,6 +1833,8 @@
     return ax::mojom::BoolAttribute::kSupportsTextLocation;
   if (0 == strcmp(bool_attribute, "isLineBreakingObject"))
     return ax::mojom::BoolAttribute::kIsLineBreakingObject;
+  if (0 == strcmp(bool_attribute, "isPageBreakingObject"))
+    return ax::mojom::BoolAttribute::kIsPageBreakingObject;
   return ax::mojom::BoolAttribute::kNone;
 }
 
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 05a962d..dfddc67 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -662,8 +662,10 @@
 // given attribute, while another tree source only uses two.
 enum BoolAttribute {
   kNone,
+
   // Generic busy state, does not have to be on a live region.
   kBusy,
+
   // The object is at the root of an editable field, such as a content
   // editable.
   kEditableRoot,
@@ -706,6 +708,9 @@
   // Indicates whether this node causes a hard line-break
   // (e.g. block level elements, or <br>)
   kIsLineBreakingObject,
+
+  // Indicates whether this node causes a page break
+  kIsPageBreakingObject,
 };
 
 enum IntListAttribute {
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index d8c510a..e7e564b 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -56,7 +56,7 @@
 AXNode* AXNode::GetUnignoredParent() const {
   DCHECK(!tree_->GetTreeUpdateInProgressState());
   AXNode* result = parent();
-  while (result && result->data().HasState(ax::mojom::State::kIgnored))
+  while (result && result->IsIgnored())
     result = result->parent();
   return result;
 }
@@ -83,7 +83,7 @@
   while (parent_node) {
     if (index < parent_node->children().size()) {
       AXNode* child = parent_node->children()[index];
-      if (!child->data().HasState(ax::mojom::State::kIgnored))
+      if (!child->IsIgnored())
         return child;  // valid position (unignored child)
 
       // If the node is ignored, drill down to the ignored node's first child.
@@ -92,7 +92,7 @@
     } else {
       // If the parent is not ignored and we are past all of its children, there
       // is no next sibling.
-      if (!parent_node->data().HasState(ax::mojom::State::kIgnored))
+      if (!parent_node->IsIgnored())
         return nullptr;
 
       // If the parent is ignored and we are past all of its children, continue
@@ -112,7 +112,7 @@
   while (parent_node) {
     if (!before_first_child) {
       AXNode* child = parent_node->children()[index];
-      if (!child->data().HasState(ax::mojom::State::kIgnored))
+      if (!child->IsIgnored())
         return child;  // valid position (unignored child)
 
       // If the node is ignored, drill down to the ignored node's last child.
@@ -122,7 +122,7 @@
     } else {
       // If the parent is not ignored and we are past all of its children, there
       // is no next sibling.
-      if (!parent_node->data().HasState(ax::mojom::State::kIgnored))
+      if (!parent_node->IsIgnored())
         return nullptr;
 
       // If the parent is ignored and we are past all of its children, continue
@@ -177,7 +177,7 @@
 }
 
 void AXNode::UpdateUnignoredCachedValues() {
-  if (!data().HasState(ax::mojom::State::kIgnored))
+  if (!IsIgnored())
     UpdateUnignoredCachedValuesRecursive(0);
 }
 
@@ -752,7 +752,7 @@
 int AXNode::UpdateUnignoredCachedValuesRecursive(int startIndex) {
   int count = 0;
   for (AXNode* child : children_) {
-    if (child->data().HasState(ax::mojom::State::kIgnored)) {
+    if (child->IsIgnored()) {
       child->unignored_index_in_parent_ = 0;
       count += child->UpdateUnignoredCachedValuesRecursive(startIndex + count);
     } else {
@@ -769,9 +769,8 @@
   AXNode* result = parent();
   // Continue walking up while parent is invalid, ignored, a generic container,
   // or unknown.
-  while (result && (result->data().HasState(ax::mojom::State::kIgnored) ||
+  while (result && (result->IsIgnored() ||
                     result->data().role == ax::mojom::Role::kGenericContainer ||
-                    result->data().role == ax::mojom::Role::kIgnored ||
                     result->data().role == ax::mojom::Role::kUnknown)) {
     result = result->parent();
   }
@@ -785,7 +784,7 @@
 
   for (int i = static_cast<int>(children().size()) - 1; i >= 0; --i) {
     AXNode* child = children_[i];
-    if (!child->data().HasState(ax::mojom::State::kIgnored))
+    if (!child->IsIgnored())
       return child;
 
     AXNode* descendant = child->ComputeLastUnignoredChildRecursive();
@@ -799,7 +798,7 @@
   DCHECK(!tree_->GetTreeUpdateInProgressState());
   for (size_t i = 0; i < children().size(); i++) {
     AXNode* child = children_[i];
-    if (!child->data().HasState(ax::mojom::State::kIgnored))
+    if (!child->IsIgnored())
       return child;
 
     AXNode* descendant = child->ComputeFirstUnignoredChildRecursive();
@@ -809,4 +808,8 @@
   return nullptr;
 }
 
+bool AXNode::IsIgnored() const {
+  return ui::IsIgnored(data());
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index b692a07..40814d27 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -41,6 +41,7 @@
     virtual int32_t GetSetSize(const AXNode& node,
                                const AXNode* ordered_set) = 0;
     virtual bool GetTreeUpdateInProgressState() const = 0;
+    virtual bool HasPaginationSupport() const = 0;
   };
 
   template <typename NodeType,
@@ -349,6 +350,9 @@
   // part of the language detection feature.
   void SetLanguageInfo(std::unique_ptr<AXLanguageInfo> lang_info);
 
+  // Returns true if node has ignored state or ignored role.
+  bool IsIgnored() const;
+
  private:
   // Computes the text offset where each line starts by traversing all child
   // leaf nodes.
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 7775c4a..eca9ad0 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -1428,6 +1428,9 @@
       case ax::mojom::BoolAttribute::kIsLineBreakingObject:
         result += " is_line_breaking_object=" + value;
         break;
+      case ax::mojom::BoolAttribute::kIsPageBreakingObject:
+        result += " is_page_breaking_object=" + value;
+        break;
       case ax::mojom::BoolAttribute::kNone:
         break;
     }
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 6a60fce4..2d60ff6 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -49,6 +49,68 @@
  protected:
   void SetUp() override;
   void TearDown() override;
+  AXTree* CreateMultipageDocument(ui::AXNodeData& root_data,
+                                  ui::AXNodeData& page_1_data,
+                                  ui::AXNodeData& page_1_text_data,
+                                  ui::AXNodeData& page_2_data,
+                                  ui::AXNodeData& page_2_text_data,
+                                  ui::AXNodeData& page_3_data,
+                                  ui::AXNodeData& page_3_text_data) {
+    AXNodePosition::SetTreeForTesting(nullptr);
+
+    root_data.id = 1;
+    root_data.role = ax::mojom::Role::kDocument;
+
+    page_1_data.id = 2;
+    page_1_data.role = ax::mojom::Role::kRegion;
+    page_1_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    page_1_text_data.id = 3;
+    page_1_text_data.role = ax::mojom::Role::kStaticText;
+    page_1_text_data.SetName("some text on page 1");
+    page_1_text_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+    page_1_data.child_ids = {3};
+
+    page_2_data.id = 4;
+    page_2_data.role = ax::mojom::Role::kRegion;
+    page_2_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    page_2_text_data.id = 5;
+    page_2_text_data.role = ax::mojom::Role::kStaticText;
+    page_2_text_data.SetName("some text on page 2");
+    page_2_text_data.AddIntAttribute(
+        ax::mojom::IntAttribute::kTextStyle,
+        static_cast<int32_t>(ax::mojom::TextStyle::kBold));
+    page_2_data.child_ids = {5};
+
+    page_3_data.id = 6;
+    page_3_data.role = ax::mojom::Role::kRegion;
+    page_3_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    page_3_text_data.id = 7;
+    page_3_text_data.role = ax::mojom::Role::kStaticText;
+    page_3_text_data.SetName("some more text on page 3");
+    page_3_data.child_ids = {7};
+
+    root_data.child_ids = {2, 4, 6};
+
+    ui::AXTreeUpdate update;
+    ui::AXTreeData tree_data;
+    AXTreeID new_id = AXTreeID::CreateNewAXTreeID();
+    tree_data.tree_id = new_id;
+    update.tree_data = tree_data;
+    update.has_tree_data = true;
+    update.root_id = root_data.id;
+    update.nodes = {root_data,       page_1_data,      page_1_text_data,
+                    page_2_data,     page_2_text_data, page_3_data,
+                    page_3_text_data};
+
+    return new AXTree(update);
+  }
 
   void AssertTextLengthEquals(const AXTree* tree,
                               int32_t node_id,
@@ -137,6 +199,18 @@
 const char* AXPositionTest::TEXT_VALUE = "Line 1\nLine 2";
 
 void AXPositionTest::SetUp() {
+  // root_
+  //  |
+  //  +------------+-----------+
+  //  |            |           |
+  // button_  check_box_  text_field_
+  //                           |
+  //               +-----------+------------+
+  //               |           |            |
+  //        static_text1_  line_break_   static_text2_
+  //               |                        |
+  //        inline_box1_                 inline_box2_
+
   root_.id = ROOT_ID;
   button_.id = BUTTON_ID;
   check_box_.id = CHECK_BOX_ID;
@@ -1617,6 +1691,279 @@
   AXNodePosition::SetTreeForTesting(&tree_);
 }
 
+TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) {
+  AXNodePosition::SetTreeForTesting(nullptr);
+
+  ui::AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
+      page_2_text_data, page_3_data, page_3_text_data;
+  std::unique_ptr<ui::AXTree> new_tree(CreateMultipageDocument(
+      root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data,
+      page_3_data, page_3_text_data));
+  AXNodePosition::SetTreeForTesting(new_tree.get());
+
+  // Test CreateNextPageStartPosition at the start of the document.
+  TestPositionType text_position = AXNodePosition::CreateTextPosition(
+      new_tree->data().tree_id, page_1_text_data.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, text_position);
+  TestPositionType test_position = text_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(19, test_position->text_offset());
+
+  // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary
+  test_position = text_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(19, test_position->text_offset());
+
+  // Test CreateNextPageStartPosition until the end of document is reached
+  test_position = text_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  test_position = test_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  test_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(24, test_position->text_offset());
+
+  test_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(24, test_position->text_offset());
+
+  // Moving forward past the end should return a null position
+  TestPositionType null_position = test_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  null_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  // Now move backward through the document
+  test_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(19, test_position->text_offset());
+
+  test_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(19, test_position->text_offset());
+
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  // Moving before the start should return a null position
+  null_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  null_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  AXNodePosition::SetTreeForTesting(&tree_);
+}
+
+TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) {
+  AXNodePosition::SetTreeForTesting(nullptr);
+  ui::AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
+      page_2_text_data, page_3_data, page_3_text_data;
+  std::unique_ptr<ui::AXTree> new_tree(CreateMultipageDocument(
+      root_data, page_1_data, page_1_text_data, page_2_data, page_2_text_data,
+      page_3_data, page_3_text_data));
+  AXNodePosition::SetTreeForTesting(new_tree.get());
+
+  // Test CreateNextPageStartPosition at the start of the document.
+  TestPositionType tree_position = AXNodePosition::CreateTreePosition(
+      new_tree->data().tree_id, page_1_data.id, 0 /* child_index */);
+  ASSERT_NE(nullptr, tree_position);
+  TestPositionType test_position = tree_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->child_index());
+
+  // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary
+  test_position = tree_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_1_data.id, test_position->anchor_id());
+  EXPECT_EQ(1, test_position->child_index());
+
+  // Test CreateNextPageStartPosition until the end of document is reached
+  test_position = tree_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
+
+  test_position = test_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
+
+  test_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->child_index());
+
+  test_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->child_index());
+
+  // Moving forward past the end should return a null position
+  TestPositionType null_position = test_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  null_position = test_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  // Now move backward through the document
+  test_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->child_index());
+
+  test_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->child_index());
+
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
+
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
+  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
+
+  // Moving before the start should return a null position
+  null_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  null_position = test_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, null_position);
+  EXPECT_TRUE(null_position->IsNullPosition());
+
+  AXNodePosition::SetTreeForTesting(&tree_);
+}
+
+TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) {
+  TestPositionType null_position = AXNodePosition::CreateNullPosition();
+  ASSERT_NE(nullptr, null_position);
+  TestPositionType test_position =
+      null_position->CreatePreviousPageStartPosition(
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  test_position = null_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  test_position = null_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  test_position = null_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+}
+
 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithNullPosition) {
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
@@ -1626,6 +1973,77 @@
   EXPECT_TRUE(test_position->IsNullPosition());
 }
 
+TEST_F(AXPositionTest, CreatePagePositionWithNonPaginatedDocument) {
+  TestPositionType text_position = AXNodePosition::CreateTextPosition(
+      tree_.data().tree_id, static_text1_.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, text_position);
+
+  // Non-paginated documents should move to the start of the document for
+  // CreatePreviousPageStartPosition (treating the entire document as a single
+  // page)
+  TestPositionType test_position =
+      text_position->CreatePreviousPageStartPosition(
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(button_.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  // Since there is no next page, CreateNextPageStartPosition should return a
+  // null position
+  test_position = text_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  // Since there is no previous page, CreatePreviousPageEndPosition should
+  // return a null position
+  test_position = text_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  // Since there are no distinct pages, CreateNextPageEndPosition should move
+  // to the end of the document, as if it's one large page.
+  test_position = text_position->CreateNextPageEndPosition(
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
+  EXPECT_EQ(6, test_position->text_offset());
+
+  // CreatePreviousPageStartPosition should move back to the beginning of the
+  // document
+  test_position = test_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsTextPosition());
+  EXPECT_EQ(button_.id, test_position->anchor_id());
+  EXPECT_EQ(0, test_position->text_offset());
+
+  // Since there's no next page, CreateNextPageStartPosition should return a
+  // null position
+  test_position = test_position->CreateNextPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  // Since there's no previous page, CreatePreviousPageEndPosition should return
+  // a null position
+  test_position = text_position->CreatePreviousPageEndPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  // Since there's no previous page, CreatePreviousPageStartPosition should
+  // return a null position
+  test_position = text_position->CreatePreviousPageStartPosition(
+      AXBoundaryBehavior::CrossBoundary);
+  EXPECT_NE(nullptr, test_position);
+  EXPECT_TRUE(test_position->IsNullPosition());
+}
+
 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTreePosition) {
   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
       tree_.data().tree_id, root_.id, 0 /* child_index */);
@@ -2033,6 +2451,110 @@
   EXPECT_EQ(3, test_position->text_offset());
 }
 
+TEST_F(AXPositionTest, CreateNextLeafTreePosition) {
+  TestPositionType root_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, root_.id, 0 /* child_index */);
+  ASSERT_TRUE(root_position->IsTreePosition());
+
+  TestPositionType button_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, button_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, check_box_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, inline_box1_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, line_break_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, inline_box2_.id, AXNodePosition::BEFORE_TEXT);
+
+  TestPositionType test_position = root_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *button_position);
+
+  test_position = test_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *checkbox_position);
+
+  test_position = test_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *inline_box1_position);
+
+  test_position = test_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *line_break_position);
+
+  test_position = test_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *inline_box2_position);
+
+  test_position = test_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  TestPositionType root_text_position = AXNodePosition::CreateTextPosition(
+      tree_.data().tree_id, root_.id, 2 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(root_text_position->IsTextPosition());
+
+  test_position = root_text_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *inline_box1_position);
+
+  TestPositionType inline_box1_text_position =
+      AXNodePosition::CreateTextPosition(tree_.data().tree_id, inline_box1_.id,
+                                         2 /* text_offset */,
+                                         ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(inline_box1_text_position->IsTextPosition());
+
+  test_position = inline_box1_text_position->CreateNextLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *line_break_position);
+}
+
+TEST_F(AXPositionTest, CreatePreviousLeafTreePosition) {
+  TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, inline_box2_.id, AXNodePosition::BEFORE_TEXT);
+  ASSERT_TRUE(inline_box2_position->IsTreePosition());
+
+  TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, line_break_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, inline_box1_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, check_box_.id, AXNodePosition::BEFORE_TEXT);
+  TestPositionType button_position = AXNodePosition::CreateTreePosition(
+      tree_.data().tree_id, button_.id, AXNodePosition::BEFORE_TEXT);
+
+  TestPositionType test_position =
+      inline_box2_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *line_break_position);
+
+  test_position = test_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *inline_box1_position);
+
+  test_position = test_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *checkbox_position);
+
+  test_position = test_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *button_position);
+
+  test_position = test_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsNullPosition());
+
+  TestPositionType inline_box2_text_position =
+      AXNodePosition::CreateTextPosition(tree_.data().tree_id, inline_box2_.id,
+                                         2 /* text_offset */,
+                                         ax::mojom::TextAffinity::kDownstream);
+  EXPECT_TRUE(inline_box2_text_position->IsTextPosition());
+
+  test_position = inline_box2_text_position->CreatePreviousLeafTreePosition();
+  EXPECT_TRUE(test_position->IsTreePosition());
+  EXPECT_EQ(*test_position, *line_break_position);
+}
+
 TEST_F(AXPositionTest, AsPositionBeforeAndAfterCharacterWithNullPosition) {
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index c5b2b9e..c048aac 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -91,6 +91,9 @@
   using AXPositionInstance =
       std::unique_ptr<AXPosition<AXPositionType, AXNodeType>>;
 
+  using BoundaryConditionPredicate =
+      base::RepeatingCallback<bool(const AXPositionInstance&)>;
+
   static const int32_t INVALID_ANCHOR_ID = -1;
   static const int BEFORE_TEXT = -1;
   static const int INVALID_INDEX = -2;
@@ -415,6 +418,59 @@
     return false;
   }
 
+  bool AtStartOfPage() const {
+    AXPositionInstance text_position = AsLeafTextPosition();
+    switch (text_position->kind_) {
+      case AXPositionKind::NULL_POSITION:
+        return false;
+      case AXPositionKind::TREE_POSITION:
+        NOTREACHED();
+        return false;
+      case AXPositionKind::TEXT_POSITION: {
+        if (!text_position->AtStartOfAnchor())
+          return false;
+
+        // Search for the previous text position within the current page,
+        // using the page boundary abort predicate.
+        // If a valid position was found, then this position cannot be
+        // the start of a page.
+        // This will return a null position when an anchor movement would
+        // cross a page boundary, or the start of document was reached.
+        auto previous_text_position =
+            text_position->CreatePreviousTextAnchorPosition(
+                base::BindRepeating(&AbortMoveAtPageBoundary));
+        return previous_text_position->IsNullPosition();
+      }
+    }
+    return false;
+  }
+
+  bool AtEndOfPage() const {
+    AXPositionInstance text_position = AsLeafTextPosition();
+    switch (text_position->kind_) {
+      case AXPositionKind::NULL_POSITION:
+        return false;
+      case AXPositionKind::TREE_POSITION:
+        NOTREACHED();
+        return false;
+      case AXPositionKind::TEXT_POSITION: {
+        if (!text_position->AtEndOfAnchor())
+          return false;
+
+        // Search for the next text position within the current page,
+        // using the page boundary abort predicate.
+        // If a valid position was found, then this position cannot be
+        // the end of a page.
+        // This will return a null position when an anchor movement would
+        // cross a page boundary, or the end of document was reached.
+        auto next_text_position = text_position->CreateNextTextAnchorPosition(
+            base::BindRepeating(&AbortMoveAtPageBoundary));
+        return next_text_position->IsNullPosition();
+      }
+    }
+    return false;
+  }
+
   bool AtStartOfDocument() const {
     if (IsNullPosition())
       return false;
@@ -711,7 +767,7 @@
   }
 
   AXPositionInstance CreatePositionAtStartOfDocument() const {
-    if (kind_ == AXPositionKind::NULL_POSITION)
+    if (IsNullPosition())
       return CreateNullPosition();
 
     AXPositionInstance iterator = Clone();
@@ -726,7 +782,7 @@
   }
 
   AXPositionInstance CreatePositionAtEndOfDocument() const {
-    if (kind_ == AXPositionKind::NULL_POSITION)
+    if (IsNullPosition())
       return CreateNullPosition();
 
     AXPositionInstance iterator = Clone();
@@ -846,14 +902,40 @@
     return CreateNullPosition();
   }
 
-  // Creates a position using the next text-only node as its anchor.
+  // Creates a tree position using the next text-only node as its anchor.
+  // Assumes that text-only nodes are leaf nodes.
+  AXPositionInstance CreateNextLeafTreePosition() const {
+    AXPositionInstance next_leaf = AsTreePosition()->CreateNextAnchorPosition();
+    while (!next_leaf->IsNullPosition() && next_leaf->AnchorChildCount()) {
+      next_leaf = next_leaf->CreateNextAnchorPosition();
+    }
+
+    DCHECK(next_leaf);
+    return next_leaf;
+  }
+
+  // Creates a tree position using the previous text-only node as its anchor.
+  // Assumes that text-only nodes are leaf nodes.
+  AXPositionInstance CreatePreviousLeafTreePosition() const {
+    AXPositionInstance previous_leaf =
+        AsTreePosition()->CreatePreviousAnchorPosition();
+    while (!previous_leaf->IsNullPosition() &&
+           previous_leaf->AnchorChildCount()) {
+      previous_leaf = previous_leaf->CreatePreviousAnchorPosition();
+    }
+
+    DCHECK(previous_leaf);
+    return previous_leaf;
+  }
+
+  // Creates a text position using the next text-only node as its anchor.
   // Assumes that text-only nodes are leaf nodes.
   AXPositionInstance CreateNextTextAnchorPosition() const {
     return CreateNextTextAnchorPosition(
         base::BindRepeating(&DefaultAbortMovePredicate));
   }
 
-  // Creates a position using the previous text-only node as its anchor.
+  // Creates a text position using the previous text-only node as its anchor.
   // Assumes that text-only nodes are leaf nodes.
   AXPositionInstance CreatePreviousTextAnchorPosition() const {
     return CreatePreviousTextAnchorPosition(
@@ -1420,12 +1502,70 @@
 
   AXPositionInstance CreateNextParagraphStartPosition(
       AXBoundaryBehavior boundary_behavior) const {
+    return CreateNextBoundaryStartPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfParagraphPredicate),
+        base::BindRepeating(&AtEndOfParagraphPredicate));
+  }
+
+  AXPositionInstance CreatePreviousParagraphStartPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreatePreviousBoundaryStartPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfParagraphPredicate),
+        base::BindRepeating(&AtEndOfParagraphPredicate));
+  }
+
+  AXPositionInstance CreateNextParagraphEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateNextBoundaryEndPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfParagraphPredicate),
+        base::BindRepeating(&AtEndOfParagraphPredicate));
+  }
+
+  AXPositionInstance CreatePreviousParagraphEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreatePreviousBoundaryEndPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfParagraphPredicate),
+        base::BindRepeating(&AtEndOfParagraphPredicate));
+  }
+
+  AXPositionInstance CreateNextPageStartPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateNextBoundaryStartPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfPagePredicate),
+        base::BindRepeating(&AtEndOfPagePredicate));
+  }
+
+  AXPositionInstance CreatePreviousPageStartPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreatePreviousBoundaryStartPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfPagePredicate),
+        base::BindRepeating(&AtEndOfPagePredicate));
+  }
+
+  AXPositionInstance CreateNextPageEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateNextBoundaryEndPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfPagePredicate),
+        base::BindRepeating(&AtEndOfPagePredicate));
+  }
+
+  AXPositionInstance CreatePreviousPageEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreatePreviousBoundaryEndPosition(
+        boundary_behavior, base::BindRepeating(&AtStartOfPagePredicate),
+        base::BindRepeating(&AtEndOfPagePredicate));
+  }
+
+  AXPositionInstance CreateNextBoundaryStartPosition(
+      AXBoundaryBehavior boundary_behavior,
+      BoundaryConditionPredicate at_start_condition,
+      BoundaryConditionPredicate at_end_condition) const {
     bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
     if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        text_position->AtStartOfParagraph()) {
+        at_start_condition.Run(text_position)) {
       AXPositionInstance clone = Clone();
       clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
       return clone;
@@ -1439,17 +1579,17 @@
         return text_position;
       }
 
-      // Continue searching for the next paragraph start until the next logical
+      // Continue searching for the next boundary start until the next logical
       // text position is reached.
     } while (
-        !text_position->AtStartOfParagraph() ||
+        !at_start_condition.Run(text_position) ||
         (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
          *this == *text_position));
 
-    // If the paragraph boundary is in the same subtree, return a position
-    // rooted at the current position.
-    // This is necessary because we don't want to return any position that might
-    // be in the shadow DOM if the original position was not.
+    // If the boundary is in the same subtree, return a position rooted at the
+    // current position. This is necessary because we don't want to return any
+    // position that might be in the shadow DOM if the original position was
+    // not.
     AXPositionInstance common_ancestor =
         text_position->LowestCommonAncestor(*this);
     if (GetAnchor() == common_ancestor->GetAnchor()) {
@@ -1463,12 +1603,14 @@
     return text_position;
   }
 
-  AXPositionInstance CreatePreviousParagraphStartPosition(
-      AXBoundaryBehavior boundary_behavior) const {
+  AXPositionInstance CreatePreviousBoundaryStartPosition(
+      AXBoundaryBehavior boundary_behavior,
+      BoundaryConditionPredicate at_start_condition,
+      BoundaryConditionPredicate at_end_condition) const {
     bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        text_position->AtStartOfParagraph()) {
+        at_start_condition.Run(text_position)) {
       AXPositionInstance clone = Clone();
       clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
       return clone;
@@ -1487,17 +1629,17 @@
         return text_position;
       }
 
-      // Continue searching for the previous paragraph start until the next
+      // Continue searching for the previous page start until the next
       // logical text position is reached.
     } while (
-        !text_position->AtStartOfParagraph() ||
+        !at_start_condition.Run(text_position) ||
         (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
          *this == *text_position));
 
-    // If the paragraph boundary is in the same subtree, return a position
-    // rooted at the current position.
-    // This is necessary because we don't want to return any position that might
-    // be in the shadow DOM if the original position was not.
+    // If the boundary is in the same subtree, return a position rooted at the
+    // current position. This is necessary because we don't want to return any
+    // position that might be in the shadow DOM if the original position was
+    // not.
     AXPositionInstance common_ancestor =
         text_position->LowestCommonAncestor(*this);
     if (GetAnchor() == common_ancestor->GetAnchor()) {
@@ -1511,19 +1653,21 @@
     return text_position;
   }
 
-  AXPositionInstance CreateNextParagraphEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
+  AXPositionInstance CreateNextBoundaryEndPosition(
+      AXBoundaryBehavior boundary_behavior,
+      BoundaryConditionPredicate at_start_condition,
+      BoundaryConditionPredicate at_end_condition) const {
     bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        text_position->AtEndOfParagraph()) {
+        at_end_condition.Run(text_position)) {
       AXPositionInstance clone = Clone();
       // If there is no ambiguity as to whether the position is at the end of
-      // the current paragraph or the start of the next paragraph, affinity
+      // the current boundary or the start of the next boundary, affinity
       // should be reset in order to get consistent output from this function
       // regardless of input affinity.
       clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
-      if (clone->AtStartOfParagraph())
+      if (at_start_condition.Run(clone))
         clone->affinity_ = ax::mojom::TextAffinity::kUpstream;
       return clone;
     }
@@ -1542,17 +1686,17 @@
         return text_position;
       }
 
-      // Continue searching for the next paragraph end until the next logical
+      // Continue searching for the next boundary end until the next logical
       // text position is reached.
     } while (
-        !text_position->AtEndOfParagraph() ||
+        !at_end_condition.Run(text_position) ||
         (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
          *this == *text_position));
 
-    // If the paragraph boundary is in the same subtree, return a position
-    // rooted at the current position.
-    // This is necessary because we don't want to return any position that might
-    // be in the shadow DOM if the original position was not.
+    // If the boundary is in the same subtree, return a position rooted at the
+    // current position. This is necessary because we don't want to return any
+    // position that might be in the shadow DOM if the original position was
+    // not.
     AXPositionInstance common_ancestor =
         text_position->LowestCommonAncestor(*this);
     if (GetAnchor() == common_ancestor->GetAnchor()) {
@@ -1566,21 +1710,23 @@
     return text_position;
   }
 
-  AXPositionInstance CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
+  AXPositionInstance CreatePreviousBoundaryEndPosition(
+      AXBoundaryBehavior boundary_behavior,
+      BoundaryConditionPredicate at_start_condition,
+      BoundaryConditionPredicate at_end_condition) const {
     bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
     if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        text_position->AtEndOfParagraph()) {
+        at_end_condition.Run(text_position)) {
       AXPositionInstance clone = Clone();
       // If there is no ambiguity as to whether the position is at the end of
-      // the current line or the start of the next line, affinity should be
-      // reset in order to get consistent output from this function regardless
-      // of input affinity.
+      // the current boundary or the start of the next boundary, affinity
+      // should be reset in order to get consistent output from this function
+      // regardless of input affinity.
       clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
-      if (clone->AtStartOfParagraph())
+      if (at_start_condition.Run(clone))
         clone->affinity_ = ax::mojom::TextAffinity::kUpstream;
       return clone;
     }
@@ -1594,17 +1740,17 @@
         return text_position;
       }
 
-      // Continue searching for the previous paragraph end until the next
+      // Continue searching for the previous boundary end until the next
       // logical text position is reached.
     } while (
-        !text_position->AtEndOfParagraph() ||
+        !at_end_condition.Run(text_position) ||
         (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
          *this == *text_position));
 
-    // If the paragraph boundary is in the same subtree, return a position
-    // rooted at the current position.
-    // This is necessary because we don't want to return any position that might
-    // be in the shadow DOM if the original position was not.
+    // If the boundary is in the same subtree, return a position rooted at the
+    // current position. This is necessary because we don't want to return any
+    // position that might be in the shadow DOM if the original position was
+    // not.
     AXPositionInstance common_ancestor =
         text_position->LowestCommonAncestor(*this);
     if (GetAnchor() == common_ancestor->GetAnchor()) {
@@ -2025,6 +2171,31 @@
     return previous_leaf->AsLeafTextPosition();
   }
 
+  // Static helpers for lambda usage
+  static bool AtStartOfParagraphPredicate(const AXPositionInstance& position) {
+    return position->AtStartOfParagraph();
+  }
+
+  static bool AtEndOfParagraphPredicate(const AXPositionInstance& position) {
+    return position->AtEndOfParagraph();
+  }
+
+  static bool AtStartOfPagePredicate(const AXPositionInstance& position) {
+    return position->AtStartOfPage();
+  }
+
+  static bool AtEndOfPagePredicate(const AXPositionInstance& position) {
+    return position->AtEndOfPage();
+  }
+
+  static bool AtStartOfLinePredicate(const AXPositionInstance& position) {
+    return position->AtStartOfLine();
+  }
+
+  static bool AtEndOfLinePredicate(const AXPositionInstance& position) {
+    return position->AtEndOfLine();
+  }
+
   // Default behavior is to never abort
   static bool DefaultAbortMovePredicate(
       const AXPosition<AXPositionType, AXNodeType>& move_from,
@@ -2094,6 +2265,40 @@
     return false;
   }
 
+  // AbortMovePredicate function used to detect page boundaries.
+  static bool AbortMoveAtPageBoundary(
+      const AXPosition<AXPositionType, AXNodeType>& move_from,
+      const AXPosition<AXPositionType, AXNodeType>& move_to,
+      const AXMoveType move_type,
+      const AXMoveDirection direction) {
+    if (move_from.IsNullPosition() || move_to.IsNullPosition())
+      return true;
+
+    const bool move_from_break = move_from.GetAnchor()->data().GetBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject);
+    const bool move_to_break = move_to.GetAnchor()->data().GetBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject);
+
+    switch (move_type) {
+      case AXMoveType::kAncestor:
+        // For Ancestor moves, only abort when exiting a page break.
+        // We don't care if the ancestor is a page break or not, since the
+        // descendant is contained by it.
+        return move_from_break;
+      case AXMoveType::kDescendant:
+        // For Descendant moves, only abort when entering a page break
+        // descendant. We don't care if the ancestor is a page break  or not,
+        // since the descendant is contained by it.
+        return move_to_break;
+      case AXMoveType::kSibling:
+        // For Sibling moves, abort if at both of the siblings are a page
+        // break, because that would mean exiting and/or entering a page break.
+        return move_from_break && move_to_break;
+    }
+
+    return false;
+  }
+
   AXPositionKind kind_;
   AXTreeID tree_id_;
   int32_t anchor_id_;
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h
index dc53794..62e4880 100644
--- a/ui/accessibility/ax_range.h
+++ b/ui/accessibility/ax_range.h
@@ -182,8 +182,7 @@
       if (current_start_->GetAnchor() == iterator_end_->GetAnchor()) {
         current_start_ = AXPositionType::CreateNullPosition();
       } else {
-        current_start_ =
-            current_start_->CreateNextAnchorPosition()->AsLeafTextPosition();
+        current_start_ = current_start_->CreateNextLeafTreePosition();
         DCHECK_LE(*current_start_, *iterator_end_);
       }
       return *this;
@@ -197,8 +196,8 @@
               : iterator_end_->Clone();
       DCHECK_LE(*current_end, *iterator_end_);
 
-      AXRange current_leaf_text_range(current_start_->Clone(),
-                                      std::move(current_end));
+      AXRange current_leaf_text_range(current_start_->AsTextPosition(),
+                                      current_end->AsTextPosition());
       DCHECK(current_leaf_text_range.IsLeafTextRange());
       return std::move(current_leaf_text_range);
     }
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index c0075b1b..f8e8893 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -138,6 +138,7 @@
 
 bool IsDocument(const ax::mojom::Role role) {
   switch (role) {
+    case ax::mojom::Role::kDocument:
     case ax::mojom::Role::kRootWebArea:
     case ax::mojom::Role::kWebArea:
       return true;
@@ -168,6 +169,13 @@
   }
 }
 
+bool IsIgnored(const AXNodeData& data) {
+  if (data.HasState(ax::mojom::State::kIgnored) ||
+      data.role == ax::mojom::Role::kIgnored)
+    return true;
+  return false;
+}
+
 bool IsImage(const ax::mojom::Role role) {
   switch (role) {
     case ax::mojom::Role::kCanvas:
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h
index b5c3ae9..d017543 100644
--- a/ui/accessibility/ax_role_properties.h
+++ b/ui/accessibility/ax_role_properties.h
@@ -43,6 +43,9 @@
 // Returns true if the provided role belongs to a heading or a table header.
 AX_EXPORT bool IsHeadingOrTableHeader(const ax::mojom::Role role);
 
+// Returns true if the given AXNodeData has ignored state or ignored role.
+AX_EXPORT bool IsIgnored(const AXNodeData& data);
+
 // Returns true if the provided role belongs to an image, graphic, canvas, etc.
 AX_EXPORT bool IsImage(const ax::mojom::Role role);
 
diff --git a/ui/accessibility/ax_serializable_tree.cc b/ui/accessibility/ax_serializable_tree.cc
index f2473aa..4ad26d7 100644
--- a/ui/accessibility/ax_serializable_tree.cc
+++ b/ui/accessibility/ax_serializable_tree.cc
@@ -45,7 +45,7 @@
   }
 
   bool IsIgnored(const AXNode* node) const override {
-    return node->data().HasState(ax::mojom::State::kIgnored);
+    return node->IsIgnored();
   }
 
   bool IsValid(const AXNode* node) const override { return node != nullptr; }
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index dc8b2d3..f1a6485 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -26,7 +26,7 @@
 // in-between a table row and its cells.
 void FindCellsInRow(AXNode* node, std::vector<AXNode*>* cell_nodes) {
   for (AXNode* child : node->children()) {
-    if (child->data().HasState(ax::mojom::State::kIgnored) ||
+    if (child->IsIgnored() ||
         child->data().role == ax::mojom::Role::kGenericContainer)
       FindCellsInRow(child, cell_nodes);
     else if (IsCellOrTableHeader(child->data().role))
@@ -47,7 +47,7 @@
                           std::vector<std::vector<AXNode*>>* cell_nodes_per_row,
                           int32_t& caption_node_id) {
   for (AXNode* child : node->children()) {
-    if (child->data().HasState(ax::mojom::State::kIgnored) ||
+    if (child->IsIgnored() ||
         child->data().role == ax::mojom::Role::kGenericContainer ||
         child->data().role == ax::mojom::Role::kGroup) {
       FindRowsAndThenCells(child, row_nodes, cell_nodes_per_row,
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 5352b9d0..ac5e3a40 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -1280,6 +1280,10 @@
     node->SetData(src);
   }
 
+  // If we come across a page breaking object, mark the tree as a paginated root
+  if (src.GetBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject))
+    has_pagination_support_ = true;
+
   update_state->node_data_changed_ids.insert(node->id());
 
   // First, delete nodes that used to be children of this node but aren't
@@ -1724,7 +1728,7 @@
                                      std::vector<const AXNode*>& items,
                                      const AXNode& original_node) const {
   // Ignored nodes are not a part of ordered sets.
-  if (original_node.data().HasState(ax::mojom::State::kIgnored))
+  if (original_node.IsIgnored())
     return;
 
   // Stop searching current path if roles of local_parent and ordered set match.
@@ -1803,8 +1807,8 @@
       items.push_back(child);
 
     // Recurse if there is a generic container, ignored, or unknown.
-    if (child->data().role == ax::mojom::Role::kGenericContainer ||
-        child->data().role == ax::mojom::Role::kIgnored ||
+    if (child->IsIgnored() ||
+        child->data().role == ax::mojom::Role::kGenericContainer ||
         child->data().role == ax::mojom::Role::kUnknown) {
       PopulateOrderedSetItems(ordered_set, child, items, original_node);
     }
@@ -1960,4 +1964,8 @@
   tree_update_in_progress_ = set_tree_update_value;
 }
 
+bool AXTree::HasPaginationSupport() const {
+  return has_pagination_support_;
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index b2dfc3c..d20a32df 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -150,6 +150,10 @@
   bool GetTreeUpdateInProgressState() const override;
   void SetTreeUpdateInProgressState(bool set_tree_update_value);
 
+  // AXNode::OwnerTree override.
+  // Returns true if the tree represents a paginated document
+  bool HasPaginationSupport() const override;
+
   // Language detection manager, entry point to language detection features.
   // TODO(chrishall): Should this be stored by pointer or value?
   //                  When should we initialize this?
@@ -327,6 +331,9 @@
 
   // Indicates if the tree is updating.
   bool tree_update_in_progress_ = false;
+
+  // Indicates if the tree represents a paginated document
+  bool has_pagination_support_ = false;
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree_serializer_unittest.cc b/ui/accessibility/ax_tree_serializer_unittest.cc
index d305964..814accfd 100644
--- a/ui/accessibility/ax_tree_serializer_unittest.cc
+++ b/ui/accessibility/ax_tree_serializer_unittest.cc
@@ -263,7 +263,7 @@
     return node->parent();
   }
   bool IsIgnored(const AXNode* node) const override {
-    return node->data().HasState(ax::mojom::State::kIgnored);
+    return node->IsIgnored();
   }
   bool IsValid(const AXNode* node) const override {
     return node != nullptr && node->id() != invalid_id_;
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index c9967e7..e7f39053 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -3237,7 +3237,7 @@
 void AXPlatformNodeAuraLinux::OnSubtreeCreated() {
   // We might not have a parent, in that case we don't need to send the event.
   // We also don't want to notify if this is an ignored node
-  if (!GetParent() || GetData().HasState(ax::mojom::State::kIgnored))
+  if (!GetParent() || ui::IsIgnored(GetData()))
     return;
   g_signal_emit_by_name(GetParent(), "children-changed::add",
                         GetIndexInParent(), GetOrCreateAtkObject());
@@ -3246,7 +3246,7 @@
 void AXPlatformNodeAuraLinux::OnSubtreeWillBeDeleted() {
   // There is a chance there won't be a parent as we're in the deletion process.
   // We also don't want to notify if this is an ignored node
-  if (!GetParent() || GetData().HasState(ax::mojom::State::kIgnored))
+  if (!GetParent() || ui::IsIgnored(GetData()))
     return;
 
   g_signal_emit_by_name(GetParent(), "children-changed::remove",
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 16c0257..fe081ae 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -725,9 +725,7 @@
 
 bool AXPlatformNodeBase::IsInvisibleOrIgnored() const {
   const AXNodeData& data = GetData();
-  return data.HasState(ax::mojom::State::kInvisible) ||
-         data.HasState(ax::mojom::State::kIgnored) ||
-         data.role == ax::mojom::Role::kIgnored;
+  return data.HasState(ax::mojom::State::kInvisible) || ui::IsIgnored(data);
 }
 
 bool AXPlatformNodeBase::IsScrollable() const {
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 63c1d43..312fe58 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -178,10 +178,19 @@
       end_ = start_->CreateNextParagraphEndPosition(
           AXBoundaryBehavior::StopIfAlreadyAtBoundary);
       break;
-
-    // Since web content is not paginated, TextUnit_Page is not supported.
-    // Substituting it by the next larger unit: TextUnit_Document.
-    case TextUnit_Page:
+    case TextUnit_Page: {
+      // If the document doesn't support pagination, default to document units
+      // per UIA spec
+      AXPositionInstance common_ancestor = start_->LowestCommonAncestor(*end_);
+      if (common_ancestor->GetAnchor()->tree()->HasPaginationSupport()) {
+        start_ = start_->CreatePreviousPageStartPosition(
+            ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+        end_ = start_->CreateNextPageEndPosition(
+            ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+        break;
+      }
+    }
+      FALLTHROUGH;
     case TextUnit_Document:
       start_ = start_->CreatePositionAtStartOfDocument()->AsLeafTextPosition();
       end_ = start_->CreatePositionAtEndOfDocument();
@@ -586,9 +595,10 @@
       new_position = MoveEndpointByParagraph(
           position_to_move, is_start_endpoint, count, units_moved);
       break;
-    // Since web content is not paginated, TextUnit_Page is not supported.
-    // Substituting it by the next larger unit: TextUnit_Document.
     case TextUnit_Page:
+      new_position = MoveEndpointByPage(position_to_move, is_start_endpoint,
+                                        count, units_moved);
+      break;
     case TextUnit_Document:
       new_position =
           MoveEndpointByDocument(position_to_move, count, units_moved);
@@ -930,6 +940,53 @@
 }
 
 AXPlatformNodeTextRangeProviderWin::AXPositionInstance
+AXPlatformNodeTextRangeProviderWin::MoveEndpointByPage(
+    const AXNodePosition::AXPositionInstance& endpoint,
+    const bool is_start_endpoint,
+    const int count,
+    int* count_moved) {
+  // If the document doesn't support pagination, default to document navigation
+  // per UIA spec
+  AXPositionInstance common_ancestor = start_->LowestCommonAncestor(*end_);
+  if (!common_ancestor->GetAnchor()->tree()->HasPaginationSupport())
+    return MoveEndpointByDocument(std::move(endpoint), count, count_moved);
+
+  auto current_endpoint = endpoint->Clone();
+  const bool forwards = count > 0;
+  const int count_abs = std::abs(count);
+  const auto behavior = ui::AXBoundaryBehavior::CrossBoundary;
+  int iteration = 0;
+  for (iteration = 0; iteration < count_abs; ++iteration) {
+    AXPositionInstance next_endpoint;
+    if (forwards) {
+      next_endpoint =
+          is_start_endpoint
+              ? current_endpoint->CreateNextPageStartPosition(behavior)
+              : current_endpoint->CreateNextPageEndPosition(behavior);
+    } else {
+      next_endpoint =
+          is_start_endpoint
+              ? current_endpoint->CreatePreviousPageStartPosition(behavior)
+              : current_endpoint->CreatePreviousPageEndPosition(behavior);
+    }
+    // End of document
+    if (next_endpoint->IsNullPosition()) {
+      int document_moved;
+      next_endpoint = MoveEndpointByDocument(endpoint, count, &document_moved);
+      if (*endpoint != *next_endpoint && !next_endpoint->IsNullPosition()) {
+        ++iteration;
+        current_endpoint = std::move(next_endpoint);
+      }
+      break;
+    }
+    current_endpoint = std::move(next_endpoint);
+  }
+
+  *count_moved = (forwards) ? iteration : -iteration;
+  return current_endpoint;
+}
+
+AXPlatformNodeTextRangeProviderWin::AXPositionInstance
 AXPlatformNodeTextRangeProviderWin::MoveEndpointByDocument(
     const AXPositionInstance& endpoint,
     const int count,
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 3e8c4d3..f39cc51d 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -115,6 +115,10 @@
                                              const bool is_start_endpoint,
                                              const int count,
                                              int* count_moved);
+  AXPositionInstance MoveEndpointByPage(const AXPositionInstance& endpoint,
+                                        const bool is_start_endpoint,
+                                        const int count,
+                                        int* count_moved);
   AXPositionInstance MoveEndpointByFormat(const AXPositionInstance& endpoint,
                                           const int count,
                                           int* units_moved);
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index b95ad50..250f0ce 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -669,6 +669,67 @@
 
     return update;
   }
+
+  ui::AXTreeUpdate BuildAXTreeForMoveByPage() {
+    ui::AXNodeData root_data;
+    root_data.id = 1;
+    root_data.role = ax::mojom::Role::kDocument;
+
+    ui::AXNodeData page_1_data;
+    page_1_data.id = 2;
+    page_1_data.role = ax::mojom::Role::kRegion;
+    page_1_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    ui::AXNodeData page_1_text_data;
+    page_1_text_data.id = 3;
+    page_1_text_data.role = ax::mojom::Role::kStaticText;
+    page_1_text_data.SetName("some text on page 1");
+    page_1_text_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
+    page_1_data.child_ids = {3};
+
+    ui::AXNodeData page_2_data;
+    page_2_data.id = 4;
+    page_2_data.role = ax::mojom::Role::kRegion;
+    page_2_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    ui::AXNodeData page_2_text_data;
+    page_2_text_data.id = 5;
+    page_2_text_data.role = ax::mojom::Role::kStaticText;
+    page_2_text_data.SetName("some text on page 2");
+    page_2_text_data.AddIntAttribute(
+        ax::mojom::IntAttribute::kTextStyle,
+        static_cast<int32_t>(ax::mojom::TextStyle::kBold));
+    page_2_data.child_ids = {5};
+
+    ui::AXNodeData page_3_data;
+    page_3_data.id = 6;
+    page_3_data.role = ax::mojom::Role::kRegion;
+    page_3_data.AddBoolAttribute(
+        ax::mojom::BoolAttribute::kIsPageBreakingObject, true);
+
+    ui::AXNodeData page_3_text_data;
+    page_3_text_data.id = 7;
+    page_3_text_data.role = ax::mojom::Role::kStaticText;
+    page_3_text_data.SetName("some more text on page 3");
+    page_3_data.child_ids = {7};
+
+    root_data.child_ids = {2, 4, 6};
+
+    ui::AXTreeUpdate update;
+    ui::AXTreeData tree_data;
+    tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+    update.tree_data = tree_data;
+    update.has_tree_data = true;
+    update.root_id = root_data.id;
+    update.nodes = {root_data,       page_1_data,      page_1_text_data,
+                    page_2_data,     page_2_text_data, page_3_data,
+                    page_3_text_data};
+
+    return update;
+  }
 };
 
 class MockAXPlatformNodeTextRangeProviderWin
@@ -1560,6 +1621,85 @@
   AXNodePosition::SetTreeForTesting(nullptr);
 }
 
+TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderMovePage) {
+  Init(BuildAXTreeForMoveByPage());
+  AXNode* root_node = GetRootNode();
+  AXNodePosition::SetTreeForTesting(tree_.get());
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+  GetTextRangeProviderFromTextNode(text_range_provider, root_node);
+
+  // Moving by 0 should have no effect.
+  EXPECT_UIA_MOVE(
+      text_range_provider, TextUnit_Page,
+      /*count*/ 0,
+      /*expected_text*/
+      L"some text on page 1\nsome text on page 2some more text on page 3",
+      /*expected_count*/ 0);
+
+  // Backwards endpoint moves
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Page,
+      /*count*/ -1,
+      /*expected_text*/ L"some text on page 1\nsome text on page 2",
+      /*expected_count*/ -1);
+
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
+                                   TextPatternRangeEndpoint_End, TextUnit_Page,
+                                   /*count*/ -5,
+                                   /*expected_text*/ L"",
+                                   /*expected_count*/ -2);
+
+  // Forwards endpoint move
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Page,
+      /*count*/ 5,
+      /*expected_text*/
+      L"some text on page 1\nsome text on page 2some more text on page 3",
+      /*expected_count*/ 4);
+
+  // Range moves
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
+                  /*count*/ 1,
+                  /*expected_text*/ L"some text on page 2",
+                  /*expected_count*/ 1);
+
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
+                  /*count*/ 1,
+                  /*expected_text*/ L"some more text on page 3",
+                  /*expected_count*/ 1);
+
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
+                  /*count*/ -1,
+                  /*expected_text*/ L"some text on page 2",
+                  /*expected_count*/ -1);
+
+  // ExpandToEnclosingUnit - first move by character so it's not on a
+  // page boundary before calling ExpandToEnclosingUnit
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Character,
+      /*count*/ -2,
+      /*expected_text*/ L"some text on page",
+      /*expected_count*/ -2);
+
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
+      text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Character,
+      /*count*/ 2,
+      /*expected_text*/ L"me text on page",
+      /*expected_count*/ 2);
+
+  ASSERT_HRESULT_SUCCEEDED(
+      text_range_provider->ExpandToEnclosingUnit(TextUnit_Page));
+
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
+                  /*count*/ 0,
+                  /*expected_text*/
+                  L"some text on page 2",
+                  /*expected_count*/ 0);
+
+  AXNodePosition::SetTreeForTesting(nullptr);
+}
+
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderMoveWord) {
   Init(BuildAXTreeForMove());
   AXNode* root_node = GetRootNode();
diff --git a/ui/base/clipboard/OWNERS b/ui/base/clipboard/OWNERS
index c1468a7..4a0f01a 100644
--- a/ui/base/clipboard/OWNERS
+++ b/ui/base/clipboard/OWNERS
@@ -1,4 +1,5 @@
 dcheng@chromium.org
+huangdarwin@chromium.org
 
 # TEAM: storage-dev@chromium.org
 # COMPONENT: Blink>DataTransfer
diff --git a/ui/events/blink/prediction/kalman_predictor.cc b/ui/events/blink/prediction/kalman_predictor.cc
index f525ec6..dd3ff766 100644
--- a/ui/events/blink/prediction/kalman_predictor.cc
+++ b/ui/events/blink/prediction/kalman_predictor.cc
@@ -22,15 +22,12 @@
 constexpr base::TimeDelta InputPredictor::kTimeInterval;
 constexpr base::TimeDelta InputPredictor::kMinimumTimeInterval;
 
-KalmanPredictor::KalmanPredictor(const bool enable_time_filtering)
-    : enable_time_filtering_(enable_time_filtering) {}
+KalmanPredictor::KalmanPredictor() = default;
 
 KalmanPredictor::~KalmanPredictor() = default;
 
 const char* KalmanPredictor::GetName() const {
-  return enable_time_filtering_
-             ? input_prediction::kScrollPredictorNameKalmanTimeFiltered
-             : input_prediction::kScrollPredictorNameKalman;
+  return input_prediction::kScrollPredictorNameKalman;
 }
 
 void KalmanPredictor::Reset() {
@@ -47,12 +44,11 @@
     dt = cur_input.time_stamp - last_point_.time_stamp;
     if (dt > kMaxTimeDelta)
       Reset();
-    else if (enable_time_filtering_)
+    else
       time_filter_.Update(dt.InMillisecondsF(), 0);
   }
 
-  double dt_ms = enable_time_filtering_ ? time_filter_.GetPosition()
-                                        : dt.InMillisecondsF();
+  double dt_ms = time_filter_.GetPosition();
   last_point_ = cur_input;
   x_predictor_.Update(cur_input.pos.x(), dt_ms);
   y_predictor_.Update(cur_input.pos.y(), dt_ms);
diff --git a/ui/events/blink/prediction/kalman_predictor.h b/ui/events/blink/prediction/kalman_predictor.h
index 4ba1c8b..1acfe0f 100644
--- a/ui/events/blink/prediction/kalman_predictor.h
+++ b/ui/events/blink/prediction/kalman_predictor.h
@@ -19,7 +19,7 @@
 // be used to predict one dimension (x, y).
 class KalmanPredictor : public InputPredictor {
  public:
-  explicit KalmanPredictor(bool enable_time_filtering);
+  explicit KalmanPredictor();
   ~KalmanPredictor() override;
 
   const char* GetName() const override;
@@ -53,7 +53,6 @@
 
   // Filter to smooth time intervals.
   KalmanFilter time_filter_;
-  bool enable_time_filtering_;
 
   // The last input point.
   InputData last_point_;
diff --git a/ui/events/blink/prediction/kalman_predictor_unittest.cc b/ui/events/blink/prediction/kalman_predictor_unittest.cc
index edaeda86..98a51a1f 100644
--- a/ui/events/blink/prediction/kalman_predictor_unittest.cc
+++ b/ui/events/blink/prediction/kalman_predictor_unittest.cc
@@ -44,8 +44,7 @@
   explicit KalmanPredictorTest() {}
 
   void SetUp() override {
-    predictor_ = std::make_unique<ui::KalmanPredictor>(
-        false /* enable_time_filtering */);
+    predictor_ = std::make_unique<ui::KalmanPredictor>();
   }
 
   DISALLOW_COPY_AND_ASSIGN(KalmanPredictorTest);
@@ -90,50 +89,35 @@
   ValidatePredictor(x, y, t);
 }
 
-// Tests the kalman predictor constant position with time filtering.
-TEST_F(KalmanPredictorTest, PredictConstantValueTimeFiltered) {
-  predictor_ =
-      std::make_unique<ui::KalmanPredictor>(true /* enable_time_filtering */);
-  std::vector<double> x = {50, 50, 50, 50, 50, 50};
-  std::vector<double> y = {-50, -50, -50, -50, -50, -50};
-  std::vector<double> t = {8, 16, 24, 32, 40, 48};
-  ValidatePredictor(x, y, t);
-}
-
 // Tests the kalman predictor predict constant velocity.
 TEST_F(KalmanPredictorTest, PredictLinearValue) {
-  std::vector<double> x = {0, 8, 16, 20, 23, 27, 31, 38, 48, 60};
-  std::vector<double> y = {30, 38, 46, 50, 53, 57, 61, 68, 78, 90};
-  std::vector<double> t = {0, 8, 16, 20, 23, 27, 31, 38, 48, 60};
-  ValidatePredictor(x, y, t);
-}
+  // The kalman filter is initialized with a velocity of zero. The change of
+  // velocity from zero to the constant value will be attributed to
+  // acceleration. Given how the filter works, it will take a few updates for it
+  // to get accustomed to a constant velocity.
+  std::vector<double> x_stabilizer = {-40, -32, -24, -16, -8};
+  std::vector<double> y_stabilizer = {-10, -2, 6, 14, 22};
+  std::vector<double> t_stabilizer = {-40, -32, -24, -16, -8};
+  for (size_t i = 0; i < t_stabilizer.size(); i++) {
+    InputPredictor::InputData data = {
+        gfx::PointF(x_stabilizer[i], y_stabilizer[i]),
+        FromMilliseconds(t_stabilizer[i])};
+    predictor_->Update(data);
+  }
 
-// Tests the time filtering as the kalman predictor predicts constant velocity.
-// Position is changing on a fixed rate assuming a fixed hardware resample
-// frequency. Due to timestamp noise generated by the OS event delivery,
-// timestamps are inconsistent.
-TEST_F(KalmanPredictorTest, PredictLinearValueTimeFiltered) {
-  std::unique_ptr<ui::KalmanPredictor> filtered_predictor =
-      std::make_unique<ui::KalmanPredictor>(true /* enable_time_filtering */);
   std::vector<double> x = {0, 8, 16, 24, 32, 40, 48, 60};
   std::vector<double> y = {30, 38, 46, 54, 62, 70, 78, 90};
-  std::vector<double> t = {0, 8, 16, 23, 33, 39, 49, 60};
+  std::vector<double> t = {0, 8, 16, 24, 32, 40, 48, 60};
   for (size_t i = 0; i < t.size(); i++) {
-    if (filtered_predictor->HasPrediction() && predictor_->HasPrediction()) {
-      ui::InputPredictor::InputData filtered_result;
-      ui::InputPredictor::InputData unfiltered_result;
-      EXPECT_TRUE(filtered_predictor->GeneratePrediction(FromMilliseconds(t[i]),
-                                                         &filtered_result));
-      EXPECT_TRUE(predictor_->GeneratePrediction(FromMilliseconds(t[i]),
-                                                 &unfiltered_result));
-      EXPECT_LT(std::abs(filtered_result.pos.x() - x[i]),
-                std::abs(unfiltered_result.pos.x() - x[i]));
-      EXPECT_LT(std::abs(filtered_result.pos.y() - y[i]),
-                std::abs(unfiltered_result.pos.y() - y[i]));
+    if (predictor_->HasPrediction()) {
+      ui::InputPredictor::InputData result;
+      EXPECT_TRUE(
+          predictor_->GeneratePrediction(FromMilliseconds(t[i]), &result));
+      EXPECT_NEAR(result.pos.x(), x[i], kEpsilon);
+      EXPECT_NEAR(result.pos.y(), y[i], kEpsilon);
     }
     InputPredictor::InputData data = {gfx::PointF(x[i], y[i]),
                                       FromMilliseconds(t[i])};
-    filtered_predictor->Update(data);
     predictor_->Update(data);
   }
 }
@@ -146,20 +130,9 @@
   ValidatePredictor(x, y, t);
 }
 
-// Tests the kalman predictor predict constant acceleration with time filtering.
-TEST_F(KalmanPredictorTest, PredictQuadraticValueTimeFiltered) {
-  predictor_ =
-      std::make_unique<ui::KalmanPredictor>(true /* enable_time_filtering */);
-  std::vector<double> x = {0, 2, 8, 18, 32, 50, 72, 98};
-  std::vector<double> y = {10, 11, 14, 19, 26, 35, 46, 59};
-  std::vector<double> t = {8, 16, 24, 32, 40, 48, 56, 64};
-  ValidatePredictor(x, y, t);
-}
-
 // Tests the kalman predictor time interval filter.
 TEST_F(KalmanPredictorTest, TimeInterval) {
-  predictor_ =
-      std::make_unique<ui::KalmanPredictor>(true /* enable_time_filtering */);
+  predictor_ = std::make_unique<ui::KalmanPredictor>();
   EXPECT_EQ(predictor_->TimeInterval(), kExpectedDefaultTimeInterval);
   std::vector<double> x = {0, 2, 8, 18};
   std::vector<double> y = {10, 11, 14, 19};
diff --git a/ui/events/blink/prediction/predictor_factory.cc b/ui/events/blink/prediction/predictor_factory.cc
index b0f617c..f971870 100644
--- a/ui/events/blink/prediction/predictor_factory.cc
+++ b/ui/events/blink/prediction/predictor_factory.cc
@@ -15,7 +15,6 @@
 
 const char kScrollPredictorNameLsq[] = "lsq";
 const char kScrollPredictorNameKalman[] = "kalman";
-const char kScrollPredictorNameKalmanTimeFiltered[] = "kalman_time_filtered";
 const char kScrollPredictorNameLinearFirst[] = "linear_first";
 const char kScrollPredictorNameLinearSecond[] = "linear_second";
 const char kScrollPredictorNameEmpty[] = "empty";
@@ -32,9 +31,6 @@
     return PredictorType::kScrollPredictorTypeLsq;
   else if (predictor_name == input_prediction::kScrollPredictorNameKalman)
     return PredictorType::kScrollPredictorTypeKalman;
-  else if (predictor_name ==
-           input_prediction::kScrollPredictorNameKalmanTimeFiltered)
-    return PredictorType::kScrollPredictorTypeKalmanTimeFiltered;
   else if (predictor_name == input_prediction::kScrollPredictorNameLinearFirst)
     return PredictorType::kScrollPredictorTypeLinearFirst;
   else if (predictor_name == input_prediction::kScrollPredictorNameLinearSecond)
@@ -48,10 +44,7 @@
   if (predictor_type == PredictorType::kScrollPredictorTypeLsq)
     return std::make_unique<LeastSquaresPredictor>();
   else if (predictor_type == PredictorType::kScrollPredictorTypeKalman)
-    return std::make_unique<KalmanPredictor>(false /* enable_time_filtering */);
-  else if (predictor_type ==
-           PredictorType::kScrollPredictorTypeKalmanTimeFiltered)
-    return std::make_unique<KalmanPredictor>(true /* enable_time_filtering */);
+    return std::make_unique<KalmanPredictor>();
   else if (predictor_type == PredictorType::kScrollPredictorTypeLinearFirst)
     return std::make_unique<LinearPredictor>(
         LinearPredictor::EquationOrder::kFirstOrder);
diff --git a/ui/events/blink/prediction/predictor_factory.h b/ui/events/blink/prediction/predictor_factory.h
index 1d21781..7488cfa 100644
--- a/ui/events/blink/prediction/predictor_factory.h
+++ b/ui/events/blink/prediction/predictor_factory.h
@@ -13,7 +13,6 @@
 
 extern const char kScrollPredictorNameLsq[];
 extern const char kScrollPredictorNameKalman[];
-extern const char kScrollPredictorNameKalmanTimeFiltered[];
 extern const char kScrollPredictorNameLinearFirst[];
 extern const char kScrollPredictorNameLinearSecond[];
 extern const char kScrollPredictorNameEmpty[];
@@ -21,7 +20,6 @@
 enum class PredictorType {
   kScrollPredictorTypeLsq,
   kScrollPredictorTypeKalman,
-  kScrollPredictorTypeKalmanTimeFiltered,
   kScrollPredictorTypeLinearFirst,
   kScrollPredictorTypeLinearSecond,
   kScrollPredictorTypeEmpty
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 7c442a51..d15c1c1 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -247,6 +247,10 @@
   'versions': [{ 'name': 'glClientWaitSync',
                  'extensions': ['GL_ARB_sync'] }],
   'arguments': 'GLsync sync, GLbitfield flags, GLuint64 timeout', },
+{ 'return_type': 'GLenum',
+  'versions': [{ 'name': 'glClientWaitSyncAPPLE',
+                 'extensions': ['GL_APPLE_sync'] }],
+  'arguments': 'GLsync sync, GLbitfield flags, GLuint64 timeout', },
 { 'return_type': 'void',
   'names': ['glColorMask'],
   'arguments':
@@ -467,6 +471,10 @@
                  'extensions': ['GL_ARB_sync'] }],
   'arguments': 'GLsync sync', },
 { 'return_type': 'void',
+  'versions': [{ 'name': 'glDeleteSyncAPPLE',
+                 'extensions': ['GL_APPLE_sync'] }],
+  'arguments': 'GLsync sync', },
+{ 'return_type': 'void',
   'names': ['glDeleteTextures'],
   'arguments': 'GLsizei n, const GLuint* textures', },
 { 'return_type': 'void',
@@ -576,6 +584,10 @@
   'versions': [{ 'name': 'glFenceSync',
                  'extensions': ['GL_ARB_sync'] }],
   'arguments': 'GLenum condition, GLbitfield flags', },
+{ 'return_type': 'GLsync',
+  'versions': [{ 'name': 'glFenceSyncAPPLE',
+                 'extensions': ['GL_APPLE_sync'] }],
+  'arguments': 'GLenum condition, GLbitfield flags', },
 { 'return_type': 'void',
   'names': ['glFinish'],
   'arguments': 'void', },
@@ -1321,6 +1333,10 @@
                  'extensions': ['GL_ARB_sync'] }],
   'arguments': 'GLsync sync', },
 { 'return_type': 'GLboolean',
+  'versions': [{ 'name': 'glIsSyncAPPLE',
+                 'extensions': ['GL_APPLE_sync'] }],
+  'arguments': 'GLsync sync', },
+{ 'return_type': 'GLboolean',
   'names': ['glIsTexture'],
   'arguments': 'GLuint texture', },
 { 'return_type': 'GLboolean',
@@ -2203,6 +2219,11 @@
   'arguments':
     'GLsync sync, GLbitfield flags, GLuint64 timeout', },
 { 'return_type': 'void',
+  'versions': [{ 'name': 'glWaitSyncAPPLE',
+                 'extensions': ['GL_APPLE_sync'] }],
+  'arguments':
+    'GLsync sync, GLbitfield flags, GLuint64 timeout', },
+{ 'return_type': 'void',
   'names': ['glWindowRectanglesEXT'],
   'arguments': 'GLenum mode, GLsizei n, const GLint* box', },
 ]
@@ -3416,7 +3437,7 @@
                         'GLsizei': '0',
                         'GLfloat': '0.0f',
                         'GLdouble': '0.0',
-                        'GLsync': 'NULL',
+                        'GLsync': 'nullptr',
                         'GLDEBUGPROC': 'NULL'}
       if return_type.endswith('*'):
         file.write('  return NULL;\n')
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index 4209b0c7..a0cb30d7 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -130,6 +130,9 @@
 GLenum glClientWaitSyncFn(GLsync sync,
                           GLbitfield flags,
                           GLuint64 timeout) override;
+GLenum glClientWaitSyncAPPLEFn(GLsync sync,
+                               GLbitfield flags,
+                               GLuint64 timeout) override;
 void glColorMaskFn(GLboolean red,
                    GLboolean green,
                    GLboolean blue,
@@ -319,6 +322,7 @@
 void glDeleteSemaphoresEXTFn(GLsizei n, const GLuint* semaphores) override;
 void glDeleteShaderFn(GLuint shader) override;
 void glDeleteSyncFn(GLsync sync) override;
+void glDeleteSyncAPPLEFn(GLsync sync) override;
 void glDeleteTexturesFn(GLsizei n, const GLuint* textures) override;
 void glDeleteTransformFeedbacksFn(GLsizei n, const GLuint* ids) override;
 void glDeleteVertexArraysOESFn(GLsizei n, const GLuint* arrays) override;
@@ -371,6 +375,7 @@
 void glEndQueryFn(GLenum target) override;
 void glEndTransformFeedbackFn(void) override;
 GLsync glFenceSyncFn(GLenum condition, GLbitfield flags) override;
+GLsync glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) override;
 void glFinishFn(void) override;
 void glFinishFenceAPPLEFn(GLuint fence) override;
 void glFinishFenceNVFn(GLuint fence) override;
@@ -888,6 +893,7 @@
 GLboolean glIsSamplerFn(GLuint sampler) override;
 GLboolean glIsShaderFn(GLuint shader) override;
 GLboolean glIsSyncFn(GLsync sync) override;
+GLboolean glIsSyncAPPLEFn(GLsync sync) override;
 GLboolean glIsTextureFn(GLuint texture) override;
 GLboolean glIsTransformFeedbackFn(GLuint id) override;
 GLboolean glIsVertexArrayOESFn(GLuint array) override;
@@ -1547,4 +1553,7 @@
                           const GLuint* textures,
                           const GLenum* srcLayouts) override;
 void glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+void glWaitSyncAPPLEFn(GLsync sync,
+                       GLbitfield flags,
+                       GLuint64 timeout) override;
 void glWindowRectanglesEXTFn(GLenum mode, GLsizei n, const GLint* box) override;
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index 1383f590..7849e73 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -298,6 +298,7 @@
   ext.b_GL_ANGLE_translated_shader_source =
       gfx::HasExtension(extensions, "GL_ANGLE_translated_shader_source");
   ext.b_GL_APPLE_fence = gfx::HasExtension(extensions, "GL_APPLE_fence");
+  ext.b_GL_APPLE_sync = gfx::HasExtension(extensions, "GL_APPLE_sync");
   ext.b_GL_APPLE_vertex_array_object =
       gfx::HasExtension(extensions, "GL_APPLE_vertex_array_object");
   ext.b_GL_ARB_ES2_compatibility =
@@ -652,6 +653,11 @@
         GetGLProcAddress("glClientWaitSync"));
   }
 
+  if (ext.b_GL_APPLE_sync) {
+    fn.glClientWaitSyncAPPLEFn = reinterpret_cast<glClientWaitSyncAPPLEProc>(
+        GetGLProcAddress("glClientWaitSyncAPPLE"));
+  }
+
   if (ext.b_GL_ANGLE_robust_client_memory) {
     fn.glCompressedTexImage2DRobustANGLEFn =
         reinterpret_cast<glCompressedTexImage2DRobustANGLEProc>(
@@ -860,6 +866,11 @@
         reinterpret_cast<glDeleteSyncProc>(GetGLProcAddress("glDeleteSync"));
   }
 
+  if (ext.b_GL_APPLE_sync) {
+    fn.glDeleteSyncAPPLEFn = reinterpret_cast<glDeleteSyncAPPLEProc>(
+        GetGLProcAddress("glDeleteSyncAPPLE"));
+  }
+
   if (ver->IsAtLeastGLES(3u, 0u) || ver->IsAtLeastGL(4u, 0u) ||
       ext.b_GL_ARB_transform_feedback2) {
     fn.glDeleteTransformFeedbacksFn =
@@ -1001,6 +1012,11 @@
         reinterpret_cast<glFenceSyncProc>(GetGLProcAddress("glFenceSync"));
   }
 
+  if (ext.b_GL_APPLE_sync) {
+    fn.glFenceSyncAPPLEFn = reinterpret_cast<glFenceSyncAPPLEProc>(
+        GetGLProcAddress("glFenceSyncAPPLE"));
+  }
+
   if (ext.b_GL_APPLE_fence) {
     fn.glFinishFenceAPPLEFn = reinterpret_cast<glFinishFenceAPPLEProc>(
         GetGLProcAddress("glFinishFenceAPPLE"));
@@ -1855,6 +1871,11 @@
         reinterpret_cast<glIsSyncProc>(GetGLProcAddress("glIsSync"));
   }
 
+  if (ext.b_GL_APPLE_sync) {
+    fn.glIsSyncAPPLEFn =
+        reinterpret_cast<glIsSyncAPPLEProc>(GetGLProcAddress("glIsSyncAPPLE"));
+  }
+
   if (ver->IsAtLeastGLES(3u, 0u) || ver->IsAtLeastGL(4u, 0u) ||
       ext.b_GL_ARB_transform_feedback2) {
     fn.glIsTransformFeedbackFn = reinterpret_cast<glIsTransformFeedbackProc>(
@@ -2788,6 +2809,11 @@
         reinterpret_cast<glWaitSyncProc>(GetGLProcAddress("glWaitSync"));
   }
 
+  if (ext.b_GL_APPLE_sync) {
+    fn.glWaitSyncAPPLEFn = reinterpret_cast<glWaitSyncAPPLEProc>(
+        GetGLProcAddress("glWaitSyncAPPLE"));
+  }
+
   if (ext.b_GL_EXT_window_rectangles) {
     fn.glWindowRectanglesEXTFn = reinterpret_cast<glWindowRectanglesEXTProc>(
         GetGLProcAddress("glWindowRectanglesEXT"));
@@ -3055,6 +3081,12 @@
   return driver_->fn.glClientWaitSyncFn(sync, flags, timeout);
 }
 
+GLenum GLApiBase::glClientWaitSyncAPPLEFn(GLsync sync,
+                                          GLbitfield flags,
+                                          GLuint64 timeout) {
+  return driver_->fn.glClientWaitSyncAPPLEFn(sync, flags, timeout);
+}
+
 void GLApiBase::glColorMaskFn(GLboolean red,
                               GLboolean green,
                               GLboolean blue,
@@ -3398,6 +3430,10 @@
   driver_->fn.glDeleteSyncFn(sync);
 }
 
+void GLApiBase::glDeleteSyncAPPLEFn(GLsync sync) {
+  driver_->fn.glDeleteSyncAPPLEFn(sync);
+}
+
 void GLApiBase::glDeleteTexturesFn(GLsizei n, const GLuint* textures) {
   driver_->fn.glDeleteTexturesFn(n, textures);
 }
@@ -3538,6 +3574,10 @@
   return driver_->fn.glFenceSyncFn(condition, flags);
 }
 
+GLsync GLApiBase::glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) {
+  return driver_->fn.glFenceSyncAPPLEFn(condition, flags);
+}
+
 void GLApiBase::glFinishFn(void) {
   driver_->fn.glFinishFn();
 }
@@ -4622,6 +4662,10 @@
   return driver_->fn.glIsSyncFn(sync);
 }
 
+GLboolean GLApiBase::glIsSyncAPPLEFn(GLsync sync) {
+  return driver_->fn.glIsSyncAPPLEFn(sync);
+}
+
 GLboolean GLApiBase::glIsTextureFn(GLuint texture) {
   return driver_->fn.glIsTextureFn(texture);
 }
@@ -5970,6 +6014,12 @@
   driver_->fn.glWaitSyncFn(sync, flags, timeout);
 }
 
+void GLApiBase::glWaitSyncAPPLEFn(GLsync sync,
+                                  GLbitfield flags,
+                                  GLuint64 timeout) {
+  driver_->fn.glWaitSyncAPPLEFn(sync, flags, timeout);
+}
+
 void GLApiBase::glWindowRectanglesEXTFn(GLenum mode,
                                         GLsizei n,
                                         const GLint* box) {
@@ -6284,6 +6334,13 @@
   return gl_api_->glClientWaitSyncFn(sync, flags, timeout);
 }
 
+GLenum TraceGLApi::glClientWaitSyncAPPLEFn(GLsync sync,
+                                           GLbitfield flags,
+                                           GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glClientWaitSyncAPPLE")
+  return gl_api_->glClientWaitSyncAPPLEFn(sync, flags, timeout);
+}
+
 void TraceGLApi::glColorMaskFn(GLboolean red,
                                GLboolean green,
                                GLboolean blue,
@@ -6675,6 +6732,11 @@
   gl_api_->glDeleteSyncFn(sync);
 }
 
+void TraceGLApi::glDeleteSyncAPPLEFn(GLsync sync) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glDeleteSyncAPPLE")
+  gl_api_->glDeleteSyncAPPLEFn(sync);
+}
+
 void TraceGLApi::glDeleteTexturesFn(GLsizei n, const GLuint* textures) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glDeleteTextures")
   gl_api_->glDeleteTexturesFn(n, textures);
@@ -6847,6 +6909,11 @@
   return gl_api_->glFenceSyncFn(condition, flags);
 }
 
+GLsync TraceGLApi::glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glFenceSyncAPPLE")
+  return gl_api_->glFenceSyncAPPLEFn(condition, flags);
+}
+
 void TraceGLApi::glFinishFn(void) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glFinish")
   gl_api_->glFinishFn();
@@ -8129,6 +8196,11 @@
   return gl_api_->glIsSyncFn(sync);
 }
 
+GLboolean TraceGLApi::glIsSyncAPPLEFn(GLsync sync) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glIsSyncAPPLE")
+  return gl_api_->glIsSyncAPPLEFn(sync);
+}
+
 GLboolean TraceGLApi::glIsTextureFn(GLuint texture) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glIsTexture")
   return gl_api_->glIsTextureFn(texture);
@@ -9704,6 +9776,13 @@
   gl_api_->glWaitSyncFn(sync, flags, timeout);
 }
 
+void TraceGLApi::glWaitSyncAPPLEFn(GLsync sync,
+                                   GLbitfield flags,
+                                   GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glWaitSyncAPPLE")
+  gl_api_->glWaitSyncAPPLEFn(sync, flags, timeout);
+}
+
 void TraceGLApi::glWindowRectanglesEXTFn(GLenum mode,
                                          GLsizei n,
                                          const GLint* box) {
@@ -10104,6 +10183,16 @@
   return result;
 }
 
+GLenum DebugGLApi::glClientWaitSyncAPPLEFn(GLsync sync,
+                                           GLbitfield flags,
+                                           GLuint64 timeout) {
+  GL_SERVICE_LOG("glClientWaitSyncAPPLE"
+                 << "(" << sync << ", " << flags << ", " << timeout << ")");
+  GLenum result = gl_api_->glClientWaitSyncAPPLEFn(sync, flags, timeout);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 void DebugGLApi::glColorMaskFn(GLboolean red,
                                GLboolean green,
                                GLboolean blue,
@@ -10624,6 +10713,12 @@
   gl_api_->glDeleteSyncFn(sync);
 }
 
+void DebugGLApi::glDeleteSyncAPPLEFn(GLsync sync) {
+  GL_SERVICE_LOG("glDeleteSyncAPPLE"
+                 << "(" << sync << ")");
+  gl_api_->glDeleteSyncAPPLEFn(sync);
+}
+
 void DebugGLApi::glDeleteTexturesFn(GLsizei n, const GLuint* textures) {
   GL_SERVICE_LOG("glDeleteTextures"
                  << "(" << n << ", " << static_cast<const void*>(textures)
@@ -10845,6 +10940,15 @@
   return result;
 }
 
+GLsync DebugGLApi::glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) {
+  GL_SERVICE_LOG("glFenceSyncAPPLE"
+                 << "(" << GLEnums::GetStringEnum(condition) << ", " << flags
+                 << ")");
+  GLsync result = gl_api_->glFenceSyncAPPLEFn(condition, flags);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 void DebugGLApi::glFinishFn(void) {
   GL_SERVICE_LOG("glFinish"
                  << "("
@@ -12564,6 +12668,14 @@
   return result;
 }
 
+GLboolean DebugGLApi::glIsSyncAPPLEFn(GLsync sync) {
+  GL_SERVICE_LOG("glIsSyncAPPLE"
+                 << "(" << sync << ")");
+  GLboolean result = gl_api_->glIsSyncAPPLEFn(sync);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 GLboolean DebugGLApi::glIsTextureFn(GLuint texture) {
   GL_SERVICE_LOG("glIsTexture"
                  << "(" << texture << ")");
@@ -14628,6 +14740,14 @@
   gl_api_->glWaitSyncFn(sync, flags, timeout);
 }
 
+void DebugGLApi::glWaitSyncAPPLEFn(GLsync sync,
+                                   GLbitfield flags,
+                                   GLuint64 timeout) {
+  GL_SERVICE_LOG("glWaitSyncAPPLE"
+                 << "(" << sync << ", " << flags << ", " << timeout << ")");
+  gl_api_->glWaitSyncAPPLEFn(sync, flags, timeout);
+}
+
 void DebugGLApi::glWindowRectanglesEXTFn(GLenum mode,
                                          GLsizei n,
                                          const GLint* box) {
@@ -14905,6 +15025,13 @@
   return static_cast<GLenum>(0);
 }
 
+GLenum NoContextGLApi::glClientWaitSyncAPPLEFn(GLsync sync,
+                                               GLbitfield flags,
+                                               GLuint64 timeout) {
+  NoContextHelper("glClientWaitSyncAPPLE");
+  return static_cast<GLenum>(0);
+}
+
 void NoContextGLApi::glColorMaskFn(GLboolean red,
                                    GLboolean green,
                                    GLboolean blue,
@@ -15230,6 +15357,10 @@
   NoContextHelper("glDeleteSync");
 }
 
+void NoContextGLApi::glDeleteSyncAPPLEFn(GLsync sync) {
+  NoContextHelper("glDeleteSyncAPPLE");
+}
+
 void NoContextGLApi::glDeleteTexturesFn(GLsizei n, const GLuint* textures) {
   NoContextHelper("glDeleteTextures");
 }
@@ -15370,7 +15501,12 @@
 
 GLsync NoContextGLApi::glFenceSyncFn(GLenum condition, GLbitfield flags) {
   NoContextHelper("glFenceSync");
-  return NULL;
+  return nullptr;
+}
+
+GLsync NoContextGLApi::glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) {
+  NoContextHelper("glFenceSyncAPPLE");
+  return nullptr;
 }
 
 void NoContextGLApi::glFinishFn(void) {
@@ -16438,6 +16574,11 @@
   return GL_FALSE;
 }
 
+GLboolean NoContextGLApi::glIsSyncAPPLEFn(GLsync sync) {
+  NoContextHelper("glIsSyncAPPLE");
+  return GL_FALSE;
+}
+
 GLboolean NoContextGLApi::glIsTextureFn(GLuint texture) {
   NoContextHelper("glIsTexture");
   return GL_FALSE;
@@ -17779,6 +17920,12 @@
   NoContextHelper("glWaitSync");
 }
 
+void NoContextGLApi::glWaitSyncAPPLEFn(GLsync sync,
+                                       GLbitfield flags,
+                                       GLuint64 timeout) {
+  NoContextHelper("glWaitSyncAPPLE");
+}
+
 void NoContextGLApi::glWindowRectanglesEXTFn(GLenum mode,
                                              GLsizei n,
                                              const GLint* box) {
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index a946928..4151b91 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -147,6 +147,9 @@
 typedef GLenum(GL_BINDING_CALL* glClientWaitSyncProc)(GLsync sync,
                                                       GLbitfield flags,
                                                       GLuint64 timeout);
+typedef GLenum(GL_BINDING_CALL* glClientWaitSyncAPPLEProc)(GLsync sync,
+                                                           GLbitfield flags,
+                                                           GLuint64 timeout);
 typedef void(GL_BINDING_CALL* glColorMaskProc)(GLboolean red,
                                                GLboolean green,
                                                GLboolean blue,
@@ -362,6 +365,7 @@
     const GLuint* semaphores);
 typedef void(GL_BINDING_CALL* glDeleteShaderProc)(GLuint shader);
 typedef void(GL_BINDING_CALL* glDeleteSyncProc)(GLsync sync);
+typedef void(GL_BINDING_CALL* glDeleteSyncAPPLEProc)(GLsync sync);
 typedef void(GL_BINDING_CALL* glDeleteTexturesProc)(GLsizei n,
                                                     const GLuint* textures);
 typedef void(GL_BINDING_CALL* glDeleteTransformFeedbacksProc)(
@@ -430,6 +434,8 @@
 typedef void(GL_BINDING_CALL* glEndTransformFeedbackProc)(void);
 typedef GLsync(GL_BINDING_CALL* glFenceSyncProc)(GLenum condition,
                                                  GLbitfield flags);
+typedef GLsync(GL_BINDING_CALL* glFenceSyncAPPLEProc)(GLenum condition,
+                                                      GLbitfield flags);
 typedef void(GL_BINDING_CALL* glFinishProc)(void);
 typedef void(GL_BINDING_CALL* glFinishFenceAPPLEProc)(GLuint fence);
 typedef void(GL_BINDING_CALL* glFinishFenceNVProc)(GLuint fence);
@@ -1046,6 +1052,7 @@
 typedef GLboolean(GL_BINDING_CALL* glIsSamplerProc)(GLuint sampler);
 typedef GLboolean(GL_BINDING_CALL* glIsShaderProc)(GLuint shader);
 typedef GLboolean(GL_BINDING_CALL* glIsSyncProc)(GLsync sync);
+typedef GLboolean(GL_BINDING_CALL* glIsSyncAPPLEProc)(GLsync sync);
 typedef GLboolean(GL_BINDING_CALL* glIsTextureProc)(GLuint texture);
 typedef GLboolean(GL_BINDING_CALL* glIsTransformFeedbackProc)(GLuint id);
 typedef GLboolean(GL_BINDING_CALL* glIsVertexArrayOESProc)(GLuint array);
@@ -1840,6 +1847,9 @@
 typedef void(GL_BINDING_CALL* glWaitSyncProc)(GLsync sync,
                                               GLbitfield flags,
                                               GLuint64 timeout);
+typedef void(GL_BINDING_CALL* glWaitSyncAPPLEProc)(GLsync sync,
+                                                   GLbitfield flags,
+                                                   GLuint64 timeout);
 typedef void(GL_BINDING_CALL* glWindowRectanglesEXTProc)(GLenum mode,
                                                          GLsizei n,
                                                          const GLint* box);
@@ -1855,6 +1865,7 @@
   bool b_GL_ANGLE_texture_external_update;
   bool b_GL_ANGLE_translated_shader_source;
   bool b_GL_APPLE_fence;
+  bool b_GL_APPLE_sync;
   bool b_GL_APPLE_vertex_array_object;
   bool b_GL_ARB_ES2_compatibility;
   bool b_GL_ARB_blend_func_extended;
@@ -1978,6 +1989,7 @@
   glClearTexImageProc glClearTexImageFn;
   glClearTexSubImageProc glClearTexSubImageFn;
   glClientWaitSyncProc glClientWaitSyncFn;
+  glClientWaitSyncAPPLEProc glClientWaitSyncAPPLEFn;
   glColorMaskProc glColorMaskFn;
   glCompileShaderProc glCompileShaderFn;
   glCompressedTexImage2DProc glCompressedTexImage2DFn;
@@ -2022,6 +2034,7 @@
   glDeleteSemaphoresEXTProc glDeleteSemaphoresEXTFn;
   glDeleteShaderProc glDeleteShaderFn;
   glDeleteSyncProc glDeleteSyncFn;
+  glDeleteSyncAPPLEProc glDeleteSyncAPPLEFn;
   glDeleteTexturesProc glDeleteTexturesFn;
   glDeleteTransformFeedbacksProc glDeleteTransformFeedbacksFn;
   glDeleteVertexArraysOESProc glDeleteVertexArraysOESFn;
@@ -2052,6 +2065,7 @@
   glEndQueryProc glEndQueryFn;
   glEndTransformFeedbackProc glEndTransformFeedbackFn;
   glFenceSyncProc glFenceSyncFn;
+  glFenceSyncAPPLEProc glFenceSyncAPPLEFn;
   glFinishProc glFinishFn;
   glFinishFenceAPPLEProc glFinishFenceAPPLEFn;
   glFinishFenceNVProc glFinishFenceNVFn;
@@ -2221,6 +2235,7 @@
   glIsSamplerProc glIsSamplerFn;
   glIsShaderProc glIsShaderFn;
   glIsSyncProc glIsSyncFn;
+  glIsSyncAPPLEProc glIsSyncAPPLEFn;
   glIsTextureProc glIsTextureFn;
   glIsTransformFeedbackProc glIsTransformFeedbackFn;
   glIsVertexArrayOESProc glIsVertexArrayOESFn;
@@ -2422,6 +2437,7 @@
   glViewportProc glViewportFn;
   glWaitSemaphoreEXTProc glWaitSemaphoreEXTFn;
   glWaitSyncProc glWaitSyncFn;
+  glWaitSyncAPPLEProc glWaitSyncAPPLEFn;
   glWindowRectanglesEXTProc glWindowRectanglesEXTFn;
 };
 
@@ -2552,6 +2568,9 @@
   virtual GLenum glClientWaitSyncFn(GLsync sync,
                                     GLbitfield flags,
                                     GLuint64 timeout) = 0;
+  virtual GLenum glClientWaitSyncAPPLEFn(GLsync sync,
+                                         GLbitfield flags,
+                                         GLuint64 timeout) = 0;
   virtual void glColorMaskFn(GLboolean red,
                              GLboolean green,
                              GLboolean blue,
@@ -2744,6 +2763,7 @@
   virtual void glDeleteSemaphoresEXTFn(GLsizei n, const GLuint* semaphores) = 0;
   virtual void glDeleteShaderFn(GLuint shader) = 0;
   virtual void glDeleteSyncFn(GLsync sync) = 0;
+  virtual void glDeleteSyncAPPLEFn(GLsync sync) = 0;
   virtual void glDeleteTexturesFn(GLsizei n, const GLuint* textures) = 0;
   virtual void glDeleteTransformFeedbacksFn(GLsizei n, const GLuint* ids) = 0;
   virtual void glDeleteVertexArraysOESFn(GLsizei n, const GLuint* arrays) = 0;
@@ -2797,6 +2817,7 @@
   virtual void glEndQueryFn(GLenum target) = 0;
   virtual void glEndTransformFeedbackFn(void) = 0;
   virtual GLsync glFenceSyncFn(GLenum condition, GLbitfield flags) = 0;
+  virtual GLsync glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) = 0;
   virtual void glFinishFn(void) = 0;
   virtual void glFinishFenceAPPLEFn(GLuint fence) = 0;
   virtual void glFinishFenceNVFn(GLuint fence) = 0;
@@ -3337,6 +3358,7 @@
   virtual GLboolean glIsSamplerFn(GLuint sampler) = 0;
   virtual GLboolean glIsShaderFn(GLuint shader) = 0;
   virtual GLboolean glIsSyncFn(GLsync sync) = 0;
+  virtual GLboolean glIsSyncAPPLEFn(GLsync sync) = 0;
   virtual GLboolean glIsTextureFn(GLuint texture) = 0;
   virtual GLboolean glIsTransformFeedbackFn(GLuint id) = 0;
   virtual GLboolean glIsVertexArrayOESFn(GLuint array) = 0;
@@ -4062,6 +4084,9 @@
   virtual void glWaitSyncFn(GLsync sync,
                             GLbitfield flags,
                             GLuint64 timeout) = 0;
+  virtual void glWaitSyncAPPLEFn(GLsync sync,
+                                 GLbitfield flags,
+                                 GLuint64 timeout) = 0;
   virtual void glWindowRectanglesEXTFn(GLenum mode,
                                        GLsizei n,
                                        const GLint* box) = 0;
@@ -4127,6 +4152,8 @@
 #define glClearTexImage ::gl::g_current_gl_context->glClearTexImageFn
 #define glClearTexSubImage ::gl::g_current_gl_context->glClearTexSubImageFn
 #define glClientWaitSync ::gl::g_current_gl_context->glClientWaitSyncFn
+#define glClientWaitSyncAPPLE \
+  ::gl::g_current_gl_context->glClientWaitSyncAPPLEFn
 #define glColorMask ::gl::g_current_gl_context->glColorMaskFn
 #define glCompileShader ::gl::g_current_gl_context->glCompileShaderFn
 #define glCompressedTexImage2D \
@@ -4190,6 +4217,7 @@
   ::gl::g_current_gl_context->glDeleteSemaphoresEXTFn
 #define glDeleteShader ::gl::g_current_gl_context->glDeleteShaderFn
 #define glDeleteSync ::gl::g_current_gl_context->glDeleteSyncFn
+#define glDeleteSyncAPPLE ::gl::g_current_gl_context->glDeleteSyncAPPLEFn
 #define glDeleteTextures ::gl::g_current_gl_context->glDeleteTexturesFn
 #define glDeleteTransformFeedbacks \
   ::gl::g_current_gl_context->glDeleteTransformFeedbacksFn
@@ -4231,6 +4259,7 @@
 #define glEndTransformFeedback \
   ::gl::g_current_gl_context->glEndTransformFeedbackFn
 #define glFenceSync ::gl::g_current_gl_context->glFenceSyncFn
+#define glFenceSyncAPPLE ::gl::g_current_gl_context->glFenceSyncAPPLEFn
 #define glFinish ::gl::g_current_gl_context->glFinishFn
 #define glFinishFenceAPPLE ::gl::g_current_gl_context->glFinishFenceAPPLEFn
 #define glFinishFenceNV ::gl::g_current_gl_context->glFinishFenceNVFn
@@ -4481,6 +4510,7 @@
 #define glIsSampler ::gl::g_current_gl_context->glIsSamplerFn
 #define glIsShader ::gl::g_current_gl_context->glIsShaderFn
 #define glIsSync ::gl::g_current_gl_context->glIsSyncFn
+#define glIsSyncAPPLE ::gl::g_current_gl_context->glIsSyncAPPLEFn
 #define glIsTexture ::gl::g_current_gl_context->glIsTextureFn
 #define glIsTransformFeedback \
   ::gl::g_current_gl_context->glIsTransformFeedbackFn
@@ -4740,6 +4770,7 @@
 #define glViewport ::gl::g_current_gl_context->glViewportFn
 #define glWaitSemaphoreEXT ::gl::g_current_gl_context->glWaitSemaphoreEXTFn
 #define glWaitSync ::gl::g_current_gl_context->glWaitSyncFn
+#define glWaitSyncAPPLE ::gl::g_current_gl_context->glWaitSyncAPPLEFn
 #define glWindowRectanglesEXT \
   ::gl::g_current_gl_context->glWindowRectanglesEXTFn
 
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index a123fe0..483d0a8 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -511,6 +511,14 @@
   return interface_->ClientWaitSync(sync, flags, timeout);
 }
 
+GLenum GL_BINDING_CALL
+MockGLInterface::Mock_glClientWaitSyncAPPLE(GLsync sync,
+                                            GLbitfield flags,
+                                            GLuint64 timeout) {
+  MakeGlMockFunctionUnique("glClientWaitSyncAPPLE");
+  return interface_->ClientWaitSyncAPPLE(sync, flags, timeout);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glColorMask(GLboolean red,
                                                        GLboolean green,
                                                        GLboolean blue,
@@ -1037,6 +1045,11 @@
   interface_->DeleteSync(sync);
 }
 
+void GL_BINDING_CALL MockGLInterface::Mock_glDeleteSyncAPPLE(GLsync sync) {
+  MakeGlMockFunctionUnique("glDeleteSyncAPPLE");
+  interface_->DeleteSyncAPPLE(sync);
+}
+
 void GL_BINDING_CALL
 MockGLInterface::Mock_glDeleteTextures(GLsizei n, const GLuint* textures) {
   MakeGlMockFunctionUnique("glDeleteTextures");
@@ -1306,6 +1319,12 @@
   return interface_->FenceSync(condition, flags);
 }
 
+GLsync GL_BINDING_CALL
+MockGLInterface::Mock_glFenceSyncAPPLE(GLenum condition, GLbitfield flags) {
+  MakeGlMockFunctionUnique("glFenceSyncAPPLE");
+  return interface_->FenceSyncAPPLE(condition, flags);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glFinish(void) {
   MakeGlMockFunctionUnique("glFinish");
   interface_->Finish();
@@ -2948,6 +2967,11 @@
   return interface_->IsSync(sync);
 }
 
+GLboolean GL_BINDING_CALL MockGLInterface::Mock_glIsSyncAPPLE(GLsync sync) {
+  MakeGlMockFunctionUnique("glIsSyncAPPLE");
+  return interface_->IsSyncAPPLE(sync);
+}
+
 GLboolean GL_BINDING_CALL MockGLInterface::Mock_glIsTexture(GLuint texture) {
   MakeGlMockFunctionUnique("glIsTexture");
   return interface_->IsTexture(texture);
@@ -4990,6 +5014,13 @@
   interface_->WaitSync(sync, flags, timeout);
 }
 
+void GL_BINDING_CALL MockGLInterface::Mock_glWaitSyncAPPLE(GLsync sync,
+                                                           GLbitfield flags,
+                                                           GLuint64 timeout) {
+  MakeGlMockFunctionUnique("glWaitSyncAPPLE");
+  interface_->WaitSyncAPPLE(sync, flags, timeout);
+}
+
 void GL_BINDING_CALL
 MockGLInterface::Mock_glWindowRectanglesEXT(GLenum mode,
                                             GLsizei n,
@@ -5142,6 +5173,8 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glClearTexSubImageEXT);
   if (strcmp(name, "glClientWaitSync") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glClientWaitSync);
+  if (strcmp(name, "glClientWaitSyncAPPLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glClientWaitSyncAPPLE);
   if (strcmp(name, "glColorMask") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glColorMask);
   if (strcmp(name, "glCompileShader") == 0)
@@ -5273,6 +5306,8 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteShader);
   if (strcmp(name, "glDeleteSync") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteSync);
+  if (strcmp(name, "glDeleteSyncAPPLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteSyncAPPLE);
   if (strcmp(name, "glDeleteTextures") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glDeleteTextures);
   if (strcmp(name, "glDeleteTransformFeedbacks") == 0)
@@ -5368,6 +5403,8 @@
         Mock_glEndTransformFeedbackEXT);
   if (strcmp(name, "glFenceSync") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glFenceSync);
+  if (strcmp(name, "glFenceSyncAPPLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glFenceSyncAPPLE);
   if (strcmp(name, "glFinish") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glFinish);
   if (strcmp(name, "glFinishFenceAPPLE") == 0)
@@ -5852,6 +5889,8 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glIsShader);
   if (strcmp(name, "glIsSync") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glIsSync);
+  if (strcmp(name, "glIsSyncAPPLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glIsSyncAPPLE);
   if (strcmp(name, "glIsTexture") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glIsTexture);
   if (strcmp(name, "glIsTransformFeedback") == 0)
@@ -6394,6 +6433,8 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glWaitSemaphoreEXT);
   if (strcmp(name, "glWaitSync") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glWaitSync);
+  if (strcmp(name, "glWaitSyncAPPLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_glWaitSyncAPPLE);
   if (strcmp(name, "glWindowRectanglesEXT") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glWindowRectanglesEXT);
   return reinterpret_cast<GLFunctionPointerType>(&MockGlInvalidFunction);
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index 45a0b1bd43..db74334 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -211,6 +211,9 @@
 static GLenum GL_BINDING_CALL Mock_glClientWaitSync(GLsync sync,
                                                     GLbitfield flags,
                                                     GLuint64 timeout);
+static GLenum GL_BINDING_CALL Mock_glClientWaitSyncAPPLE(GLsync sync,
+                                                         GLbitfield flags,
+                                                         GLuint64 timeout);
 static void GL_BINDING_CALL Mock_glColorMask(GLboolean red,
                                              GLboolean green,
                                              GLboolean blue,
@@ -465,6 +468,7 @@
 Mock_glDeleteSemaphoresEXT(GLsizei n, const GLuint* semaphores);
 static void GL_BINDING_CALL Mock_glDeleteShader(GLuint shader);
 static void GL_BINDING_CALL Mock_glDeleteSync(GLsync sync);
+static void GL_BINDING_CALL Mock_glDeleteSyncAPPLE(GLsync sync);
 static void GL_BINDING_CALL Mock_glDeleteTextures(GLsizei n,
                                                   const GLuint* textures);
 static void GL_BINDING_CALL Mock_glDeleteTransformFeedbacks(GLsizei n,
@@ -555,6 +559,8 @@
 static void GL_BINDING_CALL Mock_glEndTransformFeedbackEXT(void);
 static GLsync GL_BINDING_CALL Mock_glFenceSync(GLenum condition,
                                                GLbitfield flags);
+static GLsync GL_BINDING_CALL Mock_glFenceSyncAPPLE(GLenum condition,
+                                                    GLbitfield flags);
 static void GL_BINDING_CALL Mock_glFinish(void);
 static void GL_BINDING_CALL Mock_glFinishFenceAPPLE(GLuint fence);
 static void GL_BINDING_CALL Mock_glFinishFenceNV(GLuint fence);
@@ -1261,6 +1267,7 @@
 static GLboolean GL_BINDING_CALL Mock_glIsSampler(GLuint sampler);
 static GLboolean GL_BINDING_CALL Mock_glIsShader(GLuint shader);
 static GLboolean GL_BINDING_CALL Mock_glIsSync(GLsync sync);
+static GLboolean GL_BINDING_CALL Mock_glIsSyncAPPLE(GLsync sync);
 static GLboolean GL_BINDING_CALL Mock_glIsTexture(GLuint texture);
 static GLboolean GL_BINDING_CALL Mock_glIsTransformFeedback(GLuint id);
 static GLboolean GL_BINDING_CALL Mock_glIsVertexArray(GLuint array);
@@ -2181,6 +2188,9 @@
 static void GL_BINDING_CALL Mock_glWaitSync(GLsync sync,
                                             GLbitfield flags,
                                             GLuint64 timeout);
+static void GL_BINDING_CALL Mock_glWaitSyncAPPLE(GLsync sync,
+                                                 GLbitfield flags,
+                                                 GLuint64 timeout);
 static void GL_BINDING_CALL Mock_glWindowRectanglesEXT(GLenum mode,
                                                        GLsizei n,
                                                        const GLint* box);
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index 59ef4f7b..69e6d25 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -107,6 +107,8 @@
 // glClearTexSubImage cannot be mocked because it has 11 args.
 MOCK_METHOD3(ClientWaitSync,
              GLenum(GLsync sync, GLbitfield flags, GLuint64 timeout));
+MOCK_METHOD3(ClientWaitSyncAPPLE,
+             GLenum(GLsync sync, GLbitfield flags, GLuint64 timeout));
 MOCK_METHOD4(
     ColorMask,
     void(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha));
@@ -279,6 +281,7 @@
 MOCK_METHOD2(DeleteSemaphoresEXT, void(GLsizei n, const GLuint* semaphores));
 MOCK_METHOD1(DeleteShader, void(GLuint shader));
 MOCK_METHOD1(DeleteSync, void(GLsync sync));
+MOCK_METHOD1(DeleteSyncAPPLE, void(GLsync sync));
 MOCK_METHOD2(DeleteTextures, void(GLsizei n, const GLuint* textures));
 MOCK_METHOD2(DeleteTransformFeedbacks, void(GLsizei n, const GLuint* ids));
 MOCK_METHOD2(DeleteVertexArraysOES, void(GLsizei n, const GLuint* arrays));
@@ -329,6 +332,7 @@
 MOCK_METHOD1(EndQuery, void(GLenum target));
 MOCK_METHOD0(EndTransformFeedback, void());
 MOCK_METHOD2(FenceSync, GLsync(GLenum condition, GLbitfield flags));
+MOCK_METHOD2(FenceSyncAPPLE, GLsync(GLenum condition, GLbitfield flags));
 MOCK_METHOD0(Finish, void());
 MOCK_METHOD1(FinishFenceAPPLE, void(GLuint fence));
 MOCK_METHOD1(FinishFenceNV, void(GLuint fence));
@@ -880,6 +884,7 @@
 MOCK_METHOD1(IsSampler, GLboolean(GLuint sampler));
 MOCK_METHOD1(IsShader, GLboolean(GLuint shader));
 MOCK_METHOD1(IsSync, GLboolean(GLsync sync));
+MOCK_METHOD1(IsSyncAPPLE, GLboolean(GLsync sync));
 MOCK_METHOD1(IsTexture, GLboolean(GLuint texture));
 MOCK_METHOD1(IsTransformFeedback, GLboolean(GLuint id));
 MOCK_METHOD1(IsVertexArrayOES, GLboolean(GLuint array));
@@ -1490,5 +1495,7 @@
                   const GLuint* textures,
                   const GLenum* srcLayouts));
 MOCK_METHOD3(WaitSync, void(GLsync sync, GLbitfield flags, GLuint64 timeout));
+MOCK_METHOD3(WaitSyncAPPLE,
+             void(GLsync sync, GLbitfield flags, GLuint64 timeout));
 MOCK_METHOD3(WindowRectanglesEXT,
              void(GLenum mode, GLsizei n, const GLint* box));
diff --git a/ui/gl/gl_stub_autogen_gl.cc b/ui/gl/gl_stub_autogen_gl.cc
index 33f7dff4..3fee2b3 100644
--- a/ui/gl/gl_stub_autogen_gl.cc
+++ b/ui/gl/gl_stub_autogen_gl.cc
@@ -22,6 +22,12 @@
   return 0;
 }
 
+GLenum GLStubApiBase::glClientWaitSyncAPPLEFn(GLsync sync,
+                                              GLbitfield flags,
+                                              GLuint64 timeout) {
+  return 0;
+}
+
 GLuint GLStubApiBase::glCreateProgramFn() {
   return 0;
 }
@@ -40,6 +46,10 @@
   return 0;
 }
 
+GLsync GLStubApiBase::glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) {
+  return 0;
+}
+
 GLuint GLStubApiBase::glGenPathsNVFn(GLsizei range) {
   return 0;
 }
@@ -160,6 +170,10 @@
   return 0;
 }
 
+GLboolean GLStubApiBase::glIsSyncAPPLEFn(GLsync sync) {
+  return 0;
+}
+
 GLboolean GLStubApiBase::glIsTextureFn(GLuint texture) {
   return 0;
 }
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 5f464db..bc452de 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -129,6 +129,9 @@
 GLenum glClientWaitSyncFn(GLsync sync,
                           GLbitfield flags,
                           GLuint64 timeout) override;
+GLenum glClientWaitSyncAPPLEFn(GLsync sync,
+                               GLbitfield flags,
+                               GLuint64 timeout) override;
 void glColorMaskFn(GLboolean red,
                    GLboolean green,
                    GLboolean blue,
@@ -319,6 +322,7 @@
 void glDeleteSemaphoresEXTFn(GLsizei n, const GLuint* semaphores) override {}
 void glDeleteShaderFn(GLuint shader) override {}
 void glDeleteSyncFn(GLsync sync) override {}
+void glDeleteSyncAPPLEFn(GLsync sync) override {}
 void glDeleteTexturesFn(GLsizei n, const GLuint* textures) override {}
 void glDeleteTransformFeedbacksFn(GLsizei n, const GLuint* ids) override {}
 void glDeleteVertexArraysOESFn(GLsizei n, const GLuint* arrays) override {}
@@ -371,6 +375,7 @@
 void glEndQueryFn(GLenum target) override {}
 void glEndTransformFeedbackFn() override {}
 GLsync glFenceSyncFn(GLenum condition, GLbitfield flags) override;
+GLsync glFenceSyncAPPLEFn(GLenum condition, GLbitfield flags) override;
 void glFinishFn() override {}
 void glFinishFenceAPPLEFn(GLuint fence) override {}
 void glFinishFenceNVFn(GLuint fence) override {}
@@ -897,6 +902,7 @@
 GLboolean glIsSamplerFn(GLuint sampler) override;
 GLboolean glIsShaderFn(GLuint shader) override;
 GLboolean glIsSyncFn(GLsync sync) override;
+GLboolean glIsSyncAPPLEFn(GLsync sync) override;
 GLboolean glIsTextureFn(GLuint texture) override;
 GLboolean glIsTransformFeedbackFn(GLuint id) override;
 GLboolean glIsVertexArrayOESFn(GLuint array) override;
@@ -1569,6 +1575,9 @@
                           const GLuint* textures,
                           const GLenum* srcLayouts) override {}
 void glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override {}
+void glWaitSyncAPPLEFn(GLsync sync,
+                       GLbitfield flags,
+                       GLuint64 timeout) override {}
 void glWindowRectanglesEXTFn(GLenum mode,
                              GLsizei n,
                              const GLint* box) override {}
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc
index 2f5ccbff..6f8bbac8 100644
--- a/ui/gl/init/create_gr_gl_interface.cc
+++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -11,6 +11,81 @@
 namespace gl {
 namespace init {
 
+// This code emulates GL fences (GL_APPLE_sync or GL_ARB_sync) via
+// EGL_KHR_fence_sync extension. It's used to provide Skia ways of
+// synchronization on platforms that does not have GL fences but support EGL
+namespace {
+struct EGLFenceData {
+  EGLSync sync;
+  EGLDisplay display;
+};
+
+GLsync glFenceSyncEmulateEGL(GLenum condition, GLbitfield flags) {
+  DCHECK(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
+  DCHECK(flags == 0);
+
+  init::EGLFenceData* data = new EGLFenceData;
+
+  data->display = eglGetCurrentDisplay();
+  data->sync = eglCreateSyncKHR(data->display, EGL_SYNC_FENCE_KHR, nullptr);
+
+  return reinterpret_cast<GLsync>(data);
+}
+
+void glDeleteSyncEmulateEGL(GLsync sync) {
+  EGLFenceData* data = reinterpret_cast<EGLFenceData*>(sync);
+  eglDestroySyncKHR(data->display, data->sync);
+  delete data;
+}
+
+GLenum glClientWaitSyncEmulateEGL(GLsync sync,
+                                  GLbitfield flags,
+                                  GLuint64 timeout) {
+  init::EGLFenceData* data = reinterpret_cast<init::EGLFenceData*>(sync);
+
+  EGLint egl_flags = 0;
+
+  if (flags & GL_SYNC_FLUSH_COMMANDS_BIT) {
+    egl_flags |= EGL_SYNC_FLUSH_COMMANDS_BIT;
+  }
+  EGLint result =
+      eglClientWaitSyncKHR(data->display, data->sync, egl_flags, timeout);
+
+  switch (result) {
+    case EGL_CONDITION_SATISFIED:
+      return GL_CONDITION_SATISFIED;
+    case EGL_TIMEOUT_EXPIRED:
+      return GL_TIMEOUT_EXPIRED;
+    case EGL_FALSE:
+      return GL_WAIT_FAILED;
+  }
+
+  NOTREACHED();
+  return 0;
+}
+
+void glWaitSyncEmulateEGL(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  init::EGLFenceData* data = reinterpret_cast<init::EGLFenceData*>(sync);
+
+  DCHECK(timeout == GL_TIMEOUT_IGNORED);
+  DCHECK(flags == 0);
+
+  if (!g_driver_egl.ext.b_EGL_KHR_wait_sync) {
+    eglClientWaitSyncKHR(data->display, data->sync, 0, EGL_FOREVER_KHR);
+    return;
+  }
+
+  EGLint result = eglWaitSyncKHR(data->display, data->sync, 0);
+  DCHECK(result);
+}
+
+GLboolean glIsSyncEmulateEGL(GLsync sync) {
+  NOTREACHED();
+  return true;
+}
+
+}  // namespace
+
 namespace {
 
 template <typename R, typename... Args>
@@ -88,7 +163,6 @@
 
 const char* kBlacklistExtensions[] = {
     "GL_APPLE_framebuffer_multisample",
-    "GL_APPLE_sync",
     "GL_ARB_ES3_1_compatibility",
     "GL_ARB_draw_indirect",
     "GL_ARB_invalidate_subdata",
@@ -587,6 +661,26 @@
   functions->fWaitSync = gl->glWaitSyncFn;
   functions->fDeleteSync = gl->glDeleteSyncFn;
 
+  if (!gl->glFenceSyncFn) {
+    // NOTE: Skia uses the same function pointers without APPLE suffix
+    if (extensions.has("GL_APPLE_sync")) {
+      functions->fFenceSync = gl->glFenceSyncAPPLEFn;
+      functions->fIsSync = gl->glIsSyncAPPLEFn;
+      functions->fClientWaitSync = gl->glClientWaitSyncAPPLEFn;
+      functions->fWaitSync = gl->glWaitSyncAPPLEFn;
+      functions->fDeleteSync = gl->glDeleteSyncAPPLEFn;
+    } else if (g_driver_egl.ext.b_EGL_KHR_fence_sync) {
+      // Emulate APPLE_sync via egl
+      extensions.add("GL_APPLE_sync");
+
+      functions->fFenceSync = glFenceSyncEmulateEGL;
+      functions->fIsSync = glIsSyncEmulateEGL;
+      functions->fClientWaitSync = glClientWaitSyncEmulateEGL;
+      functions->fWaitSync = glWaitSyncEmulateEGL;
+      functions->fDeleteSync = glDeleteSyncEmulateEGL;
+    }
+  }
+
   functions->fGetInternalformativ = gl->glGetInternalformativFn;
 
   interface->fStandard = standard;
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 091f0b7..2370d15 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -79,6 +79,32 @@
   return CaptionStyle::FromSystemSettings();
 }
 
+const std::map<NativeTheme::SystemThemeColor, SkColor>&
+NativeTheme::GetSystemColors() const {
+  return system_colors_;
+}
+
+bool NativeTheme::HasDifferentSystemColors(
+    const std::map<NativeTheme::SystemThemeColor, SkColor>& colors) const {
+  return system_colors_ != colors;
+}
+
+void NativeTheme::set_system_colors(
+    const std::map<NativeTheme::SystemThemeColor, SkColor>& colors) {
+  system_colors_ = colors;
+}
+
+void NativeTheme::UpdateSystemColorInfo(
+    bool is_dark_mode,
+    bool is_high_contrast,
+    PreferredColorScheme preferred_color_scheme,
+    const base::flat_map<SystemThemeColor, uint32_t>& colors) {
+  set_use_dark_colors(is_dark_mode);
+  set_high_contrast(is_high_contrast);
+  set_preferred_color_scheme(preferred_color_scheme);
+  system_colors_.insert(colors.begin(), colors.end());
+}
+
 NativeTheme::ColorSchemeNativeThemeObserver::ColorSchemeNativeThemeObserver(
     NativeTheme* theme_to_update)
     : theme_to_update_(theme_to_update) {}
@@ -107,6 +133,12 @@
     notify_observers = true;
   }
 
+  const auto& system_colors = observed_theme->GetSystemColors();
+  if (theme_to_update_->HasDifferentSystemColors(system_colors)) {
+    theme_to_update_->set_system_colors(system_colors);
+    notify_observers = true;
+  }
+
   if (notify_observers)
     theme_to_update_->NotifyObservers();
 }
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 8d2e68a0..d12315d 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -5,6 +5,8 @@
 #ifndef UI_NATIVE_THEME_NATIVE_THEME_H_
 #define UI_NATIVE_THEME_NATIVE_THEME_H_
 
+#include <map>
+
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "build/build_config.h"
@@ -106,6 +108,7 @@
     kNoPreference,
     kDark,
     kLight,
+    kMaxValue = kLight,
   };
 
   // The color scheme used for painting the native controls.
@@ -399,6 +402,21 @@
     kColorId_NumColors,
   };
 
+  enum class SystemThemeColor {
+    kNotSupported,
+    kButtonFace,
+    kButtonText,
+    kGrayText,
+    kHighlight,
+    kHighlightText,
+    kHotlight,
+    kMenuHighlight,
+    kScrollbar,
+    kWindow,
+    kWindowText,
+    kMaxValue = kWindowText,
+  };
+
   // Return a color from the system theme.
   virtual SkColor GetSystemColor(
       ColorId color_id,
@@ -444,6 +462,29 @@
 
   ColorScheme GetSystemColorScheme() const;
 
+  virtual const std::map<SystemThemeColor, SkColor>& GetSystemColors() const;
+
+  bool HasDifferentSystemColors(
+      const std::map<SystemThemeColor, SkColor>& colors) const;
+
+  void set_use_dark_colors(bool should_use_dark_colors) {
+    should_use_dark_colors_ = should_use_dark_colors;
+  }
+  void set_high_contrast(bool is_high_contrast) {
+    is_high_contrast_ = is_high_contrast;
+  }
+  void set_preferred_color_scheme(PreferredColorScheme preferred_color_scheme) {
+    preferred_color_scheme_ = preferred_color_scheme;
+  }
+
+  void set_system_colors(const std::map<SystemThemeColor, SkColor>& colors);
+
+  void UpdateSystemColorInfo(
+      bool is_dark_mode,
+      bool is_high_contrast,
+      PreferredColorScheme preferred_color_scheme,
+      const base::flat_map<SystemThemeColor, uint32_t>& colors);
+
  protected:
   NativeTheme();
   virtual ~NativeTheme();
@@ -468,16 +509,6 @@
   // be set to no-preference.
   virtual PreferredColorScheme CalculatePreferredColorScheme() const;
 
-  void set_use_dark_colors(bool should_use_dark_colors) {
-    should_use_dark_colors_ = should_use_dark_colors;
-  }
-  void set_high_contrast(bool is_high_contrast) {
-    is_high_contrast_ = is_high_contrast;
-  }
-  void set_preferred_color_scheme(PreferredColorScheme preferred_color_scheme) {
-    preferred_color_scheme_ = preferred_color_scheme;
-  }
-
   // Allows one native theme to observe changes in another. For example, the
   // web native theme for Windows observes the corresponding ui native theme in
   // order to receive changes regarding the state of dark mode, high contrast,
@@ -498,6 +529,8 @@
     DISALLOW_COPY_AND_ASSIGN(ColorSchemeNativeThemeObserver);
   };
 
+  mutable std::map<SystemThemeColor, SkColor> system_colors_;
+
  private:
   // Observers to notify when the native theme changes.
   base::ObserverList<NativeThemeObserver>::Unchecked native_theme_observers_;
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 78e3b14..2572060 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -49,18 +49,10 @@
 namespace {
 
 // Windows system color IDs cached and updated by the native theme.
-const int kSystemColors[] = {
-  COLOR_3DFACE,
-  COLOR_BTNFACE,
-  COLOR_BTNTEXT,
-  COLOR_GRAYTEXT,
-  COLOR_HIGHLIGHT,
-  COLOR_HIGHLIGHTTEXT,
-  COLOR_HOTLIGHT,
-  COLOR_MENUHIGHLIGHT,
-  COLOR_SCROLLBAR,
-  COLOR_WINDOW,
-  COLOR_WINDOWTEXT,
+const int kSysColors[] = {
+    COLOR_BTNFACE,       COLOR_BTNTEXT,    COLOR_GRAYTEXT,      COLOR_HIGHLIGHT,
+    COLOR_HIGHLIGHTTEXT, COLOR_HOTLIGHT,   COLOR_MENUHIGHLIGHT, COLOR_SCROLLBAR,
+    COLOR_WINDOW,        COLOR_WINDOWTEXT,
 };
 
 void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
@@ -148,6 +140,33 @@
 
 namespace ui {
 
+NativeTheme::SystemThemeColor SysColorToSystemThemeColor(int system_color) {
+  switch (system_color) {
+    case COLOR_BTNFACE:
+      return NativeTheme::SystemThemeColor::kButtonFace;
+    case COLOR_BTNTEXT:
+      return NativeTheme::SystemThemeColor::kButtonText;
+    case COLOR_GRAYTEXT:
+      return NativeTheme::SystemThemeColor::kGrayText;
+    case COLOR_HIGHLIGHT:
+      return NativeTheme::SystemThemeColor::kHighlight;
+    case COLOR_HIGHLIGHTTEXT:
+      return NativeTheme::SystemThemeColor::kHighlightText;
+    case COLOR_HOTLIGHT:
+      return NativeTheme::SystemThemeColor::kHotlight;
+    case COLOR_MENUHIGHLIGHT:
+      return NativeTheme::SystemThemeColor::kMenuHighlight;
+    case COLOR_SCROLLBAR:
+      return NativeTheme::SystemThemeColor::kScrollbar;
+    case COLOR_WINDOW:
+      return NativeTheme::SystemThemeColor::kWindow;
+    case COLOR_WINDOWTEXT:
+      return NativeTheme::SystemThemeColor::kWindowText;
+    default:
+      return NativeTheme::SystemThemeColor::kNotSupported;
+  }
+}
+
 NativeTheme* NativeTheme::GetInstanceForNativeUi() {
   return NativeThemeWin::instance();
 }
@@ -268,6 +287,13 @@
 
   // Initialize the cached system colors.
   UpdateSystemColors();
+
+  // Initialize the native theme web instance with the system color info.
+  NativeTheme* web_instance = NativeTheme::GetInstanceForWeb();
+  web_instance->set_use_dark_colors(ShouldUseDarkColors());
+  web_instance->set_high_contrast(UsesHighContrastColors());
+  web_instance->set_preferred_color_scheme(GetPreferredColorScheme());
+  web_instance->set_system_colors(GetSystemColors());
 }
 
 NativeThemeWin::~NativeThemeWin() {
@@ -301,8 +327,9 @@
 }
 
 void NativeThemeWin::UpdateSystemColors() {
-  for (int kSystemColor : kSystemColors)
-    system_colors_[kSystemColor] = color_utils::GetSysSkColor(kSystemColor);
+  for (int sys_color : kSysColors)
+    system_colors_[SysColorToSystemThemeColor(sys_color)] =
+        color_utils::GetSysSkColor(sys_color);
 }
 
 void NativeThemeWin::PaintMenuSeparator(cc::PaintCanvas* canvas,
@@ -449,7 +476,7 @@
   switch (color_id) {
     // Windows
     case kColorId_WindowBackground:
-      return system_colors_[COLOR_WINDOW];
+      return system_colors_[SystemThemeColor::kWindow];
 
     // Dialogs
     case kColorId_DialogBackground:
@@ -463,72 +490,74 @@
 
     // Button
     case kColorId_ButtonEnabledColor:
-      return system_colors_[COLOR_BTNTEXT];
+      return system_colors_[SystemThemeColor::kButtonText];
     case kColorId_ButtonHoverColor:
       return kButtonHoverColor;
 
     // Label
     case kColorId_LabelEnabledColor:
-      return system_colors_[COLOR_BTNTEXT];
+      return system_colors_[SystemThemeColor::kButtonText];
     case kColorId_LabelDisabledColor:
-      return system_colors_[COLOR_GRAYTEXT];
+      return system_colors_[SystemThemeColor::kGrayText];
     case kColorId_LabelTextSelectionColor:
-      return system_colors_[COLOR_HIGHLIGHTTEXT];
+      return system_colors_[SystemThemeColor::kHighlightText];
     case kColorId_LabelTextSelectionBackgroundFocused:
       return kLabelTextSelectionBackgroundFocusedColor;
 
     // Textfield
     case kColorId_TextfieldDefaultColor:
-      return system_colors_[COLOR_WINDOWTEXT];
+      return system_colors_[SystemThemeColor::kWindowText];
     case kColorId_TextfieldDefaultBackground:
-      return system_colors_[COLOR_WINDOW];
+      return system_colors_[SystemThemeColor::kWindow];
     case kColorId_TextfieldReadOnlyColor:
-      return system_colors_[COLOR_GRAYTEXT];
+      return system_colors_[SystemThemeColor::kGrayText];
     case kColorId_TextfieldReadOnlyBackground:
-      return system_colors_[COLOR_3DFACE];
+      return system_colors_[SystemThemeColor::kButtonFace];
     case kColorId_TextfieldSelectionColor:
-      return system_colors_[COLOR_HIGHLIGHTTEXT];
+      return system_colors_[SystemThemeColor::kHighlightText];
     case kColorId_TextfieldSelectionBackgroundFocused:
-      return system_colors_[COLOR_HIGHLIGHT];
+      return system_colors_[SystemThemeColor::kHighlight];
 
     // Tooltip
     case kColorId_TooltipBackground:
-      return system_colors_[COLOR_WINDOW];
+      return system_colors_[SystemThemeColor::kWindow];
     case kColorId_TooltipText:
-      return system_colors_[COLOR_WINDOWTEXT];
+      return system_colors_[SystemThemeColor::kWindowText];
 
     // Tree
     // NOTE: these aren't right for all themes, but as close as I could get.
     case kColorId_TreeBackground:
-      return system_colors_[COLOR_WINDOW];
+      return system_colors_[SystemThemeColor::kWindow];
     case kColorId_TreeText:
-      return system_colors_[COLOR_WINDOWTEXT];
+      return system_colors_[SystemThemeColor::kWindowText];
     case kColorId_TreeSelectedText:
-      return system_colors_[COLOR_HIGHLIGHTTEXT];
+      return system_colors_[SystemThemeColor::kHighlightText];
     case kColorId_TreeSelectedTextUnfocused:
-      return system_colors_[COLOR_BTNTEXT];
+      return system_colors_[SystemThemeColor::kButtonText];
     case kColorId_TreeSelectionBackgroundFocused:
-      return system_colors_[COLOR_HIGHLIGHT];
+      return system_colors_[SystemThemeColor::kHighlight];
     case kColorId_TreeSelectionBackgroundUnfocused:
-      return system_colors_[UsesHighContrastColors() ? COLOR_MENUHIGHLIGHT
-                                                     : COLOR_BTNFACE];
+      return system_colors_[UsesHighContrastColors()
+                                ? SystemThemeColor::kMenuHighlight
+                                : SystemThemeColor::kButtonFace];
 
     // Table
     case kColorId_TableBackground:
-      return system_colors_[COLOR_WINDOW];
+      return system_colors_[SystemThemeColor::kWindow];
     case kColorId_TableText:
-      return system_colors_[COLOR_WINDOWTEXT];
+      return system_colors_[SystemThemeColor::kWindowText];
     case kColorId_TableSelectedText:
-      return system_colors_[COLOR_HIGHLIGHTTEXT];
+      return system_colors_[SystemThemeColor::kHighlightText];
     case kColorId_TableSelectedTextUnfocused:
-      return system_colors_[COLOR_BTNTEXT];
+      return system_colors_[SystemThemeColor::kButtonText];
     case kColorId_TableSelectionBackgroundFocused:
-      return system_colors_[COLOR_HIGHLIGHT];
+      return system_colors_[SystemThemeColor::kHighlight];
     case kColorId_TableSelectionBackgroundUnfocused:
-      return system_colors_[UsesHighContrastColors() ? COLOR_MENUHIGHLIGHT
-                                                     : COLOR_BTNFACE];
+      return system_colors_[UsesHighContrastColors()
+                                ? SystemThemeColor::kMenuHighlight
+                                : SystemThemeColor::kButtonFace];
     case kColorId_TableGroupingIndicatorColor:
-      return system_colors_[COLOR_GRAYTEXT];
+      return system_colors_[SystemThemeColor::kGrayText];
 
     default:
       break;
@@ -585,8 +614,8 @@
     // as a string. However, this string is language dependent. Instead, to
     // account for non-English systems, sniff out the system colors to
     // determine the high contrast color scheme.
-    SkColor fg_color = system_colors_[COLOR_WINDOWTEXT];
-    SkColor bg_color = system_colors_[COLOR_WINDOW];
+    SkColor fg_color = system_colors_[SystemThemeColor::kWindowText];
+    SkColor bg_color = system_colors_[SystemThemeColor::kWindow];
     if (bg_color == SK_ColorWHITE && fg_color == SK_ColorBLACK) {
       return NativeTheme::PreferredColorScheme::kLight;
     }
@@ -1167,8 +1196,10 @@
   }
 
   // Draw it manually.
-  if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
-      (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
+  if ((system_colors_[SystemThemeColor::kScrollbar] !=
+       system_colors_[SystemThemeColor::kButtonFace]) &&
+      (system_colors_[SystemThemeColor::kScrollbar] !=
+       system_colors_[SystemThemeColor::kWindow])) {
     FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
   } else {
     SkPaint paint;
diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h
index 574559c..50481e31 100644
--- a/ui/native_theme/native_theme_win.h
+++ b/ui/native_theme/native_theme_win.h
@@ -10,9 +10,6 @@
 // NativeThemeWin::instance().
 // For more information on visual style parts and states, see:
 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
-
-#include <map>
-
 #include <windows.h>
 
 #include "base/compiler_specific.h"
@@ -94,8 +91,6 @@
   NativeThemeWin();
   ~NativeThemeWin() override;
 
-  mutable std::map<int, SkColor> system_colors_;
-
  private:
   bool IsUsingHighContrastThemeInternal() const;
   void CloseHandlesInternal();
diff --git a/ui/native_theme/native_theme_win_unittest.cc b/ui/native_theme/native_theme_win_unittest.cc
index a2864e55..9a30b71 100644
--- a/ui/native_theme/native_theme_win_unittest.cc
+++ b/ui/native_theme/native_theme_win_unittest.cc
@@ -8,6 +8,7 @@
 namespace ui {
 
 using Scheme = ui::NativeTheme::PreferredColorScheme;
+using SystemThemeColor = ui::NativeTheme::SystemThemeColor;
 
 class TestNativeThemeWin : public NativeThemeWin {
  public:
@@ -22,7 +23,7 @@
   void SetUsesHighContrastColors(bool high_contrast) {
     high_contrast_ = high_contrast;
   }
-  void SetSystemColor(int system_color, SkColor color) {
+  void SetSystemColor(SystemThemeColor system_color, SkColor color) {
     system_colors_[system_color] = color;
   }
 
@@ -44,15 +45,15 @@
   ASSERT_EQ(theme.CalculatePreferredColorScheme(), Scheme::kLight);
 
   theme.SetUsesHighContrastColors(true);
-  theme.SetSystemColor(COLOR_WINDOW, SK_ColorBLACK);
-  theme.SetSystemColor(COLOR_WINDOWTEXT, SK_ColorWHITE);
+  theme.SetSystemColor(SystemThemeColor::kWindow, SK_ColorBLACK);
+  theme.SetSystemColor(SystemThemeColor::kWindowText, SK_ColorWHITE);
   ASSERT_EQ(theme.CalculatePreferredColorScheme(), Scheme::kDark);
 
-  theme.SetSystemColor(COLOR_WINDOW, SK_ColorWHITE);
-  theme.SetSystemColor(COLOR_WINDOWTEXT, SK_ColorBLACK);
+  theme.SetSystemColor(SystemThemeColor::kWindow, SK_ColorWHITE);
+  theme.SetSystemColor(SystemThemeColor::kWindowText, SK_ColorBLACK);
   ASSERT_EQ(theme.CalculatePreferredColorScheme(), Scheme::kLight);
 
-  theme.SetSystemColor(COLOR_WINDOWTEXT, SK_ColorBLUE);
+  theme.SetSystemColor(SystemThemeColor::kWindowText, SK_ColorBLUE);
   ASSERT_EQ(theme.CalculatePreferredColorScheme(), Scheme::kNoPreference);
 
   theme.SetUsesHighContrastColors(false);
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.cc b/ui/views/accessibility/view_ax_platform_node_delegate.cc
index fed9566..0290635 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -487,8 +487,7 @@
             ViewAXPlatformNodeDelegate* ax_delegate =
                 static_cast<ViewAXPlatformNodeDelegate*>(&view_accessibility);
             if (ax_delegate)
-              is_ignored = is_ignored || (ax_delegate->GetData().role ==
-                                          ax::mojom::Role::kIgnored);
+              is_ignored = is_ignored || ui::IsIgnored(ax_delegate->GetData());
             return is_ignored;
           }),
       views_in_group->end());
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 4a4ee0c..a3f2499 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -461,9 +461,6 @@
 }
 
 void Textfield::SetBackgroundColor(SkColor color) {
-  if (background_color_ == color)
-    return;
-
   background_color_ = color;
   use_default_background_color_ = false;
   UpdateBackgroundColor();
@@ -482,9 +479,6 @@
 }
 
 void Textfield::SetSelectionTextColor(SkColor color) {
-  if (selection_text_color_ == color)
-    return;
-
   selection_text_color_ = color;
   use_default_selection_text_color_ = false;
   UpdateSelectionTextColor();
@@ -507,9 +501,6 @@
 }
 
 void Textfield::SetSelectionBackgroundColor(SkColor color) {
-  if (selection_background_color_ == color)
-    return;
-
   selection_background_color_ = color;
   use_default_selection_background_color_ = false;
   UpdateSelectionBackgroundColor();
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index dccc4e3d..14e97d2 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -22,6 +22,7 @@
 #include "ui/views/widget/native_widget_private.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 #include "ui/web_dialogs/web_dialog_ui.h"
 
@@ -39,11 +40,13 @@
 
 WebDialogView::WebDialogView(content::BrowserContext* context,
                              WebDialogDelegate* delegate,
-                             std::unique_ptr<WebContentsHandler> handler)
+                             std::unique_ptr<WebContentsHandler> handler,
+                             bool use_dialog_frame)
     : ClientView(nullptr, nullptr),
       WebDialogWebContentsDelegate(context, std::move(handler)),
       delegate_(delegate),
-      web_view_(new views::WebView(context)) {
+      web_view_(new views::WebView(context)),
+      use_dialog_frame_(use_dialog_frame) {
   web_view_->set_allow_accelerators(true);
   AddChildView(web_view_);
   set_contents_view(web_view_);
@@ -172,6 +175,12 @@
   return this;
 }
 
+NonClientFrameView* WebDialogView::CreateNonClientFrameView(Widget* widget) {
+  if (use_dialog_frame_)
+    return DialogDelegate::CreateDialogFrameView(widget);
+  return WidgetDelegate::CreateNonClientFrameView(widget);
+}
+
 views::View* WebDialogView::GetInitiallyFocusedView() {
   return web_view_;
 }
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index f77676f..8f32667 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -47,9 +47,12 @@
                                      public views::WidgetDelegate {
  public:
   // |handler| must not be nullptr.
+  // |use_dialog_frame| indicates whether to use dialog frame view for non
+  // client frame view.
   WebDialogView(content::BrowserContext* context,
                 ui::WebDialogDelegate* delegate,
-                std::unique_ptr<WebContentsHandler> handler);
+                std::unique_ptr<WebContentsHandler> handler,
+                bool use_dialog_frame = false);
   ~WebDialogView() override;
 
   // For testing.
@@ -73,6 +76,7 @@
   void WindowClosing() override;
   views::View* GetContentsView() override;
   ClientView* CreateClientView(views::Widget* widget) override;
+  NonClientFrameView* CreateNonClientFrameView(Widget* widget) override;
   views::View* GetInitiallyFocusedView() override;
   bool ShouldShowWindowTitle() const override;
   views::Widget* GetWidget() override;
@@ -167,6 +171,9 @@
   // Handler for unhandled key events from renderer.
   UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
 
+  // Whether to use dialog frame view for non client frame view.
+  bool use_dialog_frame_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(WebDialogView);
 };
 
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index a173b43..df632c9 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -52,6 +52,19 @@
 }
 
 // static
+bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // The new style doesn't support unparented dialogs on Linux desktop.
+  return parent != nullptr;
+#elif defined(OS_WIN)
+  // The new style doesn't support unparented dialogs on Windows Classic themes.
+  if (!ui::win::IsAeroGlassEnabled())
+    return parent != nullptr;
+#endif
+  return true;
+}
+
+// static
 Widget::InitParams DialogDelegate::GetDialogWidgetInitParams(
     WidgetDelegate* delegate,
     gfx::NativeWindow context,
@@ -62,15 +75,8 @@
   params.bounds = bounds;
   DialogDelegate* dialog = delegate->AsDialogDelegate();
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  // The new style doesn't support unparented dialogs on Linux desktop.
   if (dialog)
-    dialog->supports_custom_frame_ &= parent != nullptr;
-#elif defined(OS_WIN)
-  // The new style doesn't support unparented dialogs on Windows Classic themes.
-  if (dialog && !ui::win::IsAeroGlassEnabled())
-    dialog->supports_custom_frame_ &= parent != nullptr;
-#endif
+    dialog->supports_custom_frame_ &= CanSupportCustomFrame(parent);
 
   if (!dialog || dialog->ShouldUseCustomFrame()) {
     params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW;
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 0eb49e1a..1850f53 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -42,6 +42,9 @@
                                     gfx::NativeWindow context,
                                     gfx::NativeView parent);
 
+  // Whether using custom dialog frame is supported for this dialog.
+  static bool CanSupportCustomFrame(gfx::NativeView parent);
+
   // Returns the dialog widget InitParams for a given |context| or |parent|.
   // If |bounds| is not empty, used to initially place the dialog, otherwise
   // a default location is used.